多阶段构建是解决镜像臃肿的必要手段,通过builder与runner阶段分离,将900MB镜像压缩至90MB以下;需选对基础镜像、精简每步操作并建立体积验证机制。

多阶段构建不是“锦上添花”的技巧,而是解决镜像臃肿问题的必要手段。它能将原本包含编译器、调试工具、源码、缓存等大量冗余内容的镜像,精简为仅含可执行文件或运行时依赖的最小集合——实际项目中从 900MB 压到 90MB 甚至更低,压缩率超 90%,完全可行。
拆分构建与运行环境,彻底隔离无用层
核心逻辑是:让“需要巨量工具才能编译”的阶段和“只需要一个二进制或 Python 包就能跑”的阶段互不污染。
- 第一阶段(builder)使用完整镜像(如
golang:1.22、python:3.9-slim或node:18),安装编译依赖、拉取源码、执行构建 - 第二阶段(runner)切换为极简镜像(如
alpine:3.19、python:3.9-slim或distroless),只通过COPY --from=builder复制最终产物(如app二进制、/opt/venv、build/目录) - 构建工具链、中间文件、
node_modules源码、__pycache__、测试代码等,全部留在 builder 阶段,不会进入最终镜像
选对基础镜像,避免 Alpine 的隐性开销
别盲目追“最小”,要算总账:Alpine 虽小,但 musl libc 可能触发源码编译,反而拖慢构建、引入兼容性风险。
- Python 项目优先用
python:3.11-slim(约 120MB):兼容 glibc、支持绝大多数预编译 wheel,体积可控且稳定 - Go 项目开启静态编译:
CGO_ENABLED=0 GOOS=linux go build -a -o app,这样第二阶段可直接用scratch(0B)或alpine - Node.js 项目可用
node:18-alpine,但需确认所有 native 模块(如 bcrypt)有 Alpine 兼容 wheel,否则回退node:18-slim
精简每一步,堵住体积泄漏点
即使用了多阶段,写法不当仍会悄悄膨胀镜像。
- 在 builder 阶段清理系统包缓存:
RUN apt-get update && apt-get install -y build-essential && rm -rf /var/lib/apt/lists/* - 避免
COPY . .后再RUN rm -f *.log *.tmp:删除操作不减少层大小,应把清理合并进前一步 - Python 场景推荐虚拟环境 +
--no-cache-dir:RUN python -m venv /opt/venv && /opt/venv/bin/pip install --no-cache-dir -r requirements.txt - Node.js 场景用
npm ci --only=production替代npm install,跳过devDependencies
验证与持续控制,防止优化失效
压缩不是一劳永逸,得建立检查机制。
- 构建后立刻查体积:
docker images --format "{{.Repository}}\t{{.Size}}" | grep your-app - 查看镜像分层构成:
docker history your-app:latest,确认 builder 阶段内容未残留 - CI 流水线中加入体积阈值断言,例如:
[[ $(docker image inspect your-app --format='{{.Size}}') -lt 104857600 ]] || (echo "Image too large!" && exit 1)(限制 100MB) - 定期用
docker-slim扫描运行时实际加载的文件,进一步剔除未使用的库


















