docker多阶段构建是贯穿研发全流程的关键实践,通过分离构建与运行环境,实现开发、测试、运维职责清晰协同;支持按需构建builder或final阶段,结合缓存优化和统一交付标准,提升安全、效率与协作质量。

Docker 多阶段构建不是只在打包时才用的技巧,而是贯穿研发全流程的关键实践。它把构建、测试、交付三个环节自然衔接,让开发、测试、运维各司其职又协同一致。
明确区分构建与运行环境,避免“一套镜像打天下”
传统做法常把编译器、测试工具、源码全塞进生产镜像,既不安全也不可控。多阶段构建强制你思考:哪些东西真正在运行时需要?
- 构建阶段(builder):用 golang:1.21、maven:3.9、node:20-alpine 等完整镜像,装依赖、跑单元测试、生成产物
- 运行阶段(final):切到 alpine:latest、distroless 或 scratch,只放二进制、配置文件、证书等最小集合
这样,CI 流水线里跑测试用的是 builder 阶段,部署上线用的是 final 阶段,职责清晰,互不干扰。
在 CI/CD 中按需触发不同阶段,提升构建效率
GitLab CI、GitHub Actions 或 Jenkins 都支持 --target 参数,直接指定构建哪个阶段:
-
docker build --target builder -t myapp:dev .→ 本地开发或 CI 中快速验证编译和测试 -
docker build --target final -t myapp:prod .→ 发布前生成精简镜像 - 还可以加
--build-arg动态传参,比如BUILD_ENV=staging控制配置加载逻辑
这样不用改 Dockerfile 就能复用同一份定义,适配 dev/test/prod 多套流程。
结合缓存与分层设计,加速重复构建
多阶段本身不提速,但配合合理分层就能显著减少 CI 耗时:
- 把
COPY package*.json .和RUN npm install放在构建阶段靠前位置,依赖不变时直接命中缓存 - 用
.dockerignore屏蔽node_modules/、.git/、*.log,防止无关变更污染缓存层 - 在 builder 阶段内再拆子阶段(如单独拉依赖、单独编译),进一步细化缓存粒度
实际项目中,前端构建常先 npm install 再 npm run build,后端则先 go mod download 再 go build,都是为缓存服务。
统一交付物标准,简化 QA 与运维协作
最终交付的镜像不再包含源码、.git 文件、调试工具或未删日志——这些都在 builder 阶段被自然隔离。
- QA 测试时拿到的镜像,和线上运行的一致,无需担心“本地能跑线上挂”的环境差异
- 运维扫描镜像漏洞时,面对的是纯 Alpine 或 Distroless,攻击面小、报告干净
- 审计人员看到清晰的
FROM ... AS builder和COPY --from=builder,立刻理解构建逻辑,不需翻多个脚本
不复杂但容易忽略











