Python 项目 Docker 化部署实战
之前写了篇 Docker 配代理的文章,把 Docker 拉镜像慢的坑给填了。这次来聊聊一个更实际的话题——咋把 Python 项目正经地 Docker 化部署。
说实话,刚开始搞 Docker 的时候,我写的 Dockerfile 那叫一个粗糙,镜像动辄 1G+,构建还贼慢。后来踩了不少坑,才算摸出一套相对靠谱的写法。今天就从基础到进阶,一次性给大伙儿捋清楚。
前置条件清单(开搞之前先确认哈)
已安装 Docker(docker --version 能正常输出)
已安装 Docker Compose(docker compose version 或 docker-compose --version)
有一个能跑的 Python 项目(带 requirements.txt)
一个最基础的 Dockerfile
咱先从最简单的来,假设你有个 Python 项目,结构大概长这样:
1 | my-project/ |
最基本的 Dockerfile 写法:
1 | FROM python:3.10-slim |
看着挺简单对吧?但这其实就是个”能跑”的版本,离”好用”还差得远。
这里用的是 python:3.10-slim 而不是 python:3.10。slim 版本基于 Debian 精简版,体积小了一大截,一般场景够用了。如果你的项目需要编译 C 扩展,可能还得加上 build-essential。
层缓存优化——别小看这几行顺序
上面的 Dockerfile 里,我
Docker 构建镜像的时候是按层缓存的。如果某一层的输入没变,Docker 就直接用缓存,不会再执行。问题来了——如果你把 COPY . . 放前面,那你改一行代码,pip install 那层就得重新跑,每次构建都得重新下载所有依赖,坑爹不?
所以正确做法是:
1 | # 第一层:依赖文件(很少变动) |
.dockerignore——别把垃圾塞进镜像
好家伙,之前有一次构建镜像,发现咋这么大?一查才发现,COPY . . 把 .git、__pycache__、venv 全 TM 的拷进去了。
所以项目根目录下一定要加一个 .dockerignore:
1 | .git |
这玩意儿的作用跟 .gitignore 类似,就是告诉 Docker 构建的时候忽略哪些文件。
之前有兄弟跟我说他的镜像 build 完 2G 多,好家伙一查就是没加 .dockerignore,把 node_modules、.git、venv 全拷进去了。这三个加起来能占 1G+,离谱。
多阶段构建——进阶玩法
接下来是重头戏。如果你的项目有需要编译的依赖(比如 psycopg2、numpy 之类的),构建阶段需要 build-essential 这些编译工具,但运行的时候根本用不到。多阶段构建就是为了解决这个问题:
1 | FROM python:3.10-slim AS builder |
1 | FROM python:3.10-slim AS runtime |
这样搞出来的好处是:build-essential 那一套加起来好几百 MB 呢,运行时根本不需要。
原理很简单:Docker 只会把最后一个阶段打包成镜像,前面阶段的东西用完就扔了。但通过 COPY --from=builder 可以把需要的产物(这里是虚拟环境)拷过来。
踩坑点合集
1. 非 root 用户运行
默认情况下,容器里的进程是以 root 身份跑的。这在生产环境是个安全隐患——万一容器被攻破,攻击者直接就是 root。
1 | # 创建非 root 用户 |
把这两行加在 COPY . . 之后、CMD 之前就行。
注意文件权限问题!如果你先 COPY 代码再切换用户,appuser 可能没权限写某些目录。需要提前用 chown 处理好,或者在 COPY 之前就切换用户。
2. 时区问题
容器默认是 UTC 时区,日志时间和咱们差 8 个小时,排查问题的时候看着贼难受。
1 | ENV TZ=Asia/Shanghai |
或者更简单,直接设置环境变量让 Python 程序自己处理:
1 | ENV TZ=Asia/Shanghai |
不过建议还是用上面 ln -snf 的方式,这样系统层面的时区也是对的。
3. 代理配置
有些兄弟的服务器网络环境特殊,pip install 走不了公网,得配代理。这个问题我之前那篇文章已经详细讲过了,这里简单提一下:
1 | # 在 pip install 之前配置代理 |
构建的时候这样传:
1 | docker build --build-arg HTTP_PROXY=http://your-proxy:port \ |
更详细的 Docker 代理配置(包括 daemon.json 全局代理、镜像加速等),可以看我之前写的这篇文章:Docker 配置代理实战。
构建流程总览
构建流程
第一步:准备文件
写好 Dockerfile 和 .dockerignore,把不需要的文件排除掉
第二步:构建镜像
运行 docker build -t myapp .,Docker 按层执行指令并缓存
第三步:本地测试
docker run -p 8000:8000 myapp 跑起来看看有没有问题
第四步:编写 compose
写 docker-compose.yml,把数据库、Redis 等依赖服务一起编排
第五步:部署上线
docker compose up -d 一键拉起所有服务,完事儿 🎉
docker-compose.yml 示例
光有 Dockerfile 还不够,实际部署一般还得配合 docker-compose。来一个完整的示例:
点击展开 docker-compose.yml 完整示例
1 | version: "3.8" |
几个要点:
restart: unless-stopped让容器意外退出后自动重启,生产环境必备volumes把日志目录挂载出来,方便排查问题,不用每次都docker exec进去看depends_on控制启动顺序,但要注意它只保证容器启动,不保证服务就绪。如果你的应用对数据库连接有时序要求,代码里还是要做好重连逻辑
depends_on 不等于”等服务就绪”!好家伙,我之前就踩过这个坑——容器起来了但数据库还没初始化完,应用直接连不上崩了。建议用 wait-for-it.sh 或者 Docker Compose 的 healthcheck + condition: service_healthy 来解决。
最终版 Dockerfile
把上面的知识点整合一下,来一个生产可用的完整版:
点击展开最终版 Dockerfile
1 | FROM python:3.10-slim AS builder |
总结
回顾一下这篇文章的核心要点:
基础镜像选 slim 版 ,别用完整版,省空间requirements.txt 单独 COPY + pip install ,利用层缓存加速构建.dockerignore 必须有 ,别把无关文件塞进镜像多阶段构建 分离编译和运行环境,减小镜像体积非 root 用户运行 ,安全第一- 时区、代理这些坑提前踩好
Docker 化部署这事儿,说难不难,但细节很多。希望这篇文章能帮你少走点弯路,别像我当初一样踩坑踩到怀疑人生。









