Dockerfile 构建基础
Dockerfile 是一个文本文件,其中包含了自动化构建 Docker 镜像的所有命令。
构建过程是在 Docker 守护进程(docker daemon)中进行的,而非命令行工具。
执行 docker build 命令时,命令行工具会将 Dockerfile 和其构建上下文(通常是包含 Dockerfile 的目录)发送给守护进程,以生成镜像。
.dockerignore
.dockerignore 文件用于指定在构建过程中应该忽略的文件和目录,以减小镜像体积并提高构建速度。
其语法与 .gitignore 类似,支持通配符和排除规则:
- .eslintignore:忽略 .eslintignore 文件
- *.md:忽略所有以 .md 结尾的文件(除了 README.md)
- !README.md:不忽略 README.md 文件
- node_modules/:忽略 node_modules 目录下的所有文件
- [a-c].txt:忽略文件名以 a, b, c 开头并且扩展名为 .txt 的文件
React 项目实践
新建项目:
npx craete-react-app dockerfile-test-app
根目录创建 .dockerignore 文件,排除不必要的文件:
# 忽略 node_modules 目录,因为它包含大量的依赖项,可以在构建镜像时通过 npm/yarn 安装
node_modules
# 忽略构建生成的目录,例如 build 或 dist,因为这些文件可以在构建镜像时重新生成
build
dist
# 忽略开发环境相关的配置文件
.env
.env.development
.env.local
.env.*.local
# 忽略编辑器生成的临时文件
.DS_Store
.idea
.vscode
# 忽略日志文件
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# 忽略测试报告和覆盖率文件
coverage
# 忽略其他不需要的文件或目录
.git
.gitignore
.dockerignore
README.md
LICENSE
在根目录创建 Dockerfile 文件,定义镜像构建过程:
# 选择一个带有Node.js的轻量级基础镜像,使用 as 多阶段构建
FROM node:16-alpine as build-stage
# 设置工作目录
WORKDIR /app
# 复制package.json和package-lock.json(如果存在)
COPY package*.json ./
# 安装项目生产依赖
# 利用 Docker 缓存层,如果 package.json 没有变化,则不会重新安装node modules
RUN npm install --only=production
# 复制项目文件到工作目录
COPY . .
# 构建应用
RUN npm run build
# 阶段2:运行
# 使用 Nginx 镜像作为基础来提供前端静态文件服务
FROM nginx:stable-alpine as production-stage
# 定义环境变量,例如NODE_ENV
# ARG NODE_ENV=production
# ENV NODE_ENV=${NODE_ENV}
WORKDIR /app
# 从构建阶段拷贝构建出的文件到Nginx目录
COPY --from=build-stage /app/build /usr/share/nginx/html
# 配置nginx,如果有自定义的nginx配置可以取消注释并修改下面的行
# COPY nginx.conf /etc/nginx/conf.d/default.conf
# 暴露80端口
EXPOSE 80
# 启动Nginx服务器
CMD ["nginx", "-g", "daemon off;"]
启动 docker dekstop,打包镜像:
docker build -t dockerfile-test-app:v1.0 .
运行镜像:
docker run -d -p 8088:80 dockerfile-test-app:v1.0
-d 表示在后台运行容器
-p 8088:80 表示将宿主机的 8088 端口映射到容器的 80 端口。
打开浏览器,访问 http://localhost:8088:
没问题。
Nest 项目实践
新建项目:
nest new dockerfile-test-nest -p npm
根目录创建 .dockerignore:
*.md
node_modules/
.git/
.DS_Store
.vscode/
.dockerignore
根目录创建 Dockerfile:
# Step 1: 使用带有 Node.js 的基础镜像
FROM node:18-alpine as builder
# 设置工作目录
WORKDIR /usr/src/app
# 复制 package.json 和 package-lock.json(如果可用)
COPY package*.json ./
# 安装项目依赖
RUN npm install --only=production
# 安装 nest CLI 工具(确保它作为项目依赖被安装)
RUN npm install @nestjs/cli -g
# 复制所有文件到容器中
COPY . .
# 构建应用程序
RUN npm run build
# Step 2: 运行时使用更精简的基础镜像
FROM node:18-alpine
# 创建 runc 的符号链接
RUN ln -s /sbin/runc /usr/bin/runc
WORKDIR /usr/src/app
# 从 builder 阶段复制构建好的 /usr/src/app/dist 和 /usr/src/app/node_modules 的文件到当工作目录
COPY --from=builder /usr/src/app/dist ./dist
COPY --from=builder /usr/src/app/node_modules ./node_modules
# 暴露 3000 端口
EXPOSE 3000
# 运行 Nest.js 应用程序
CMD ["node", "dist/main"]
打包镜像:
docker build -t dockerfile-test-nest:v1.0 .
运行镜像:
docker run -d -p 3002:3000 dockerfile-test-nest:v1.0
-p 3002:3000: 将宿主机的 3002 端口映射到容器的 3000 端口
访问 http://localhost:3002/:
代表 Nest 应用启动了,没问题。
dockerfile 最佳优化实践
优化 Dockerfile 不仅可以减少镜像的大小,还可以加快构建速度,提高容器的运行效率。
以下是一些 Dockerfile 最佳优化实践:
使用官方、轻量级、精简镜像
选择一个适合应用的官方轻量级基础镜像,例如 alpine 或 debian-slim结尾,可以显著减少最终镜像的大小。
避免使用 latest 标签,而是使用具体的版本号,保持一致性。
最小化镜像层数
- 合并命令行:通过使用逻辑运算符 (&&) 合并命令行,减少镜像层的数量。
- 清理缓存:在同一
RUN指令中安装软件后立即清理缓存和不必要的文件。
# 不推荐
RUN apt-get update
RUN apt-get install -y package1
RUN apt-get install -y package2
# 推荐
RUN apt-get update && \
apt-get install -y package1 package2 && \
rm -rf /var/lib/apt/lists/*
利用缓存
优化指令顺序:将不经常变化的指令放在 Dockerfile 的前面,这样可以利用 Docker 的层缓存,加速后续构建过程。
# 先复制不经常变动的文件
COPY requirements.txt /app/
# 再复制经常变动的文件
COPY . /app
清理不必要的文件
在同一个 RUN 指令中安装软件包后,应该清理缓存和不必要的文件,以减少镜像大小。
RUN apt-get update && \
apt-get install -y package && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
使用多阶段构建
通过在一个 Dockerfile 中使用多个 FROM 指令,可以在一个阶段构建应用,然后在另一个轻量级的基础镜像中运行该应用,从而减少最终镜像的体积。
多阶段构建可以将构建过程分为多个阶段,每个阶段使用不同的基础镜像,最终只将必要的文件复制到最终镜像中。
使用 .dockerignore 文件
通过在 .dockerignore 文件中列出不需要复制到镜像中的文件和目录,可以减少构建上下文的大小,加快构建速度,并减小镜像体积。
避免安全漏洞
- 定期更新基础镜像:定期更新基础镜像以获取安全补丁。
- 使用非 root 用户:通过 USER 指令切换到非 root 用户,以减少安全风险。
RUN adduser -D myuser
USER myuser
善用环境变量
新建目录,创建 test.js:
console.log(process.env.name);
console.log(process.env.age);
创建 Dockerfile:
FROM node:18-alpine3.14
WORKDIR /app
COPY ./test.js .
ARG name=Yun
ARG age=20
ENV name=${name} \
age=${age}
CMD ["node", "/app/test.js"]
打包镜像:
docker build -t env-test:v1.0 .
这里的 -t 参数用于给镜像命名. 表示 Dockerfile 所在的当前目录。
运行镜像:
docker run -it --rm env-test:v1.0
-it 参数表示运行一个带有交互式 shell 的 Docker 容器。--rm 参数表示在容器退出时自动删除容器。
或者我们可以手动指定:
如果有很多环境变量,可以存储在文件中,然后在使用 docker run 命令时通过 --env-file 选项指定该文件。