结论:多阶段构建允许在一个 Dockerfile 中定义多个 FROM 阶段,前一阶段的产物可以有选择地拷贝到后续阶段,从而把编译环境和运行时环境彻底分离,解决最终镜像携带编译工具链导致体积臃肿、攻击面大的问题。

展开:典型场景是编译型语言:Go 需要 go toolchain、Maven 需要 JDK 和依赖缓存,这些在运行时完全不需要。没有多阶段之前,要么写复杂的单 RUN 脚本边编译边卸载,要么维护两个 Dockerfile 加外部脚本编排,都很脆弱。多阶段构建后一个文件自包含,CI 环境只需 Docker 即可复现构建。示例:

FROM golang:1.22 AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -o server .

FROM alpine:3.19
COPY --from=builder /app/server /server
ENTRYPOINT ["/server"]

要点:--from=builder 引用命名阶段;CGO_ENABLED=0 生成静态二进制才能跑在 alpine/scratch 上。还可以用 docker build --target builder 只构建到某阶段做单元测试。最终镜像从约 1GB 降到 20MB 左右很常见。