优化 Docker build 缓存失效的核心是调整 Dockerfile 指令顺序并配合 .dockerignore 文件,适用于所有基于 Dockerfile 的构建场景。风险边界在于指令重排可能破坏依赖关系,需确保基础环境先于代码拷贝。
先说结论:通过减少变更频率高的文件进入早期构建层,可最大限度复用缓存。
- 先定位:使用 docker build `--progress`=plain 查看具体哪一层失效
- 先做:将 COPY 源码指令移至依赖安装指令之后
- 再验证:二次构建观察日志中是否出现 CACHED 标记
命令速用版
# 开启 BuildKit 增强缓存
export DOCKER_BUILDKIT=1
# .dockerignore 示例,排除无需上下文文件
echo -e "node_modules\n.git\n*.md" > .dockerignore
# Dockerfile 片段,依赖先于代码
COPY package.json .
RUN npm install
COPY . .
为什么会这样
Docker 构建缓存按指令层级生效,某一层指令或其上下文文件发生变化,该层及后续所有层缓存均失效。官方文档明确说明缓存键由指令本身和上一层镜像 ID 决定,文件内容变化会触发重新构建。
分步处理
步骤 1:配置 .dockerignore
在构建上下文目录创建 .dockerignore 文件,排除构建无需的文件。此操作减少上下文传输时间,避免无关文件变动触发缓存失效。
# .dockerignore 内容示例
.git
.dockerignore
*.log
tmp/
步骤 2:调整 Dockerfile 指令顺序
将变化频率低的依赖安装指令放在变化频率高的源码拷贝指令之前。确保 package.json 或 requirements.txt 先 COPY 并安装,最后再 COPY 源代码。
步骤 3:启用 BuildKit
设置环境变量 DOCKER_BUILDKIT=1 启用新版构建工具。BuildKit 支持更细粒度的缓存导出和并行构建,能更好识别未变更层。
步骤 4:CI/CD 缓存复用
在持续集成环境中使用 `--cache-from` 参数指定远程镜像作为缓存源。此操作适合多分支构建场景,需注意权限配置。
怎么验证是否生效
执行第二次构建命令,观察终端输出中每层指令前是否显示 CACHED。使用 docker history 镜像名 查看层哈希值,若哈希值未变则说明缓存命中。
docker build -t my-app .
docker history my-app
常见坑
1. 过早使用 COPY . .:导致任何文件变动都触发后续层重建。
2. 滥用 ADD 指令:ADD 支持 URL 和解压,行为复杂易导致缓存意外失效,官方建议优先使用 COPY。
3. 文件时间戳干扰:某些构建工具会修改文件时间戳,导致 checksum 变化而失效,需配合 .dockerignore 排除构建产物。
常见问题
为什么修改了代码后所有层都重建了
因为 COPY . . 指令放在了依赖安装之前,代码变动导致该层及后续层缓存失效。应将源码拷贝移至 Dockerfile 末尾。
如何强制不使用缓存构建
添加 `--no-cache` 参数,命令为 docker build `--no-cache` -t 镜像名 .。此操作用于确保获取最新基础镜像或依赖。
多阶段构建能优化缓存吗
能,多阶段构建将编译环境与运行环境分离,减少最终镜像层数。但缓存优化核心仍在于各阶段内的指令顺序。
参考来源
1. Docker 官方文档,Dockerfile best practices,https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
2. Docker 官方文档,BuildKit,https://docs.docker.com/build/buildkit/