4. 使用 .dockerignore 减少构建上下文
为什么构建上下文会拖慢构建
执行 docker build 时,Docker 客户端会先把构建上下文(默认是 Dockerfile 所在目录)整体打包发送给守护进程,然后才按 Dockerfile 逐层构建。如果目录里存在 node_modules、.git、日志文件等大体积内容,它们会被一并传输,即使 Dockerfile 里根本没有 COPY 它们。结果是:传输变慢、构建缓存更容易失效、镜像层可能被无关文件污染。
.dockerignore 的作用就是在打包阶段排除这些文件,语法与 .gitignore 类似,但匹配规则由 Docker 自己解释。
编写 .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 | 仅源码与配置文件 |
上下文从百兆级降到兆级,传输时间明显缩短,构建缓存也更稳定。
验证构建结果不受影响
排除文件后,必须确认镜像仍能正常构建和运行。关键检查点:
- Dockerfile 中是否 COPY 了被排除的路径。如果 Dockerfile 写了
COPY node_modules ./node_modules,排除后该指令会失败。正确做法是在容器内执行npm install或npm ci。 - 构建是否成功。运行
docker build,确认没有COPY failed: no source files之类的错误。 - 容器是否正常启动。运行
docker run --rm demo-app:with-ignore,检查应用能否启动并响应请求。 - 镜像内容是否完整。用
docker run --rm demo-app:with-ignore ls /app查看工作目录,确认源码和依赖都在。
如果构建时提示某个文件找不到,先检查它是否被
.dockerignore 误排除。可以用 docker build --no-cache 排除缓存干扰,再定位问题。常见误区
- 把 .dockerignore 当成 .gitignore 用。两者语法相似但不完全等价,Docker 的匹配规则以官方文档为准。
- 排除了 Dockerfile 本身。Dockerfile 必须保留在上下文中,否则构建无法进行。
- 以为排除后镜像里就没有这些文件。排除只影响发送给守护进程的内容;如果 Dockerfile 通过其他方式(如网络下载)引入文件,仍会进入镜像。
- 忘记排除 .env 等敏感文件。这些文件不应进入构建上下文,更不应被打进镜像。