多阶段构建是压缩生产镜像体积最直接有效的手段,核心在于把“编译”和“运行”彻底分开——构建阶段装全工具链,运行阶段只留可执行文件和最小依赖,通过builder和runner两阶段分离、精确COPY产物、选用兼容精简基础镜像(如python:3.11-slim而非alpine)、清理缓存及避免冗余层,实现镜像从GB级降至MB级。

多阶段构建是压缩生产镜像体积最直接有效的手段,核心在于把“编译”和“运行”彻底分开——构建阶段装全工具链,运行阶段只留可执行文件和最小依赖。
明确划分 builder 和 runner 两个阶段
第一阶段专注编译,第二阶段只负责运行,两者不能混用:
- 构建阶段(builder)用含 SDK 的完整镜像,例如 golang:1.22、python:3.11-slim 或 maven:3.8-openjdk-11,完成代码拉取、依赖安装、编译打包
- 运行阶段(runner)必须换极简镜像,如 alpine:3.19、distroless/static-debian12 或 scratch,不带 shell、包管理器、调试工具
- 构建阶段必须用 AS builder 命名,运行阶段不能出现 RUN go build 或 RUN npm install 这类命令
- COPY 产物时要精确指定路径:COPY --from=builder /app/app /,避免 COPY 整个目录带入冗余文件
选对基础镜像,避开隐性膨胀陷阱
标签大小≠实际体积,兼容性与运行开销同样关键:
- Go 项目开启静态编译:CGO_ENABLED=0 GOOS=linux go build -a -o app,这样 runner 阶段可用 scratch(0B)或 alpine
- Python 项目优先用 python:3.11-slim(约 120MB),兼容 glibc、支持绝大多数预编译 wheel;慎用 alpine,musl libc 易导致 C 扩展编译失败
- Node.js 项目慎用 node:18-alpine,确认所有 native 模块(如 bcrypt、sqlite3)有 Alpine wheel;否则回退 node:18-slim
- 绝对不用 ubuntu、debian、node:18 这类完整发行版作为 runner 镜像——自带几百 MB 系统工具,完全没必要
每一步都精简,堵住体积泄漏点
即使用了多阶段,松散写法仍会让镜像悄悄变胖:
- builder 阶段清理系统缓存:RUN apt-get update && apt-get install -y build-essential && rm -rf /var/lib/apt/lists/*
- Go 项目先 COPY go.mod go.sum 再 RUN go mod download,利用 Docker 缓存加速重复构建
- 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 的 SIZE 字段,要查内容是否干净:
- 运行 docker history your-image-name:最终镜像应只有 1~3 层,每层不超过几 MB;若看到几百 MB 的 layer,说明某步 COPY 或 RUN 泄漏了大文件
- 检查 runner 阶段是否残留构建工具:docker run --rm -it your-image sh 应该报错(如用 scratch 或 distroless),说明没带 shell
- 对比优化前后体积:典型场景下,900MB 镜像压到 90MB 以下非常普遍


















