推荐设置的环境变量
构建 Python Docker 镜像时,建议始终设置以下两个环境变量:
1 | # 防止 python 将 pyc 文件写入硬盘 |
不建议使用
ENV DEBUG 0这类环境变量,没有必要。
使用非 root 用户运行容器进程
出于安全考虑,推荐运行 Python 程序前,创建非 root 用户并切换到该用户:
1 | # 创建一个具有明确 UID 的非 root 用户,并增加访问 /app 文件夹的权限 |
使用 .dockerignore 排除无关文件
在项目根目录创建 .dockerignore 文件,排除不需要进入镜像的文件:
1 | **/__pycache__ |
不建议使用 Alpine 作为 Python 的基础镜像
这是一个常见的误区。很多人选择 Alpine 是因为它体积小,但实际上用 Alpine 构建 Python 镜像反而可能遇到更多问题,最终镜像甚至可能更大。
核心原因:musl vs glibc
大多数 Linux 发行版使用 GNU 版本(glibc)的标准 C 库,几乎每个 C 程序都需要这个库,包括 Python。但 Alpine Linux 使用的是 musl,这导致 Alpine 禁用了 Linux wheel 支持。
具体问题
- 缺少大量依赖:CPython 运行时依赖、openssl、libffi、gcc、数据库驱动、pip 等都需要额外安装
- 编译时间长:大多数 Python 包在 PyPI 上都提供了二进制 wheel,能大大加快安装时间。但 Alpine 禁用了 wheel 支持,意味着你需要编译每个 Python 包中的所有 C 代码
- 构建更耗时:缺少预编译的 wheel,每次 pip install 都要从源码编译
- 镜像可能更大:为了编译需要安装大量开发工具包,最终镜像反而比 slim 版本更大
如果确实需要使用 Alpine,pip 安装前需要先安装完整的编译依赖:
1 | RUN set -eux \ |
如果在 Alpine 上遇到具体的包安装问题,可以参考这篇:Dockerfile中Alpine安装依赖包失败总结
建议使用官方的 Python Slim 镜像
推荐使用 Docker Hub 官方的 Python Slim 镜像作为基础镜像:
- 镜像地址:https://hub.docker.com/_/python
- 推荐标签:
python:X.Y-slim或更具体的python:X.Y-slim-bullseye(基于更新的 Debian 版本,安全性更好)
为什么选择 Slim
- 稳定性:基于 Debian,经过充分测试
- 安全升级更及时:官方维护,安全补丁响应快
- 依赖更全:包含运行 Python 所需的最小包
- Python 版本升级更及时:第一时间获取最新 Python 版本
- 镜像体积小:不包含默认标签中的常用包,只保留运行 Python 的必要组件
一般情况下不需要多阶段构建
Python 不像 Golang 可以把所有依赖打成单一二进制包,也不像 Java 可以在 JDK 上构建、在 JRE 上运行。Python 复杂而散落的依赖关系,在多阶段构建时反而会增加复杂度。
可以考虑多阶段构建的特殊情况:
- 构建阶段需要安装编译器(如 gcc),运行时不需要
- Python 项目用到了其他语言代码(如 C/C++/Rust)
Dockerfile 中 pip 编写建议
使用 pip 安装依赖时,添加 --no-cache-dir 减少镜像体积:
1 | # 安装 pip 依赖 |
Dockerfile 完整示例
推荐:Slim 版本
1 | FROM python:3.10-slim |
不推荐:Alpine 版本
1 | FROM python:3.10-alpine |
总结
| 实践 | 建议 |
|---|---|
| 基础镜像 | 使用 python:X.Y-slim,避免 Alpine |
| 环境变量 | 设置 PYTHONDONTWRITEBYTECODE=1 和 PYTHONUNBUFFERED=1 |
| 安全 | 创建非 root 用户运行容器进程 |
| pip | 使用 --no-cache-dir 减少镜像体积 |
| .dockerignore | 排除 __pycache__、.git、venv 等无关文件 |
| 多阶段构建 | 一般不需要,除非涉及 C/C++/Rust 编译 |