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

用多阶段构建(Multi-stage Build)把编译环境和生产镜像彻底分开,是当前生产级 Docker 部署的标配做法。核心不是“能不能分”,而是“怎么分得干净、安全又可控”。
明确两个阶段的职责边界
构建阶段只干一件事:编译出可运行产物;运行阶段只做一件事:启动并运行这个产物。中间不混入任何多余文件、工具或源码。
- 构建阶段用完整工具链镜像(如
golang:1.22、node:20、maven:3.9-openjdk-17) - 运行阶段选极简基础镜像(如
alpine:latest、debian:slim或scratch) - 两个阶段之间只传递二进制文件、静态资源或配置模板,不继承文件系统、历史层或环境变量
用 --from 精确复制,杜绝冗余残留
COPY --from=builder 是分离的关键语法,它只拷贝指定路径下的文件,不会带入构建阶段的任何其他内容。
- 命名阶段(如
FROM golang:1.22 AS builder),后续直接引用名字,语义清晰 - 复制时指定绝对路径,例如
COPY --from=builder /app/myapp /usr/local/bin/ - 避免用
COPY --from=0这类数字索引,一旦增删阶段容易出错 - 若需多个产物(如二进制 + 配置模板 + 静态文件),逐条写明,不图省事打包整个目录
构建参数与运行变量各司其职
用 ARG 控制构建过程,用 ENV 定义运行行为,二者不混用、不越界。
-
ARG放在FROM前,用于动态选择基础镜像版本(如ARG GO_VERSION=1.22→FROM golang:${GO_VERSION}) -
ARG可设默认值,未传参时自动 fallback,但没设默认值又没用--build-arg会构建失败 -
ENV在运行阶段设置,比如ENV PORT=8080、ENV ENVIRONMENT=prod,这些变量最终固化进镜像,应用可直接读取 - 不要用
ENV存密码、密钥等敏感信息——运行时变量仍可见,应通过挂载 Secret 或外部配置中心注入
验证分离效果的三个硬指标
是否真正分离,不能只看 Dockerfile 写法,要落到实际镜像上检查。
- 镜像大小:运行阶段镜像应远小于构建阶段(典型 Go 服务从 350MB→15MB,Java Spring Boot 从 800MB→200MB)
- 文件内容:运行容器里执行
ls /usr/local/bin或apk list,只该有你显式复制的文件和必要依赖 - 工具链缺失:在运行容器中执行
go version、npm --version应报 command not found











