多阶段构建的核心是让最终镜像仅含运行时必需的二进制、配置和依赖库,彻底剥离编译器、源码、测试工具等非运行成分;需选最小化基础镜像(如scratch、alpine或jre-alpine),构建阶段清理临时文件,运行阶段精准COPY产物,并补充非root用户、健康检查等生产加固措施。
多阶段构建本身不是目标,而是手段——关键在于让最终镜像只含运行时必需的二进制、配置和依赖库,彻底剥离编译器、源码、测试工具、文档、缓存等所有非运行成分。
构建阶段可以宽松(如 golang:1.21 或 maven:3.9-openjdk-17),但运行阶段必须切换到真正轻量的基础镜像:
scratch(0MB)或 distroless/static(2.5MB),前提是不依赖 glibc/musl 外部动态库;alpine:3.18(5.6MB),注意 Alpine 使用 musl libc,某些 C 扩展或预编译二进制可能不兼容;busybox:glibc(4.8MB)或 ubuntu:22.04-minimal(29MB),权衡安全与可观测性;eclipse-temurin:17-jre-alpine,再进一步用 jlink 定制最小 JRE 模块集,可压至 50MB 以内。即使用了多阶段,如果构建阶段残留大量临时文件(/tmp、node_modules/.cache、target/*.jar 副本、go/pkg),仍会增大构建上下文体积,间接拖慢整个流程。务必在构建阶段末尾清理干净:
RUN CGO_ENABLED=0 go build -a -ldflags '-extldflags "-static"' -o /app/myapp . && rm -rf $GOPATH/pkg;RUN npm ci --only=production && npm cache clean --force,避免 npm install 拉全量依赖;RUN pip install --no-cache-dir --no-deps -r requirements.txt,并显式删除 __pycache__ 和 .pyc;COPY 前用 .dockerignore 排除 node_modules/、vendor/、.git/、*.log、*.md 等,减少上下文传输量。多阶段的核心价值体现在 COPY --from=builder 这一步——它决定了最终镜像里有什么:
COPY --from=builder /app/myapp /usr/local/bin/myapp;COPY --from=builder /app/ . 整个目录——这会把构建时生成的中间文件一并带入;RUN apk add --no-cache ca-certificates 显式安装,而非从构建阶段拷贝系统证书目录。体积最小 ≠ 生产就绪。微服务还需满足安全与可观测基本要求:
RUN addgroup -g 1001 -f appgroup && adduser -S appuser -u 1001,再 USER appuser;WORKDIR /app && RUN chown appuser:appgroup /app;HEALTHCHECK --interval=30s --timeout=3s CMD wget --quiet --tries=1 --spider http://localhost:8080/health || exit 1(Go/Python 可改用 curl -f http://localhost:8080/health);EXPOSE 以外的网络暴露,端口由编排层统一管理。