多阶段构建的核心是分离编译与运行:构建阶段使用含完整工具链的镜像(如golang:1.22)编译应用,运行阶段采用极小镜像(如alpine:3.20或scratch)仅复制静态二进制文件,通过--from=builder精准复制、cgo_enabled=0静态编译等手段,使镜像体积缩减70%以上,同时降低攻击面、提升部署效率。

多阶段构建的核心是把编译和运行彻底分开——构建阶段装满工具链,运行阶段只留能跑起来的那几样东西。最终镜像体积常能减少 70% 以上,还能降低攻击面、加快部署速度。
明确划分 builder 和 runner 两个阶段
第一阶段专注编译,第二阶段只负责运行,两者完全隔离:
- 构建阶段(builder)用含完整工具链的镜像,比如
golang:1.22、node:20-slim或maven:3.9-openjdk-17,执行依赖安装、代码拉取、编译打包 - 运行阶段(runner)必须换极小镜像,如
alpine:3.20、python:3.11-slim或gcr.io/distroless/static,不带 shell、包管理器、调试工具 - 构建阶段要写
AS builder显式命名;运行阶段不能有AS,也不能出现RUN go build这类命令
精准复制产物,别带多余文件
COPY 操作是关键控制点,路径越精确越安全:
- 用
COPY --from=builder /app/dist/main.js /usr/share/nginx/html/这种明确路径,避免COPY --from=builder . /app这类宽泛操作 - Golang 项目:只复制二进制文件,比如
COPY --from=builder /app/myserver /usr/local/bin/myserver - Java 项目:只复制
target/*.jar,跳过src/、pom.xml、target/classes - Node.js 项目:复制
/app/dist或/app/build目录,不复制node_modules或源码
选对基础镜像 + 构建阶段清理到位
镜像大小不是光靠“分阶段”就能压下来的,还得抠细节:
- 运行阶段慎用
ubuntu:latest、debian:slim或完整版node:20,它们自带几百 MB 系统工具 - Go 项目开启静态编译:
CGO_ENABLED=0 GOOS=linux go build -a -ldflags '-s -w' -o app .,runner 阶段可直上scratch - Python 优先用
python:3.11-slim(60–120MB),比-alpine更兼容 wheel;Node.js 推荐node:20-slim,确认 native 模块支持再切 Alpine - 构建阶段清理要写在同一条 RUN 里:Debian/Ubuntu 用
&& apt-get clean && rm -rf /var/lib/apt/lists/*;Alpine 用&& apk --no-cache add build-base后立刻清缓存
验证瘦身效果是否真实有效
别只看 docker images 表面大小,得动手查内容:
- 运行
docker run --rm -it <image> ls -la /usr/local/bin</image>,确认只有目标程序,没有gcc、npm、javac等构建工具 - 非 Alpine 镜像可用
ldd /usr/local/bin/myserver检查动态链接;Go 静态编译后应显示not a dynamic executable - 启动容器后访问健康端点(如
/healthz)或发个简单请求,确保功能没因删减依赖出问题











