多阶段构建通过分离构建与运行阶段,仅复制必要产物到极简基础镜像(如alpine或scratch),精准剥离编译器、源码、缓存等运行时无用内容,使镜像从几百mb压至十几mb。

要让Docker镜像从几百MB压缩到十几MB,必须把编译器、源码、依赖缓存这些运行时完全用不到的东西彻底剥离——多阶段构建就是专门干这件事的机制。
理解多阶段构建的本质
传统单阶段构建会把整个构建过程“快照”进最终镜像:你装了gcc、跑了go build、留了node_modules,它们全被固化在层里。而多阶段构建允许你在同一个Dockerfile里定义多个独立环境,只让指定文件跨阶段流动。
关键不是“用了几个FROM”,而是【每个阶段必须有明确角色:要么纯构建,要么纯运行】。混用会导致体积反弹——比如在运行阶段RUN apk add curl,就等于主动往精简镜像里塞工具。
写出第一个多阶段Dockerfile
方法一:Go应用最小化(推荐新手起步)
第一步:创建构建阶段,使用带Go环境的镜像
FROM golang:1.22-alpine AS builder
第二步:设置工作目录并复制源码
WORKDIR /app
COPY . .
第三步:静态编译,禁用CGO避免运行时依赖glibc
RUN CGO_ENABLED=0 GOOS=linux go build -a -ldflags '-s -w' -o myapp ./cmd/server
第四步:切换至极简运行环境
FROM alpine:3.20
RUN apk --no-cache add ca-certificates
第五步:仅复制上一阶段编译好的二进制文件
COPY --from=builder /app/myapp /usr/local/bin/myapp
第六步:声明运行命令
EXPOSE 8080
CMD ["/usr/local/bin/myapp"]
Java项目多阶段瘦身要点
方法1:Maven构建 + Alpine运行
构建阶段用maven:3.9-openjdk-17-slim,运行阶段切alpine:3.20+手动安装jre17-jre。注意【不要用openjdk:17-jre-slim,它比alpine+精简jre大3倍】。
方法2:跳过JRE,直奔Distroless
运行阶段改用gcr.io/distroless/java17-debian12,体积压到90MB以内,且彻底移除shell——连exec进入容器调试都不行,但攻击面归零。
这一步不能省:在构建阶段的pom.xml里配置maven-shade-plugin,把所有依赖打成fat jar,否则COPY时要手动处理一堆jar包。
避坑指南:这些操作会让多阶段失效
错误写法:在最终阶段执行RUN apt-get install或RUN pip install——这会把包管理器和下载的wheel文件全打进镜像。
危险操作:COPY --from=builder /app/ . ——斜杠结尾会递归复制整个/app目录,包括.git、tests、README.md等无关文件。
致命疏漏:没加.dockerignore文件,导致vendor/、node_modules/、target/这些巨型目录被COPY指令意外带上。











