Django 应用 Docker 容器化实战
手把手把 Django 应用容器化:搭配 Gunicorn 与 Caddy、编写基础 Dockerfile、构建镜像、用 Docker Compose 编排多容器部署(含 PostgreSQL)。
直接回答:Django 容器化的标准配方是:Gunicorn 做应用服务器、Caddy/Nginx 做反向代理、PostgreSQL 独立容器、Dockerfile 打包应用、Compose 编排三者——一次构建,处处运行,减少依赖环境差异,仍需核对平台架构与外部服务。
为什么是 Docker
"在我机器上是好的"——环境差异是部署事故的万恶之源。Docker 把应用与依赖打包成不可变镜像:开发、测试、生产跑的是同一份东西,扩容就是多起几个容器。
准备演示项目
一个带 PostgreSQL 的 todo 应用。确认 requirements.txt 完整,settings 里数据库连接走环境变量:
DATABASES = {
"default": dj_database_url.parse(os.environ["DATABASE_URL"])
}
应用服务器与反向代理
Django 自带的 runserver 只是开发玩具。生产组合:
- Gunicorn:WSGI 应用服务器,多 worker 接请求;
- Caddy:反向代理 + 自动 HTTPS + 静态文件服务。
pip install gunicorn
编写 Dockerfile
FROM python:3.13-slim
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
RUN useradd -r app && chown -R app /app
USER app
# collectstatic 在专用构建配置或发布任务中执行,不把生产密钥写入镜像
EXPOSE 8000
CMD ["gunicorn", "myproject.wsgi:application", "--bind", "0.0.0.0:8000", "--workers", "3"]
要点:依赖层与应用层分开(构建缓存提速)、PYTHONUNBUFFERED 保证日志实时输出、非 root 运行可再上一道 USER 指令加固。
构建镜像
docker build -t myapp:1.0 .
docker images | grep myapp
Compose 编排
services:
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD}
volumes: [pgdata:/var/lib/postgresql/data]
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 10
web:
build: .
command: gunicorn myproject.wsgi:application --bind 0.0.0.0:8000
environment:
DATABASE_URL: ${DATABASE_URL:?set DATABASE_URL}
DJANGO_SECRET_KEY: ${DJANGO_SECRET_KEY:?set DJANGO_SECRET_KEY}
DEBUG: "0"
depends_on:
db:
condition: service_healthy
proxy:
image: caddy:2
ports: ["80:80", "443:443"]
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
depends_on: [web]
volumes:
pgdata:
caddy_data:
先以受保护的环境配置设置数据库 URL(主机 db,特殊密码需 URL 编码)、强随机密钥和允许的域名。Caddyfile 需站点块,例如本地验证用 :80 { reverse_proxy web:8000 };公网 HTTPS 需真实域名、DNS 与开放端口。静态文件必须另配 WhiteNoise 或专用服务,Compose 本身不处理这些应用设置。
上线前清单
- 迁移在部署流程中独立执行(
docker compose run web python manage.py migrate); .dockerignore排除.git、本地数据库、虚拟环境、.env 和密钥文件;- 镜像扫描进 CI,密钥绝不进镜像;
- 健康检查配上,滚动更新才有底气。
- 发送一次测试请求,核对代理、Gunicorn 与 Django 日志的时间和版本,确保失败请求能留下可追溯记录。
常见问题(FAQ)
Q:镜像太大怎么瘦身?
A:用 slim 基础镜像 + 多阶段构建;编译型依赖在 builder 阶段装好轮子再拷入;.dockerignore 别漏。
Q:静态文件谁来服务?
A:开发期 Caddy/Nginx 直接挂卷;生产推荐 WhiteNoise(单容器简单)或对象存储 + CDN(多副本)。
Q:数据库该容器化吗?
A:开发与测试随意;生产建议托管数据库或至少独立宿主机 + 持久卷 + 备份策略——容器内数据库的运维成本常被低估。
官方参考
本文基于官方文档整理,未进行运行时或性能测试。示例中的业务函数、数据模型和部署地址需结合项目补全;局部片段不等同于完整生产应用。