Docker 构建上下文与 .dockerignore
docker build 的最后一个参数(通常是点号)决定构建上下文,很多镜像臃肿、构建缓慢的问题都出在上下文理解上。本章讲清上下文的本质,并给出 .dockerignore 的正确写法。
什么是构建上下文
构建时,Docker 会把「上下文目录」整体打包发给守护进程 dockerd,Dockerfile 里的 COPY/ADD 只能引用上下文内的文件;dockerfile 文件名可用 -f 指定到别处,但上下文范围不会因此扩大:
docker build -t myapp . # 点号目录就是构建上下文
docker build -f prod.Dockerfile -t myapp ./web # 上下文仍是 ./web
docker build -t myapp:1.0 -t myapp:latest . # -t 可打多个标签
上下文越大,打包传输越慢;跨网络构建(远程守护进程)时差别更明显。若 COPY 引用了上下文之外的文件,会直接报错。
.dockerignore 语法
.dockerignore 与 .gitignore 语法一致,放在上下文根目录,逐行写排除规则,支持 * 通配与 ! 反向排除:
node_modules # 依赖目录:体积大、可重建,必排除
.git # 版本库历史:又大又含敏感信息
dist
logs
*.log # 通配符:所有 .log 文件
Dockerfile # 镜像里不需要构建文件本身
被忽略的文件既不会发送给守护进程,也不会进入镜像。注意:若整个目录(如 logs/)被忽略,就无法再用 !logs/keep.log 单独放行其中的文件。
上下文过大与缓存失效
构建缓慢的两大成因是上下文过大和缓存频繁失效。发送体积由 .dockerignore 控制;缓存失效则由「改变 COPY/ADD 进镜像的文件」触发——该层及其后的所有层都会重建:
docker build -t myapp . --progress=plain # 看每步是否 CACHED
du -sh . --exclude=node_modules # 检查上下文实际大小
把不参与构建的目录写进 .dockerignore,并把依赖安装放前面、业务代码放后面(第 13 章已述),可同时缓解这两个问题。
-f 与 -t 的配合
项目里常有多套构建文件(开发/生产),用 -f 选择文件、-t 命名产物:
docker build -f docker/dev.Dockerfile -t myapp:dev .
docker build -f docker/prod.Dockerfile -t registry.example.com/myapp:1.0 .
# docker/prod.Dockerfile
FROM alpine:3.20
COPY ./app /app # 上下文仍是项目根目录,路径按上下文算
CMD ["/app/run.sh"]
BuildKit 一句话
Docker 24 以上默认启用 BuildKit:构建更快、支持并发、能跳过无用阶段,还能用 ADD https://... 拉远程文件;用 docker buildx 可进一步做多平台构建(--platform linux/amd64,linux/arm64)。
小结
把「上下文 = 发送给守护进程的目录」记牢:用 -f 选 Dockerfile、-t 命名、.dockerignore 把 node_modules/.git 等排除掉,构建既快又干净,这也是镜像瘦身的第一步。