必须锁定Python小版本(如3.11.7)、用slim-bookworm镜像、CMD exec形式启动gunicorn并绑定0.0.0.0:8000,且Dockerfile中严格按COPY requirements.txt→RUN pip install→COPY . .顺序执行,三者缺一将导致构建低效或容器启动失败。

直接说结论:用 python:3.11.7-slim-bookworm 镜像,CMD ["gunicorn", "--bind", "0.0.0.0:8000", "app:app"] 启动,且必须先 COPY requirements.txt 再 RUN pip install —— 三者缺一,轻则构建变慢、重则容器启动即崩。
为什么必须锁定 Python 小版本(如 3.11.7)?
Python 官方镜像的 python:3.11-slim 是浮动标签,某天可能从 3.11.6 升到 3.11.9。而你的 pydantic 或 numpy 可能只兼容 ABI 稳定的小版本,升级后出现 ImportError 或 Illegal instruction (core dumped)。
- 查本地版本:
python --version,然后去 Docker Hub python 镜像页 找对应小版本的-slim-bookworm标签 - 别用
python:3.11-alpine:musl libc 导致psycopg2、numpy编译失败或运行时报libpq.so.5 not found - 也别用
python:3.11(无-slim):带完整apt工具链,镜像大、攻击面广
Dockerfile 中 COPY 和 RUN 的顺序为什么关键?
顺序错,每次改一行代码都要重装全部依赖——构建从秒级拖成分钟级。
- 正确顺序:
COPY requirements.txt .→RUN pip install --no-cache-dir -r requirements.txt→COPY . . - 错误写法:
COPY . .放在RUN pip install前面,哪怕只改了app.py一个空格,pip 层缓存全失效 - 如果项目用 Poetry,先本地执行:
poetry export -f requirements.txt --without-hashes > requirements.txt,再 COPY 进去 -
--no-cache-dir是为减小镜像体积,和构建缓存无关;真正管缓存的是文件内容是否变化
Web 服务为什么必须绑定 0.0.0.0 且用 exec 形式 CMD?
Flask 默认绑 127.0.0.1:5000,容器内能通,宿主机 curl localhost:5000 直接 Connection refused;用 CMD python app.py(shell form),PID 1 是 /bin/sh,不是你的 Web 进程,SIGTERM 杀不死它。
立即学习“Python免费学习笔记(深入)”;
- 开发时 Flask 必须写:
app.run(host='0.0.0.0', port=5000) - 生产环境别用
app.run,改用gunicorn:CMD ["gunicorn", "--bind", "0.0.0.0:8000", "app:app"] - 绝对不要:
CMD ["sh", "-c", "gunicorn ..."]或CMD gunicorn ...(隐含 shell) - 验证方法:
docker exec -it <container> ps aux</container>,看 PID 1 是否为gunicorn进程
最容易被忽略的点是:WORKDIR 没设或设晚了,导致 gunicorn app:app 找不到模块;还有 EXPOSE 8000 只是文档说明,真正要访问还得在 docker run -p 8000:8000 或 docker-compose.yml 的 ports 字段里显式映射。


















