Django自带的runserver只适合开发环境,生产部署时需要换成专业的WSGI服务器,而Docker则能把这些环境依赖统一打包,避免“在我机器上能跑”的尴尬。本文将围绕Dockerfile的编写、Gunicorn的启动命令配置,以及docker-compose的整合方案,完整演示一次Django容器化部署。

一、为什么Django部署要用Gunicorn而不是runserver
很多初学者在容器里直接执行python manage.py runserver 0.0.0.0:8000就把服务跑起来了,这在测试阶段没问题,但runserver是单线程的开发服务器,没有并发处理能力,也不具备生产级的安全加固。Django官方文档明确说明runserver绝对不要用于生产环境。
Gunicorn(Green Unicorn)是一个Python的WSGI HTTP服务器,采用pre-fork worker模型,通过多个worker进程并发处理请求,配合Nginx做反向代理后可以稳定支撑较高的流量。它的安装也非常简单,只需pip install gunicorn即可。启动一个Django项目的基本命令如下:
gunicorn myproject.wsgi:application --bind 0.0.0.0:8000 --workers 4
其中myproject.wsgi:application指向Django项目中的wsgi.py文件里的application对象。--workers建议按照CPU核心数的2倍加1来计算,比如2核服务器可以设置5个worker。此外还可以用--threads启用多线程、用--timeout设置请求超时时间,避免慢请求拖垮整个进程。
二、编写生产级Dockerfile
Dockerfile的编写质量直接影响镜像体积和构建速度。推荐使用官方的python slim镜像作为基础镜像,它比完整版镜像小很多,又保留了必要的系统库。下面是一个典型的Django项目Dockerfile:
FROM python:3.11-slim # 设置环境变量,防止Python生成pyc缓存文件并实时输出日志 ENV PYTHONDONTWRITEBYTECODE=1 ENV PYTHONUNBUFFERED=1 WORKDIR /app # 先复制依赖文件,利用Docker缓存机制 COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple # 复制项目代码 COPY . . # 收集静态文件 RUN python manage.py collectstatic --noinput EXPOSE 8000 CMD ["gunicorn", "myproject.wsgi:application", "--bind", "0.0.0.0:8000", "--workers", "4"]
这里有几个关键细节值得注意。第一,先复制requirements.txt再复制源码,可以利用Docker的层缓存机制,只要依赖不变,重新构建时就不会重复安装依赖,构建速度大幅提升。第二,设置PYTHONUNBUFFERED=1确保日志实时输出到stdout,方便docker logs查看。第三,collectstatic收集静态文件这一步也可以放到容器启动脚本中执行,取决于你的部署策略。
如果项目依赖了psycopg2等PostgreSQL驱动,slim镜像缺少编译工具,需要在安装依赖前添加系统包:
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc libpq-dev \
&& rm -rf /var/lib/apt/lists/*另外强烈建议在项目根目录添加.dockerignore文件,排除.git、venv、db.sqlite3、__pycache__等目录,否则这些无关文件会被一起打进镜像,既增大体积又可能引发意外问题。
三、用docker-compose整合数据库与Nginx
生产环境中Django通常需要搭配PostgreSQL数据库和Nginx反向代理,docker-compose可以把这些服务统一编排。下面是一个完整的compose配置示例:
services:
web:
build: .
command: gunicorn myproject.wsgi:application --bind 0.0.0.0:8000 --workers 4
volumes:
- static_volume:/app/staticfiles
- media_volume:/app/media
env_file:
- .env
depends_on:
- db
expose:
- "8000"
db:
image: postgres:16
environment:
POSTGRES_DB: mydb
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
volumes:
- postgres_data:/var/lib/postgresql/data
nginx:
image: nginx:stable
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
- static_volume:/app/staticfiles:ro
depends_on:
- web
volumes:
static_volume:
media_volume:
postgres_data:这套架构中,Gunicorn只在容器内部网络监听8000端口,由Nginx统一对外暴露80端口。静态文件通过共享volume交给Nginx直接返回,避免占用Python进程的资源,这是生产环境的标准做法。数据库密码等敏感信息不要写死在compose文件里,用.env文件管理并加入版本控制忽略列表。
Nginx的配置核心是把动态请求转发给web容器,静态文件直接从volume中读取:
server {
listen 80;
server_name localhost;
location /static/ {
alias /app/staticfiles/;
}
location /media/ {
alias /app/media/;
}
location / {
proxy_pass http://web:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
}
}四、常见问题与排查思路
部署过程中最常见的问题之一是静态文件404。原因通常是Django的STATIC_ROOT没有配置,或者容器内执行了collectstatic但Nginx没有挂载对应的volume。排查时先进入容器确认/app/staticfiles目录下有文件,再检查Nginx的alias路径是否一致。
第二个常见问题是数据库连接失败。Django配置中的HOST必须写成compose中的服务名db而不是localhost,因为每个容器有独立的网络命名空间,localhost指向容器自身。同时要注意depends_on只保证容器启动顺序,不保证数据库已就绪,可以在启动脚本中加入重试逻辑。
第三个问题是worker被杀。如果接口响应时间较长,Gunicorn默认30秒超时会直接杀掉worker,日志中表现为WORKER TIMEOUT。解决办法是根据业务情况调大--timeout参数,或者把耗时任务改成异步任务交给Celery处理,而不是让Web进程阻塞等待。
最后提醒一点,构建镜像后建议用docker exec -it 容器名 python manage.py check --deploy运行Django自带的部署检查,它会列出settings中不符合生产要求的安全配置,比如DEBUG未关闭、SECRET_KEY使用默认值等,是上线前非常实用的一道防线。整个流程跑通后,后续的持续集成只需要把docker compose up -d --build接入部署脚本即可实现一键发布。
Django Docker部署DockerfileGunicorn修改时间:2026-09-01 23:40:31