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:开发与测试随意;生产建议托管数据库或至少独立宿主机 + 持久卷 + 备份策略——容器内数据库的运维成本常被低估。

官方参考

本文基于官方文档整理,未进行运行时或性能测试。示例中的业务函数、数据模型和部署地址需结合项目补全;局部片段不等同于完整生产应用。

延伸阅读

获取专属方案

联系我们

加入社区

微信扫码
加入官方交流群

立即体验

在线开通,按量计费,真正的云服务!

立即开始

选择观测云版本

代码托管平台