多阶段构建通过分离构建与运行环境,仅复制编译产物至极简基础镜像(如alpine约5mb或distroless约85mb),避免工具链和冗余文件进入最终镜像,显著压缩体积并提升安全性。
多阶段构建能显著压缩发布镜像体积,核心在于把构建环境和运行环境彻底分开,只让编译产物进入最终镜像,其余全部舍弃。
构建与运行严格分离
单阶段构建会把整个开发流程“快照”进一层层镜像中:装编译器、拉依赖、跑构建命令、留缓存——哪怕你后续删了文件,那些内容仍躺在历史层里,体积不减。多阶段构建用多个 FROM 划出独立环境,比如:
- 第一阶段用
golang:1.22-alpine或maven:3.9-openjdk-17-slim编译代码; - 第二阶段直接切到
alpine:3.20或gcr.io/distroless/java17-debian12这类极简镜像; - 仅通过
COPY --from=builder /app/app.jar .或COPY --from=builder /app/server .复制成品二进制或 fat jar。
选对基础镜像,一步省下几百MB
最终镜像大小很大程度取决于运行阶段的基础镜像:
-
alpine:3.20约 5MB,适合 Go、Rust 等静态编译语言; -
debian:slim约 60MB,兼容性好,适合需要 apt 安装少量运行时依赖的场景; -
gcr.io/distroless/java17-debian12约 85MB,无 shell、无包管理器,攻击面最小; - 避免用
openjdk:17-jre-slim,它比 alpine+手动装 jre 大 3 倍。
构建阶段也要精打细算
构建阶段虽不对外发布,但臃肿会影响构建速度和中间缓存效率:
- Go 项目加
CGO_ENABLED=0 GOOS=linux静态编译,避免 glibc 依赖; - Java 项目用
maven-shade-plugin打成 fat jar,省去运行阶段解压多个 jar 的麻烦; - Node.js 项目在构建阶段只
COPY package*.json再npm install,再COPY . .,防止 node_modules 被重复拷贝; - 所有阶段都加
--no-cache(如apk --no-cache add或apt-get clean -y && rm -rf /var/lib/apt/lists/*)。
别让这些操作悄悄毁掉瘦身效果
有些看似合理的行为,实际会让多阶段前功尽弃:
- 在最终阶段执行
RUN apk add curl或RUN pip install requests—— 工具和包全打进镜像; - 用
COPY --from=builder /app/ .整体复制,而不是精确指定文件,把 .git、test 文件、源码都带进去了; - 没写
.dockerignore,导致构建上下文塞入node_modules、target、.idea,拖慢构建还污染缓存。











