Docker 镜像为什么越建越慢?一文讲透分层缓存与 5 个优化实践

举报
yd_255634470 发表于 2026/11/09 14:06:35 2026/11/09
【摘要】 从 Docker 镜像的分层结构讲起,解释构建缓存为什么会失效,并给出 5 个可以直接落地的 Dockerfile 优化实践和多阶段构建模板。

一、从一个常见现象说起

很多同学刚开始用 Docker 时,会发现一个奇怪的现象:第一次 docker build 只要两分钟,改了一行代码之后重新构建,却要等上十几分钟。明明只改了一个文件,为什么整个镜像都要重来?要回答这个问题,得先理解 Docker 镜像的分层结构。

二、镜像不是"一个文件",而是一摞只读层

Docker 镜像由一层层的只读文件系统组成,每一层都记录着相对上一层的增量变化。docker history 可以把这些层列出来:

docker history myapp:latest

当我们执行 FROM ubuntu:22.04、RUN apt-get install、COPY . /app 时,Docker 会为每条指令生成一层。层与层之间通过内容寻址的方式关联,相同内容的层可以被多个镜像共享,这也是为什么拉取第二个基于同一基础镜像的应用时,速度会快很多。

容器启动时,Docker 在所有只读层之上再加一个可写层。所有写操作都发生在可写层,删除容器时这一层也随之消失——这就是"容器无状态"的底层原因。

三、缓存的命中规则:命中即跳过,未命中则全部重来

构建时,Docker 会按 Dockerfile 的顺序逐条检查指令。如果某条指令与上一次构建完全一致,就直接复用缓存层;一旦某条指令发生变化,它以及它后面的所有指令都会重新执行。

关键在于"完全一致"这四个字:COPY 和 ADD 会计算文件内容的校验和,文件内容、权限、时间戳的细微变化都会导致缓存失效。所以下面这种写法几乎必然踩坑:

COPY . /app
RUN npm install

只要改了一行业务代码,COPY . /app 的校验和就变了,后面的 npm install 自然全部重跑。

四、五个立竿见影的优化

  1. 先拷贝依赖清单,再安装依赖。把 package.json 和锁文件单独 COPY 进去,安装完依赖后再 COPY 源码。这样改业务代码不会影响依赖层。
  1. 合并 RUN 指令并及时清理。apt-get update 和 apt-get install 放在同一条 RUN 里,安装完立刻 rm -rf /var/lib/apt/lists/*,避免缓存文件留在层里。
  1. 使用多阶段构建。编译工具链只出现在 builder 阶段,最终镜像只保留运行产物,体积往往能缩小一个数量级。
  1. 合理使用 .dockerignore。把 node_modules、.git、日志、测试数据排除在构建上下文之外,既加快上下文传输,也避免无谓的缓存失效。
  1. 固定基础镜像版本。用 ubuntu:22.04 而不是 ubuntu:latest,避免基础镜像悄悄更新导致整条缓存链断裂。

五、一个可以直接套用的模板

FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:1.27-alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80

这个模板把"变化快的源码"放在"变化慢的依赖"之后,配合多阶段构建,既快又小。

六、写在最后

理解分层和缓存,不是为了背几条口诀,而是为了在写 Dockerfile 时能预判"这一行会不会让缓存失效"。把变化频率不同的内容分层放置,是 Docker 构建优化的第一性原理。下次构建变慢时,不妨先跑一遍 docker history,看看究竟是哪一层在反复重建。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。