缓存命中需指令及上下文完全一致,关键四点:稳定内容前置、避免过早全量COPY、固定基础镜像tag、用.dockerignore排除干扰;多阶段构建分离编译与运行环境,提升缓存复用率并减小体积;清理应区分场景,优先用docker builder prune等精准命令。
合理利用缓存能大幅缩短构建时间,而定期清理又能防止磁盘被无效层占满。关键在于理解“什么会命中”和“什么该删掉”。
让缓存真正起作用的 4 个实操要点
缓存不是自动生效的,得按规则组织 Dockerfile:
-
把稳定内容放前面:比如先 COPY
go.mod和go.sum,再 RUNgo mod download;这样依赖没变时,这一步就直接复用,不用重下包 -
避免过早 COPY 整个代码目录:写成
COPY . .意味着只要改一行代码,后续所有层(包括依赖安装)都会失效;应拆开,只复制必要文件 -
固定基础镜像 tag:用
FROM node:18.19.0而不是FROM node:18或FROM node:latest,防止基础层意外更新导致全量重建 -
用
.dockerignore排除干扰项:把node_modules/、dist/、.git、日志等加进去,避免它们进构建上下文,触发无谓的缓存失效
多阶段构建是缓存+体积双优化利器
它把构建和运行分离,既提升缓存复用率,又产出极简镜像:
- 第一阶段(builder)专注编译:装 Go、复制源码、执行
go build,这一阶段的缓存可长期复用,哪怕你每天改代码,只要依赖不变,下载和编译步骤都不用重跑 - 第二阶段(runtime)只拿产物:用
alpine或distroless镜像,仅COPY --from=builder二进制文件,不带任何编译工具或源码 - 两个阶段各自独立缓存,互不影响;最终镜像体积通常能压到原来的 1/5 甚至更小
哪些缓存该清?用对命令才不伤系统
清理不是越狠越好,要区分对象和影响范围:
-
docker image prune:只删“悬空镜像”(dangling),即没标签、也没被任何容器引用的中间层,安全,可日常执行 -
docker builder prune:清理 BuildKit 构建缓存(含未被引用的构建阶段输出),CI 流水线里建议每次构建后加这步 -
docker system prune -f:一键清空所有未使用资源(镜像、容器、网络、构建缓存),适合本地开发环境定期维护,但会删掉所有已停止的容器记录 - 查空间占用用
docker system df,重点关注 “Build Cache” 行的RECLAIMABLE值,决定是否值得清理
CI/CD 中让缓存持续有效的技巧
流水线环境容易因并行构建或分支切换导致缓存失灵,可主动干预:
- 启用 BuildKit:
DOCKER_BUILDKIT=1 docker build,支持更细粒度缓存和并发构建 - 跨构建复用缓存:
docker build --cache-from=registry.example.com/myapp:latest,从远程镜像拉取缓存层(需提前推送) - 在 GitHub Actions 等平台中,用
actions/cache缓存~/.npm或~/.m2目录,绕过容器内重复下载 - 为不同分支设置独立缓存键(如基于 git commit hash 或 branch name),避免 feature 分支污染 main 分支的缓存


















