Docker 构建缓存不命中:COPY 顺序、.dockerignore 和上下文路径三个原因
同一个 Dockerfile,昨天 build 只要十几秒,今天改了一行 README 却把依赖全部重装了一遍。多数时候不是 Docker 抽风,而是构建缓存的键被悄悄改了。下面按最常见的顺序,讲三个原因和对应的验证办法。
一、COPY 和 RUN 的顺序把缓存打断了
Docker 的层缓存从上往下匹配:只要某一层的输入变了,它和它之后的所有层都会重建。常见写法是先 COPY . . 再 RUN npm install,这样一来,任意一个源文件改动都会让 COPY 层失效,后面的依赖安装自然跟着重跑。正确做法是把依赖清单单独 COPY,先装依赖再拷代码:
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
把变动最频繁的内容放到最后,缓存命中率会明显提高。Python 项目同理,先 COPY requirements.txt 再 pip install。
二、缺少 .dockerignore,构建上下文把无关文件带了进来
docker build 会先把整个上下文打包发给守护进程。如果没写 .dockerignore,node_modules、.git、dist、日志都会被算进上下文。它们不直接进镜像,但会让 COPY 层的散列随无关文件变化,也会让每次构建多传几百 MB。最小可用的一份是:
.git
node_modules
dist
*.log
.env
注意 .dockerignore 的匹配是相对上下文根目录的,写 ./node_modules 和 node_modules 的行为并不相同,建议统一用不带前缀的写法。
三、构建上下文路径变了,缓存直接归零
docker build -f docker/Dockerfile . 和 docker build -f docker/Dockerfile docker/ 看起来只差一个路径,实际上下文根不同,COPY 的相对路径和层散列都会变,缓存自然对不上。用 BuildKit 时还可以在 compose 文件里显式指定 context,避免 CI 和本地各写一套。
四、怎么确认是缓存问题
先看每层命中情况:docker build --progress=plain . 会打印 CACHED 标记。再对照 docker history 看哪一层的时间和体积异常。想验证是不是缓存导致,用 --no-cache 跑一次作为基线,两次耗时接近说明缓存本来就没生效,问题在别处。
小结:缓存不是靠运气,而是靠输入稳定。把依赖装在最前、代码拷在最后,用 .dockerignore 把无关文件挡在上下文外,固定构建上下文路径,这三步能解决大多数每次都全量重装的问题。
- 点赞
- 收藏
- 关注作者
评论(0)