关键在于让docker缓存“稳住、传得远、用得准”:锚定稳定层(先copy依赖清单再安装,源码最后copy)、用.dockerignore过滤污染源、跨构建复用缓存(如github actions用docker/setup-buildx-action)、固定基础镜像标签、启用buildkit并合理使用多阶段构建。
关键不是让缓存“存在”,而是让它在 ci 流水线里“稳住、传得远、用得准”。持续集成环境天然具备构建高频、上下文隔离、资源可配置等特点,只要把 docker 缓存机制和 ci 的运行逻辑对齐,就能把构建时间从分钟级压到秒级。
把缓存“锚定”在稳定层上
Docker 缓存按指令顺序逐层生效,一旦某层失效,后面全重来。CI 中最常踩的坑,就是把源码 COPY 放太靠前,导致每次提交都触发全量重建。
- 先 COPY 依赖清单(package.json / requirements.txt / go.mod),再 RUN 安装 —— 这些文件变更频率低,缓存复用率高
- 源码最后 COPY,哪怕改一行,也只影响最后几层,前面的依赖安装、工具准备全都能跳过
- 避免直接 COPY . .,尤其不要让 .git、node_modules、dist 等目录混入构建上下文
用 .dockerignore 切断缓存污染源
CI 构建时上传的上下文越大,传输越慢,哈希计算越容易因无关文件变动而失效。.dockerignore 不是可选项,是缓存稳定的守门员。
- 必须包含:**/node_modules、**/__pycache__、.git、.gitignore、coverage/、dist/、build/、*.log、*.tmp
- CI 中还要额外注意:Jest 生成的 jest-junit.xml、Maven 的 target/、Python 的 .pytest_cache/ —— 这些常被忽略,却会悄悄让 COPY 层哈希变化
- 建议在 CI 脚本里加一步校验:test -f .dockerignore || (echo "Missing .dockerignore!" && exit 1)
跨构建复用:让缓存“活”过单次流水线
本地缓存只在单次构建有效,CI runner 通常是临时容器,必须把缓存“存出去、拉回来”。
- GitHub Actions 推荐用 docker/setup-buildx-action + --cache-from type=registry,ref=xxx/cache
- GitLab CI 可挂载 DOCKER_BUILDKIT=1 并配合 --cache-to type=inline,再通过 cache:paths 持久化 buildkit 目录
- 基础镜像标签要固定,比如用 node:18.19 而非 node:18 或 node:latest,防止 FROM 变更导致整条缓存链崩塌
多阶段 + BuildKit:精准复制 + 智能缓存
多阶段构建本身不提速,但配合 BuildKit 就能释放缓存潜力 —— 它支持并发 COPY、条件复制、挂载缓存目录等能力。
- 构建阶段专注下载依赖和编译,运行阶段只 COPY --from=builder 的产物,彻底隔绝源码变动对运行镜像的影响
- 启用 BuildKit(DOCKER_BUILDKIT=1),并在 Dockerfile 开头加 # syntax=docker/dockerfile:1
- 用 COPY --if-exists 避免因配置文件缺失中断构建;用 --chown 减少后续 chmod 指令,省去一层 RUN











