docker镜像构建缓存机制核心在于理解层来源、缓存断点与优化时机:from需用固定digest自建基础镜像;copy package.json + run npm ci组合实现依赖层精准复用;.dockerignore配合copy . . 放最后可避免无关文件触发全量重建;buildkit的--mount=type=cache支持跨构建复用包缓存;多阶段构建通过隔离变更域缩小缓存失效范围。

Docker 镜像构建缓存机制不是靠死记命令,而是理解“层怎么来、缓存怎么断、哪里能省时间”。学它得从构建过程本身出发,边做边验证,而不是先背理论。
搞懂 FROM 是缓存的起点
FROM 指令不只是选个基础镜像,它决定了整个构建链的“信任锚点”。如果每次构建都拉 node:20-slim 这种官方镜像,哪怕内容没变,只要 registry 返回的镜像 digest 不同(比如官方悄悄更新了小版本),本地缓存就失效。真正稳定的起点是团队自建的基础镜像,比如 myorg/base-node:20.12,由 CI 固定版本、自动构建并推送。这样所有项目共享同一层起点,后续缓存才可复用。
把 COPY 和 RUN 当成缓存开关
- COPY package.json → RUN npm ci 这一组是黄金组合:只要 package.json 不变,这一步永远走缓存;一旦改了依赖,缓存从这里断,后面全重跑。
- COPY . . 放在最后:避免把 src/、logs/、.git 这类易变文件一起复制,否则一改 README.md,整个构建就从头来。用
.dockerignore删掉无关文件,比写复杂脚本更直接有效。
用 BuildKit 让缓存“活”起来
默认 Docker 构建只用本地层缓存,BuildKit 加上 --mount=type=cache 才能让 node_modules、~/.pnpm、Cargo registry 这类目录跨构建复用。例如:
RUN --mount=type=cache,id=pnpm-store,target=/pnpm/store \
pnpm install --frozen-lockfile
这个 cache 不进最终镜像,但下次构建时,只要 id 相同,就能复用上次下载的包——不用等 3 分钟拉完 200 个依赖。
多阶段构建不是为了“炫技”,是为了隔离变更域
Go 或 Rust 项目里,把 go build 放第一阶段,COPY --from=builder 放第二阶段,好处很实在:业务代码天天改,但 go.mod 一周才动一次。改 main.go?只重建最后一层;改 go.sum?才触发编译层重建。缓存失效范围被精准控制,而不是“一动全崩”。
不复杂但容易忽略











