docker多阶段构建通过分离构建与运行环境实现镜像精简——第一阶段用含sdk的镜像编译,第二阶段用极简镜像仅复制必需产物,体积减少70%以上;需显式copy指定路径、善用构建参数和缓存,并验证产物纯净性与功能正确性。

用 Docker 多阶段构建,核心是把构建环境和运行环境彻底分开——编译打包在第一阶段完成,最终镜像只保留运行时必需的二进制文件和依赖,体积通常能减少 70% 以上。
明确划分构建与运行两个阶段
在同一个 Dockerfile 中用 多个 FROM 指令定义不同阶段,前一阶段可作为后一阶段的基础,但彼此隔离。关键不是“用了几个阶段”,而是“是否真正剥离了构建工具链”。
- 第一阶段(如
builder):使用含完整 SDK 的镜像(golang:1.22、node:20、maven:3.9-openjdk-17),安装依赖、编译源码、生成可执行文件或静态资源 - 第二阶段(如
production):切换为极简运行时镜像(alpine:3.20、debian:slim或scratch),仅复制上一阶段产出的二进制/配置/静态文件
精准复制产物,避免冗余文件泄露
用 COPY --from=builder 显式指定要复制的路径,不要用 COPY --from=0 . /app 这类宽泛操作,否则容易带入 /go、/node_modules、target/classes 等中间产物。
- Golang 示例:
COPY --from=builder /app/myserver /usr/local/bin/myserver - Node.js(生产构建后):
COPY --from=builder /app/dist /usr/src/app/dist,再用node:18-alpine运行 - Java(Maven):
COPY --from=builder /app/target/*.jar /app.jar,跳过整个target/目录其他内容
善用构建参数与缓存优化构建速度
多阶段本身不自动提速,但配合 --build-arg 和分层设计,能让高频变更部分(如源码)复用底层缓存。
- 把
git clone或ADD . /app放在阶段靠后位置,避免每次改 README 都重跑npm install - 用
ARG BUILD_ENV=prod控制是否安装 devDependencies(Node)或启用调试符号(Go) - 对 Go 项目添加
-ldflags="-s -w"去除调试信息,进一步压缩二进制体积
验证精简效果与运行正确性
别只看 docker images 大小,要实际检查镜像内容并测试启动流程。
- 运行
docker run --rm -it <image> ls -la /usr/local/bin</image>,确认只有目标程序,无gcc、npm、javac等构建工具 - 用
docker run --rm -it <image> ldd /usr/local/bin/myserver</image>(非 alpine)检查动态链接是否满足,必要时用CGO_ENABLED=0 go build生成纯静态二进制 - 启动容器后访问健康端点(如
/healthz)或执行简单请求,确保功能未因删减依赖而受损










