4. 使用 .dockerignore 减少构建上下文

使用 .dockerignore 减少构建上下文 学习卡片

为什么构建上下文会拖慢构建

执行 docker build 时,Docker 客户端会先把构建上下文(默认是 Dockerfile 所在目录)整体打包发送给守护进程,然后才按 Dockerfile 逐层构建。如果目录里存在 node_modules、.git、日志文件等大体积内容,它们会被一并传输,即使 Dockerfile 里根本没有 COPY 它们。结果是:传输变慢、构建缓存更容易失效、镜像层可能被无关文件污染。

.dockerignore 的作用就是在打包阶段排除这些文件,语法与 .gitignore 类似,但匹配规则由 Docker 自己解释。

构建上下文打包与 .dockerignore 过滤 项目目录(构建上下文) Dockerfile 保留 package.json 保留 src/ 保留 node_modules/ 排除 .git/ 排除 *.log 排除 npm-debug.log 排除 .dockerignore 过滤规则 打包前生效 发送给 Docker 守护进程 Dockerfile package.json src/ 仅保留必要文件 被排除,不参与传输 node_modules/ .git/ *.log 上下文体积显著减小

编写 .dockerignore

在项目根目录(与 Dockerfile 同级)创建 .dockerignore,逐行写入要排除的模式。以下是一个针对 Node.js 项目的示例:

# 依赖目录,容器内会重新安装
node_modules

# 版本控制与 CI 配置
.git
.gitignore
.github

# 日志与调试文件
*.log
npm-debug.log*
logs/

# 本地环境与编辑器
.env.local
.vscode
.idea

# 构建产物与缓存
dist
.cache
coverage

几点需要注意:

  • 模式匹配的是相对于构建上下文根目录的路径,不是宿主机绝对路径。
  • node_modules 会匹配任意层级的同名目录;若只想排除根目录下的,可写 /node_modules。
  • 以 ! 开头的行表示例外(重新包含),例如 !README.md。
  • 注释以 # 开头,空行会被忽略。

观察构建上下文大小变化

Docker 在构建开始时输出的第一行 Sending build context to Docker daemon 会显示上下文体积。用同一项目分别在不加和加上 .dockerignore 的情况下构建,对比该数值即可。

# 1. 先临时移走 .dockerignore,构建一次
mv .dockerignore .dockerignore.bak
docker build -t demo-app:no-ignore .

# 2. 恢复 .dockerignore,再构建一次
mv .dockerignore.bak .dockerignore
docker build -t demo-app:with-ignore .

典型输出对比(示例数据,实际数值取决于项目):

场景构建上下文大小说明
无 .dockerignore约 180 MB包含 node_modules、.git、日志
有 .dockerignore约 2 MB仅源码与配置文件

上下文从百兆级降到兆级,传输时间明显缩短,构建缓存也更稳定。

验证构建结果不受影响

排除文件后,必须确认镜像仍能正常构建和运行。关键检查点:

  1. Dockerfile 中是否 COPY 了被排除的路径。如果 Dockerfile 写了 COPY node_modules ./node_modules,排除后该指令会失败。正确做法是在容器内执行 npm install 或 npm ci。
  2. 构建是否成功。运行 docker build,确认没有 COPY failed: no source files 之类的错误。
  3. 容器是否正常启动。运行 docker run --rm demo-app:with-ignore,检查应用能否启动并响应请求。
  4. 镜像内容是否完整。用 docker run --rm demo-app:with-ignore ls /app 查看工作目录,确认源码和依赖都在。
如果构建时提示某个文件找不到,先检查它是否被 .dockerignore 误排除。可以用 docker build --no-cache 排除缓存干扰,再定位问题。

常见误区

  • 把 .dockerignore 当成 .gitignore 用。两者语法相似但不完全等价,Docker 的匹配规则以官方文档为准。
  • 排除了 Dockerfile 本身。Dockerfile 必须保留在上下文中,否则构建无法进行。
  • 以为排除后镜像里就没有这些文件。排除只影响发送给守护进程的内容;如果 Dockerfile 通过其他方式(如网络下载)引入文件,仍会进入镜像。
  • 忘记排除 .env 等敏感文件。这些文件不应进入构建上下文,更不应被打进镜像。