多阶段构建的核心是分离编译与运行:构建阶段(builder)使用含完整工具链的镜像安装依赖并编译,运行阶段(runner)则切换为极小镜像(如python:3.11-slim或distroless),仅复制编译产物和必要运行时文件,彻底剔除构建期工具、缓存及冗余组件。

直接用多阶段构建,核心是把“编译”和“运行”彻底分开——构建阶段装满工具,运行阶段只留能跑起来的那几样东西。
明确划分 builder 和 runner 两个阶段
第一阶段专注干活,第二阶段只负责运行,两者之间不能混用:
- 构建阶段(builder)用完整镜像,比如
golang:1.22、node:20-slim或maven:3.8-openjdk-17,安装依赖、拉代码、执行编译 - 运行阶段(runner)必须换极小镜像,如
alpine:3.19、python:3.11-slim或gcr.io/distroless/nodejs,不带 shell、不带包管理器、不带调试工具 - 构建阶段要写
AS builder显式命名;运行阶段不能有AS,也不能出现RUN go build这类命令 - 复制产物必须写明来源:
COPY --from=builder /app/dist/main.js /usr/share/nginx/html/,路径越精确越好
选对基础镜像,避免隐性膨胀
基础镜像选错,多阶段效果直接打折扣:
在 Linux 上通过 Docker 运行 OpenClaw,并使用 Tailscale 实现远程访问。⚠️ 涉及 sudo、Docker、Tailscale和凭证挂载——请先查阅安全章节...
- Go 项目开启静态编译:
CGO_ENABLED=0 GOOS=linux go build -a -ldflags '-s -w' -o app .,runner 阶段可直接用scratch(0B)或alpine - Python 项目优先用
python:3.11-slim(约 60–120MB),兼容 glibc、支持绝大多数 wheel;慎用-alpine,musl libc 容易导致 C 扩展报错 - Node.js 项目推荐
node:20-slim,若确认所有 native 模块(如bcrypt)有 Alpine wheel,再考虑node:20-alpine - 绝对不用
ubuntu:latest、debian:slim或node:20这类完整镜像当 runner,它们自带几百 MB 系统工具,完全没必要
构建阶段清理要到位,堵住体积泄漏点
即使分了阶段,builder 不清理,也可能把缓存、文档、头文件一并带进最终镜像:
- Debian/Ubuntu 系统:用
&& apt-get clean && rm -rf /var/lib/apt/lists/*收尾 - Alpine 系统:用
&& apk --no-cache add build-base,安装完立刻清缓存 - Python:虚拟环境建在 builder 阶段,只复制
/opt/venv/bin和/opt/venv/lib/python3.x/site-packages中实际需要的部分 - Node.js:用
npm ci --only=production替代npm install,跳过 devDependencies - 合并 RUN 命令:把安装、编译、清理写在同一行,避免中间层残留临时文件
验证是否真瘦身成功
别只看 docker images 的 SIZE 字段,要查内容是否干净:
- 运行
docker history your-image-name:最终镜像应只有 1~3 层,每层不超过几 MB;若看到几百 MB 的 layer,说明某步 COPY 或 RUN 泄漏了大文件 - 运行
docker run --rm -it your-image-name sh进去翻一翻:确认没有/src、/app/node_modules、/root/.m2这类构建期目录 - 检查运行阶段是否真的没装多余工具:执行
which gcc、which make应该返回空,否则说明基础镜像或 COPY 逻辑有问题










