核心是让不常变的操作尽量靠前且输入稳定;缓存中断会导致后续所有层重建,关键在于防止缓存断掉:依赖文件(如package.json、go.mod、requirements.txt)应单独COPY并前置,紧接安装命令以长期命中缓存;精确控制COPY范围、用.dockerignore排除无关文件、多阶段构建分层复用、固定基础镜像版本及digest、合并易变RUN命令。

核心是让不常变的操作尽量靠前,且确保它们的输入稳定。缓存一旦中断,后续所有层都会重新构建,所以关键不在“怎么用缓存”,而在于“怎么不让缓存断掉”。
把依赖文件单独 COPY 并前置
源码经常改,但 package.json、go.mod、requirements.txt 这类依赖描述文件变动频率低。把它们单独复制,并紧跟着执行安装命令,就能让依赖安装这一步长期命中缓存。
- Node.js 项目:先
COPY package.json package-lock.json .,再RUN npm install - Go 项目:先
COPY go.mod go.sum .,再RUN go mod download - Python 项目:先
COPY requirements.txt .,再RUN pip install -r requirements.txt
精确控制 COPY 范围,避免无关文件触发失效
COPY 指令会计算整个路径下所有文件的内容哈希。如果把 . 全量复制,哪怕只是改了个 README 或加了 .log 文件,缓存也会失效。
- 只复制真正需要的文件,比如
COPY src/ ./src/、COPY app.py . - 用
.dockerignore排除开发文件(node_modules、__pycache__、.git、*.md、*.log 等) - 避免在 COPY 前运行生成文件的脚本(如 build.sh),否则每次构建都算“内容变更”
多阶段构建中分层复用缓存
构建阶段和运行阶段各自独立缓存。把耗时的依赖下载、编译等操作放在 builder 阶段开头,只要依赖不变,这部分永远走缓存。
- builder 阶段里,先 COPY 依赖文件 → 安装依赖 → 再 COPY 源码 → 编译
- runner 阶段只 COPY 编译产物,不带任何构建工具或源码
- 命名阶段(
AS builder)方便引用,也利于 CI 中复用中间镜像
固定基础镜像和构建环境
基础镜像更新会导致第一层缓存失效,进而影响全部后续层。使用明确版本号甚至 digest 可规避意外更新。
- 用
FROM node:20.15.1替代FROM node:20或FROM node:latest - 更稳妥写法:
FROM node:20.15.1@sha256:abc... - 避免在 RUN 中调用可能随时间变化的命令,比如
apt-get update && apt-get install应合并为一行,或用固定版本包名


















