多阶段构建通过隔离构建与运行阶段,仅将最终二进制文件复制到精简基础镜像中,实现最小层数(如alpine+COPY共2层);其本质是跳过中间层而非删除,COPY --from不引入源阶段层数。
多阶段构建本身不是为了“减少层数”而设计的,而是为了解决镜像臃肿、安全风险高、体积过大等问题。但它确实能显著降低最终镜像的层数——关键在于:只保留运行必需的文件,跳过所有构建中间产物。
Docker 镜像层数由每条指令(FROM、RUN、COPY 等)产生的文件系统快照决定,不是由 RUN 条数简单相加。即使你在单阶段中用 RUN rm -rf /tmp 删除了临时文件,那一层依然存在,只是标记为“已删除”,实际空间未释放。
多阶段构建绕开了这个问题:它不尝试在一层里“擦除”,而是直接不用那些层。构建阶段的所有内容(Go 编译器、.go 源码、mod 缓存、中间对象)完全隔离在 builder 阶段,运行阶段从零开始,只 COPY 一个二进制文件。
最终镜像层数 = 运行阶段基础镜像层数 + 你主动添加的层数。所以控制起点最关键:
很多“层数没减下来”,其实是构建失败或误操作导致运行阶段退化成单阶段:
这个结构能稳定产出仅含 1 层基础镜像 + 1 层二进制的最终镜像:
# 构建阶段:缓存 go mod,编译静态二进制FROM golang:1.22-alpine AS builderWORKDIR /appCOPY go.mod go.sum ./RUN go mod downloadCOPY . .RUN CGO_ENABLED=0 go build -a -ldflags '-extldflags "-static"' -o /app/server .运行阶段:纯 Alpine,只 COPY 二进制
FROM alpine:3.20 WORKDIR /root COPY --from=builder /app/server . CMD ["./server"]
这里没有 RUN、没有 apk、没有额外文件操作——只有基础镜像层和一个 COPY 层,共两层。如果确认是纯静态链接且无需调试,把 alpine:3.20 换成 scratch,就能做到真正零层运行镜像。