多阶段构建是精简微服务镜像最直接有效的方式,核心在于分离构建与运行环境,只保留运行必需产物;第一阶段用完整工具链编译打包,第二阶段切换alpine或distroless等轻量镜像,仅COPY二进制、JAR或dist目录等必要文件,避免gcc/npm/javac等工具残留。

多阶段构建是精简微服务镜像最直接有效的方式,核心在于分离构建与运行环境,只把真正需要的产物带进最终镜像。
明确划分构建与运行两个阶段
第一阶段用完整工具链(如 golang:1.21、node:18-slim 或 maven:3.9-openjdk-17)完成编译、测试和打包;第二阶段换用极轻量基础镜像(如 alpine:latest 或 distroless),仅复制可执行文件或已打包的 JAR/JS 包。
- 避免在最终镜像中保留
gcc、npm、javac等构建工具 - Go 项目典型体积从 ~900MB(golang 镜像)压到 ~15MB(alpine 运行镜像)
- Node.js 项目可从 ~400MB(node:18)降至 ~120MB(node:18-alpine),再配合 distroless 可进一步压缩
精准控制产物传递,避免冗余复制
使用 COPY --from=阶段名 显式指定来源,只拷贝目标文件,不带路径残留或临时文件。
- 编译生成的二进制文件、JAR 包或
dist/目录下静态资源即可 - 不要
COPY --from=builder . /app,而应COPY --from=builder /app/build/app.jar /app/ - 若需配置文件或证书,单独
COPY,不要依赖构建阶段的整个工作目录
配合基础镜像选型与运行时精简
运行阶段的基础镜像决定下限体积。优先考虑:
-
alpine:体积小、社区支持好,但注意 glibc 兼容性(部分 Go 二进制需加
CGO_ENABLED=0编译) -
distroless(如
gcr.io/distroless/static-debian12):无 shell、无包管理器,攻击面最小,适合纯二进制服务 - 避免
ubuntu或centos等通用发行版作为运行基础镜像
跳过非必要阶段加速 CI 构建
CI 流水线中并非每次都要走完整流程。通过 --target 指定阶段,可跳过构建阶段,直接复用已有中间镜像。
-
docker build --target runtime -t mysvc:ci .—— 仅构建运行阶段,前提是builder阶段镜像已存在并缓存 - 结合 BuildKit 的
cache-from和远程缓存,让不同流水线节点共享构建层 - 单元测试可单独设为一个
test阶段,CI 中按需触发,不污染主镜像流程











