builder阶段必须用python:x-slim而非alpine,因musl libc导致numpy等包安装失败或源码编译,构建更慢、错误更多;slim镜像兼容预编译wheel,体积仅100–120MB,是稳妥起点。

builder 阶段必须用 python:x-slim,别碰 alpine
alpine 看似体积小,但 musl libc 会让 pip install 在安装 numpy、cryptography、psycopg2-binary 时频繁失败或降级为源码编译——结果构建更慢、错误更多、最终镜像反而可能更大。python:x-slim(如 python:3.12-slim)基于 Debian,兼容绝大多数预编译 wheel,体积通常仅 100–120MB,是 builder 阶段的稳妥选择。
常见错误现象:fatal error: Python.h: No such file or directory、ERROR: Command errored out with exit status 1、构建卡在某个包十几分钟不动。
- builder 阶段禁用
python:x-alpine,哪怕 runtime 阶段想用也不行——因为编译必须在 builder 完成 - 如果项目真有强体积约束(比如边缘设备),先验证所有依赖是否提供
musllinuxwheel(查 PyPI 页面的 "Download files" 标签) - 不要为省几十 MB 冒险把整个 CI 流水线拖进调试深渊
COPY --from=builder 只复制 site-packages 和代码,不复制整层文件系统
多阶段构建不是“写两个 FROM 就完事”,关键在精准搬运。builder 阶段装好的包默认在 /usr/local/lib/python3.x/site-packages 或 --user 模式下的 /root/.local/lib/python3.x/site-packages;runtime 阶段若直接 COPY --from=builder /usr/local /usr/local,会把 /usr/local/bin 里的 pip、wheel 等无用二进制也拖进来,白占空间。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- builder 中用
--target明确指定安装路径,例如:RUN pip install --no-cache-dir --target /build/site-packages -r requirements.txt - runtime 阶段只
COPY --from=builder /build/site-packages /usr/local/lib/python3.x/site-packages - 源码用显式列表复制:
COPY app.py models/ api/ ./,而非COPY . .(避免带入.git、tests/、docs/) - 务必配合
.dockerignore过滤掉__pycache__、*.pyc、.pyo、venv/、.env
RUN 指令要合并清理动作,避免残留缓存层
Docker 每条 RUN 指令都会生成一个新层,且该层内容不可逆删减。比如先 RUN pip install -r requirements.txt,再 RUN rm -rf /root/.cache/pip,前者产生的缓存仍留在上一层里,体积没真正减少。
正确做法是把安装、清理、字节码删除全部塞进单条 RUN,保证原子性:
RUN pip install --no-cache-dir -r requirements.txt \ && find /usr/local/lib/python3.12/site-packages -name "*.pyc" -delete \ && find /usr/local/lib/python3.12/site-packages -name "__pycache__" -delete \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
注意:--no-cache-dir 是 pip 参数,不是 docker build 参数;apt-get clean 和 rm -rf /var/lib/apt/lists/* 必须成对出现,否则 APT 缓存能吃掉 50MB+。
runner 阶段必须用独立 FROM,且禁用任何 RUN pip install
很多人以为 “builder 装完,COPY 过去就结束了”,结果在 runner 阶段又写一行 RUN pip install -r requirements.txt ——这等于把构建逻辑又执行一遍,不仅浪费时间,还让所有依赖重新固化进新层,彻底废掉多阶段的意义。
runner 阶段唯一任务是运行,因此:
- 必须以全新
FROM python:3.12-slim开始,不能FROM builder - 禁止任何
RUN操作,只保留COPY --from=builder、COPY代码、ENV、USER、EXPOSE、CMD - 若需非 root 用户,用
RUN useradd --create-home --shell /bin/bash appuser+USER appuser,别在 builder 阶段创建 - 检查最终镜像是否含
gcc:docker run --rm <image> which gcc</image>,返回空才表示剥离干净
实际压测中,一个含 fastapi + sqlalchemy + psycopg2-binary 的微服务,从单阶段 1.1GB 降到多阶段 142MB,冷启动拉取时间从 6 分半缩至 48 秒。最易被忽略的是:builder 和 runner 的基础镜像版本必须严格一致(如都是 python:3.12-slim),否则 site-packages 下的 C 扩展模块可能因 ABI 不兼容而报 ImportError。


















