高效缓存共享的关键在于严格分离依赖安装与代码复制,优先copy锁定文件(如go.mod、package-lock.json)再run安装依赖,随后才copy源码;并用--mount=type=cache显式挂载/root/.npm等工具缓存目录实现跨构建复用,配合命名阶段(as builder)和--target控制构建边界,结合buildx registry缓存实现团队级共享。
高效缓存共享的关键在于让 docker 复用尽可能多的中间层,尤其在多阶段构建中,要让各阶段“稳住不变的部分”,只在真正变化时才重建。核心不是堆砌技巧,而是控制变更边界、显式管理缓存生命周期、并打通构建环境之间的数据复用。
把依赖安装和代码复制严格分离
这是最基础也最关键的一步。Docker 缓存按层命中,一旦某层失效,其后所有层都会重建。因此必须确保依赖下载这一步不被源码变更“带崩”。
- 先 COPY go.mod 和 go.sum(或 package-lock.json、Pipfile.lock 等锁定文件),再 RUN 安装依赖
- 之后才 COPY . . —— 这样改一行业务代码,不会触发重新下载整个 node_modules 或 go module
- 避免 COPY 整个 src/ 目录前就 RUN npm install;更不能把 COPY 和 RUN 合并在一条指令里
用 --mount=type=cache 显式挂载可复用目录
传统层缓存对 /root/.npm、/usr/local/share/.m2、~/.cargo/registry 这类工具缓存目录无效,因为它们不在镜像层中。--mount=type=cache 能跨构建实例持久化这些内容。
- Node.js 示例:RUN --mount=type=cache,id=npm-cache,target=/root/.npm npm install
- Maven 示例:RUN --mount=type=cache,id=maven-cache,target=/root/.m2 mvn clean package
- 关键参数:id 是缓存唯一标识(建议项目级固定),target 是容器内路径,readonly 可选但推荐用于构建阶段
命名构建阶段 + 指定 target 构建调试阶段
多阶段默认构建到最后一个阶段,但开发时你只想快速验证 builder 阶段是否能编译成功,没必要走完整流程。
- 给 builder 阶段起名:FROM golang:1.21 AS builder
- 开发时只构建它:docker build --target builder -t myapp-builder .
- CI 发布时才构建最终镜像:docker build -t myapp:latest .
- 这样既能隔离调试与发布流程,也让 builder 阶段缓存更稳定(不会因 runner 阶段变动而失效)
用 Buildx 配置远程缓存实现团队/流水线共享
单机缓存只在本地有效。CI 流水线每次拉新环境,缓存就归零。Buildx 的 registry 缓存能解决这个问题。
- 启用 BuildKit:export DOCKER_BUILDKIT=1
- 构建时导出缓存到镜像仓库:--cache-to type=registry,ref=myreg.com/myapp:buildcache
- 同时拉取已有缓存:--cache-from type=registry,ref=myreg.com/myapp:buildcache
- 配合 --push 使用,一次命令完成构建、推镜像、存缓存三件事











