多阶段构建是镜像瘦身的核心手段,通过在构建阶段使用完整环境编译、运行阶段仅复制必要产物到精简基础镜像(如alpine或distroless),剔除编译工具链、缓存和无关文件,显著减小体积并提升安全性与部署效率。

多阶段构建不是“加功能”,而是做减法——把构建时需要的庞大工具链和运行时无关的文件全部留在前一个阶段,只让最终镜像承载真正要跑起来的那一小部分。
用对基础镜像,直接砍掉大半体积
运行阶段别再用 ubuntu 或 centos 这类完整发行版。alpine 镜像通常只有 5MB 左右,distroless 甚至能压到 2–3MB。比如 Go 应用从 golang:1.22(约 1GB)构建后,切换到 alpine:3.19 或 gcr.io/distroless/static,光基础层就省下 99% 以上。
- Python 项目推荐用 python:3.11-slim(约 60MB),比 python:3.11(约 900MB)轻得多
- .NET 应用务必用 mcr.microsoft.com/dotnet/aspnet:8.0(约 80MB),而不是 sdk 镜像(约 1.2GB)
- C/C++ 或 Rust 编译产物可直接扔进 scratch 镜像(真正零依赖,体积≈你的二进制文件大小)
构建阶段要“干净”,别把缓存和中间件带进去
构建阶段虽然可以装一堆工具,但每一步都要清理临时产物。apt-get、apk、pip 等包管理器留下的缓存、文档、头文件、调试符号,全是体积黑洞。
- 用 apk --no-cache add 或 apt-get install -y --no-install-recommends 来避免默认安装无关包
- RUN 命令里把安装、编译、清理串成一行:RUN apk add --no-cache build-base && go build -o app . && rm -rf /var/cache/apk/*
- Go 项目开启静态编译:CGO_ENABLED=0 GOOS=linux go build -a -ldflags '-s -w' -o app .,去掉动态链接依赖,方便塞进 alpine 或 scratch
复制文件要“精准”,拒绝整个目录拖过去
COPY --from=builder 不是搬运工,是快递员——只送收件人指定的那几样东西。复制 /app/build/ 整个目录?很可能顺手把 test/、docs/、node_modules/ 全带进来了。
- 明确路径:COPY --from=builder /app/dist/main.js /usr/share/nginx/html/
- 多个文件用数组语法(Docker 23+ 支持):COPY --from=builder ["/app/bin/app", "/app/config.yaml"] /opt/app/
- 构建产物若含大量依赖(如 Python 的 site-packages),先在 builder 阶段打包成 wheel 或 zip,再解压到运行阶段,避免漏复制或误复制
结构设计要“克制”,阶段不是越多越好
三阶段(build → test → runtime)听着专业,但 test 阶段如果只是跑完就丢,它本身不贡献最终镜像,却会拖慢构建速度、增加出错点。真正该保留的只有两个核心阶段。
- 简单服务:builder + runtime 就够了,清晰、稳定、易维护
- 需验证产物完整性(如 checksum 校验、安全扫描):可在 builder 后加一个 verify 阶段,但不要把验证工具复制进 runtime
- 前端项目常见陷阱:用 node:18 构建,却在 runtime 阶段仍用 node:18 ——其实只需 nginx:alpine 或 httpd:alpine 托管静态资源


















