多阶段构建的核心是分离编译与运行环境:第一阶段用 AS builder 显式命名(如 FROM golang:1.21-alpine AS builder)完成编译,第二阶段选用 alpine 或 distroless 等极简镜像,仅通过 COPY --from=builder 复制必要产物,严格避免携带源码、编译器或中间文件。

多阶段构建剥离编译环境,核心是把“编译”和“运行”彻底分开——构建阶段装工具、编译代码;运行阶段只留二进制或 jar,不带任何源码、编译器或中间文件。
明确划分两个阶段并命名构建阶段
第一阶段必须用 AS builder(或其他自定义名)显式命名,否则后续无法引用。例如:
-
FROM golang:1.21-alpine AS builder—— 用于编译 Go 程序 -
FROM maven:3.8-openjdk-17 AS builder—— 用于打包 Java 项目 -
FROM python:3.9-slim AS builder—— 用于安装依赖并编译 C 扩展
命名后,第二阶段才能通过 COPY --from=builder 精准复制产物。
运行阶段只选极简基础镜像
第二阶段禁止使用含完整 JDK 或 SDK 的镜像,比如 openjdk:17-jdk-slim(体积超 400MB)或 golang:1.21(约 900MB)。应优先选用:
-
alpine:latest(约 5MB),适合 Go/Python/Node.js 静态二进制或轻量服务 -
amazoncorretto:17-jre-alpine或eclipse/jre:17-jre,专为 Java 运行设计,不含 javac 等工具 -
gcr.io/distroless/static或scratch,极致精简,但需确保二进制静态链接且无动态依赖
注意:用 scratch 时,Go 要加 CGO_ENABLED=0,Java 则不适用——它没 ca-certificates 和 glibc,直接运行会报错。
只复制真正需要的文件,不带构建残留
COPY --from=builder 是关键操作,必须严格限定路径,避免误拷整个工作目录。常见写法:
- Go:
COPY --from=builder /app/main /usr/local/bin/ - Java:
COPY --from=builder /app/target/app.jar /app.jar - Python:
COPY --from=builder /app/dist/myapp.whl /tmp/,再在运行阶段 pip install
不要写 COPY --from=builder /app /app,那样会把源码、go.mod、target 目录全带进来,白费多阶段。
额外瘦身技巧不可少
仅靠多阶段还不够,配合以下操作效果更明显:
- 在构建阶段清理缓存:Go 项目加
RUN go clean -cache -modcache;Maven 加RUN mvn dependency:purge-local-repository -DreleasesOnly=true -q - 合并 RUN 指令减少镜像层数,比如把 apt update && apt install && rm -rf /var/lib/apt/lists/* 写在同一行
- 用
.dockerignore排除 .git、node_modules、__pycache__、.DS_Store 等无关文件


















