多阶段构建通过分阶段隔离与按需复制,使中间层根本不进入最终镜像,从而实现本质性瘦身;它不依赖删除,而是从源头避免冗余内容被带入。
多阶段构建本身不是清理工具,而是通过分阶段隔离、按需复制,让中间层根本不会进入最终镜像——这才是真正意义上的“清理”。关键不在删,而在不带进去。
分层机制决定了中间层无法删除,但可以不继承
Docker 镜像的每一层都是只读且不可变的。RUN、COPY 等指令都会生成新层,即使你在后续指令里用 rm -rf /tmp,那个安装包和缓存仍躺在前面的层里。多阶段构建绕开了这个限制:它允许你定义多个独立的构建阶段,每个阶段有自己的基础镜像和完整层栈,而最终镜像只取指定阶段的指定文件。
- 第一阶段(builder)可使用 golang:1.21,安装全套编译工具、下载依赖、执行 build
- 第二阶段(runtime)直接用 alpine:3.20,只 COPY 第一阶段编译好的二进制文件
- builder 阶段的所有层——包括 go、gcc、.go 文件、pkg 缓存——完全不进入最终镜像
配合 --from 实现精准搬运,避免隐式残留
多阶段构建中,COPY --from=builder 是唯一合法的跨阶段引用方式。它不复制 builder 镜像的任何层,只把目标路径下的文件内容提取出来,作为新层写入当前阶段。这意味着:
- 不会继承 builder 的 apt 缓存、node_modules、/root/.cargo 等中间产物
- 不会带上 builder 阶段的环境变量、用户配置或未清理的临时目录
- 即使 builder 阶段用了
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*,最后那句rm也仅作用于该阶段自身,对最终镜像无意义;真正起效的是“根本不从这阶段复制无关内容”
单阶段内仍需注意层合并策略
多阶段解决的是跨阶段冗余,但每个阶段内部的层仍受 Docker 分层规则约束。为避免同一阶段内产生不必要的中间层,建议:
- 把关联操作合并到一条 RUN 指令中,例如
RUN apt-get update && apt-get install -y python3-pip && pip3 install flask && apt-get clean && rm -rf /var/lib/apt/lists/* - 避免在不同 RUN 中重复安装再清理,否则清理动作会生成新层,而前一层里的包依然存在
- 利用 .dockerignore 排除源码中不需要 COPY 的文件(如 node_modules、.git、docs),减少 COPY 层体积
构建后主动清理本地悬空缓存
即使用了多阶段,Docker 仍会保留 builder 阶段产生的中间镜像(状态为
-
docker builder prune:清除构建器缓存(推荐,更精准) -
docker image prune -f:清理所有悬空镜像(包括多阶段中未被引用的 builder 镜像) - CI 环境中建议加
--no-cache或定期prune,防止缓存无限膨胀











