最直接有效的缓存优化方法是将变动少的指令前置、变动多的后置;docker逐层构建,前层变更会导致后续所有层重建,因此指令排序比内容正确性更关键。

把变动少的指令放在前面,变动多的放在后面,是提升缓存命中率最直接有效的方法。Docker 构建时逐层执行,只要某一层内容变了,它之后所有层都会重新构建——所以关键不是“写对”,而是“排对”。
基础镜像和系统依赖优先
FROM 和系统级安装(如 apt-get、apk add)几乎不随业务代码变化,应放在最开头:
- 用固定标签代替 latest,比如 FROM ubuntu:22.04 而非 FROM ubuntu:latest,避免基础镜像更新导致整层失效
- 合并安装与清理操作:RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*,防止中间缓存残留无用数据
- 避免在 RUN 中调用带时间戳或网络请求的命令(如 date、curl https://api/time),它们会让哈希值每次不同
应用依赖与源码分离复制
依赖文件(requirements.txt、package.json)比源码变更频率低得多,要单独 COPY 并提前安装:
- 先 COPY requirements.txt .,再 RUN pip install -r requirements.txt;这样只有依赖清单改了才重装,改一行代码不会触发重装
- 同理,前端项目先 COPY package*.json .,再 RUN npm install,最后才 COPY . .
- 配合 .dockerignore 排除 node_modules、__pycache__、.git 等,防止这些目录意外被 COPY 进来,破坏缓存一致性
多阶段构建中分阶段缓存
构建阶段和运行阶段各自独立缓存,能大幅隔离变更影响:
- 构建阶段只 COPY go.mod 或 Cargo.toml,RUN go mod download 或 cargo build --release,这部分缓存只要依赖没变就一直复用
- 运行阶段用 COPY --from=builder 提取二进制文件,完全跳过编译过程,也不引入构建工具链
- 实测显示:某 Go 服务将依赖下载与编译拆开后,CI 构建时间从 3 分 18 秒降至 27 秒,缓存命中率稳定在 94% 以上
精简层数量并控制 COPY 粒度
每条 RUN、COPY 都是一层,层数越多、单层越不稳定,缓存越容易断链:
- 不要为每个命令单独写 RUN,把关联操作串起来:RUN pip install && python manage.py collectstatic --noinput && rm -rf /app/staticfiles/.git
- COPY 尽量按逻辑分批:先 COPY config/,再 COPY src/,而不是一股脑 COPY . .;避免改一个 README.md 就让整个依赖安装层失效
- 启用 BuildKit(docker build --progress=plain)可更清晰看到哪一层用了缓存,方便定位瓶颈











