多阶段构建可减少60%以上镜像体积和构建时间:第一阶段用python:3.11-slim-build安装编译依赖,第二阶段用python:3.11-slim仅复制site-packages;配合.dockerignore、分步COPY requirements.txt、避免早期USER切换及显式路径COPY,提升缓存复用率。

用多阶段构建分离依赖安装和运行时环境
Python项目镜像臃肿、构建慢,往往因为把 pip install 和源码编译全塞进最终镜像。多阶段构建能直接砍掉 60% 以上体积和构建时间——关键不是“要不要用”,而是“哪几层该拆”。
- 第一阶段用
python:3.11-slim-build(或带-slim的官方镜像)装build-essential、gcc和所有pyproject.toml或requirements.txt中的依赖,包括setuptools、cython等编译型包 - 第二阶段用更小的
python:3.11-slim(不带-build),只COPY --from=0 /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages,跳过重复安装 - 避免在第二阶段执行
pip install:它会重新解压 wheel、校验哈希、写入pip缓存目录,白白增加 20–40 秒
正确利用 Docker 构建缓存加速 pip 安装
缓存失效是构建变慢的头号原因。Docker 按 layer 逐行比对,只要某一行变了,后面所有 layer 全部重建。Python 项目里最常踩坑的是把 requirements.txt 复制和安装写成两步,但没控制好变更顺序。
- 先
COPY requirements.txt /tmp/,再RUN pip install --no-cache-dir -r /tmp/requirements.txt—— 这样只有requirements.txt改了才重装依赖 - 不要
COPY . /app在pip install前:哪怕只改了一个空格,整个pip installlayer 就失效 - 如果用了
pyproject.toml,注意pip install -e .会触发源码构建,务必放在最后阶段;想缓存依赖就单独提取出[build-system]里requires列表,生成临时requirements-build.txt
避免 COPY 整个项目后再删不需要的文件
有人习惯 COPY . /app 再 RUN rm -rf /app/tests /app/docs,这会让镜像里实际还藏着被删除文件的历史 layer,体积不减反增。
- 用
.dockerignore文件过滤:至少包含__pycache__、*.pyc、.git、tests/、docs/、venv/、.pytest_cache - 如果必须条件性包含某些文件(比如不同环境的配置),改用
COPY --chown=nonroot:nonroot pyproject.toml setup.py src/ /app/,显式列出需要的路径 -
.dockerignore不影响BUILDKIT缓存判断,但能显著减少传输到 daemon 的数据量,尤其对 CI 场景下网络上传耗时敏感
使用非 root 用户但别在 pip install 阶段切用户
安全要求非 root 运行容器,但很多人在 RUN pip install 前就 USER nonroot,结果因权限不足失败或悄悄降级为用户级安装(--user),导致后续找不到包。
立即学习“Python免费学习笔记(深入)”;
-
pip install必须在root用户下执行,确保写入/usr/local/lib/python3.x/site-packages - 只在最后阶段用
USER nonroot:nonroot,并在Dockerfile开头定义好 group 和 user:RUN addgroup -g 1001 -f app && adduser -S app -u 1001 - 注意某些包(如
psycopg2-binary)的 wheel 会检查PATH或LD_LIBRARY_PATH,切用户后这些变量可能被重置,需显式ENV PATH=/usr/local/bin:$PATH
真正难的不是写出一个能跑的 Dockerfile,而是让每次 git push 后 CI 构建都稳定复用前一次的 pip layer——这取决于你对依赖变更频率的预判、对 buildkit 缓存机制的理解,以及是否愿意为缓存友好性多维护一个生成 requirements 的脚本。


















