docker构建缓存优化的核心是将稳定层前置、易变层后置:先copy依赖文件并安装,再copy源码;配合.dockerignore排除干扰文件、多阶段构建隔离变动、固定基础镜像版本,可大幅提升缓存命中率。

核心是让稳定层尽量靠前、易变层尽量靠后。Docker 构建按顺序逐层生成,一旦某层内容变化(比如代码文件更新),它之后所有层都会失效重做。所以把经常改的部分往后放,就能保住前面安装依赖、配置环境等耗时操作的缓存。
把源码复制放到最后
COPY 整个项目目录(如 COPY . /app)是最容易触发缓存失效的操作——改一行代码,哈希就变,这一层及之后全重建。正确做法是分步 COPY:
- 先 COPY 依赖声明文件:package.json + package-lock.json(Node)、go.mod + go.sum(Go)、requirements.txt(Python)
- 紧接着 RUN 安装命令(npm install / go mod download / pip install)
- 最后 COPY 其余源码(如 src/、cmd/、.env 等)
这样只要不改依赖清单,安装步骤永远命中缓存,哪怕每天提交十次代码也不影响。
避免在早期层混入构建上下文噪声
构建上下文传进 Docker 的所有文件都会参与 COPY 层哈希计算。如果 .git、node_modules、logs、.DS_Store 这类文件没被排除,它们一变,哪怕只是 git commit 增加了一个空行,也会导致 COPY . 缓存失效。
- 务必配好 .dockerignore,至少包含:.git, node_modules, logs/, *.log, .DS_Store, .env.local
- 不要用 COPY . . 代替精准路径,比如前端项目可只 COPY public/ 和 src/,跳过测试和配置目录
用多阶段构建隔离变动边界
单阶段 Dockerfile 中,所有层串在一起,一个地方动,后面全崩。多阶段能物理隔开“构建”和“运行”两套逻辑:
- builder 阶段:用完整工具链镜像(node:18、golang:1.21),执行编译、打包、测试
- runtime 阶段:用极简镜像(alpine、distroless),只 COPY builder 阶段产出的二进制或 dist/ 目录
- 两个阶段各自缓存,互不干扰 —— 源码变了只影响 builder;基础镜像升级只影响 runtime
固定基础镜像标签,守住第一道缓存
FROM 是整个镜像的第一层,它一变,全盘重来。用 node:latest 或 ubuntu:jammy 这类浮动标签,CI 跑着跑着可能拉到新版镜像,连带触发所有层重建。
- 统一用精确语义化版本:node:18.17.0-alpine3.18、python:3.11.9-slim-bookworm
- 若自建基础镜像,也必须打固定 tag(如 mybase:v1.2.3),且它的构建过程本身也要启用 BuildKit 远程缓存











