django部署最佳实践 - 生产环境部署与运维

📂 所属阶段:第三部分 — 高级主题
🎯 难度等级:高级
⏰ 预计学习时间:6-8小时
🎒 前置知识:性能优化, 安全最佳实践


目录


部署核心架构与原则

极简生产架构

刚接触生产部署的同学很容易被各种重型组件吓倒——负载均衡器、自动扩缩容、服务网格……但其实,一个稳定、高效的单机架构完全能支撑日均数千甚至数万的请求,而且理解和维护成本极低。

我们先从最经典的 单机Docker可扩展架构 入手,整个结构清晰分层,未来要横向扩展也很容易:

Internet

Nginx (反向代理、SSL终结、静态文件加速)

Gunicorn Workers (WSGI 应用服务器)

PostgreSQL (主数据库)

Redis (缓存、异步任务队列 Broker)

Celery (可选:异步任务处理)
  • Nginx:站在最前线,处理客户端所有请求,负责HTTPS加密、静态文件响应,并把动态请求转发给后端的Gunicorn。
  • Gunicorn Workers:运行django应用代码的地方,多进程并行处理Web请求。
  • PostgreSQL:数据持久化,生产环境中必须配置强密码、定期备份。
  • Redis:轻量但强大的内存数据存储,用于缓存、会话保持、消息队列。
  • Celery(可选):处理耗时异步任务,比如发送邮件、生成报表,不会阻塞用户请求。

这个架构最大的优点是简单、易维护,而且用Docker Compose一把梭就能跑起来,后面引向Docker Swarm 或 Kubernetes 也只是把定义文件升级一下,思路完全平滑。

5条铁则

在生产环境里,有一些底线绝对不能破。记住这5条,能帮你避开90%的大坑

必记原则
  1. 安全第一DEBUG 必须设为 False,数据库和Redis都设置复杂密码,所有流量强制走HTTPS。
  2. 高可用雏形:Gunicorn多Worker + 进程意外退出自动重启,保证服务持续在线。
  3. 可快速扩容:当前用单机部署,但架构设计和配置要预留水平扩展可能(比如无状态应用、共享存储与数据库)。
  4. 基础监控要到位:日志集中、进程存活探测、基本资源监控(CPU、内存、磁盘)一个不能少。
  5. 自动化半自动化结合:先写好Shell脚本或个把命令行,能手动一键完成部署、备份,再慢慢引入CI/CD管线,别一上来就想全自动化。 :::

轻量级环境准备

我们假设你已经拥有一台 干净的 Ubuntu 20.04/22.04 服务器,没有安装过Nginx、Python或其他Web服务。下面的步骤能帮你快速搭建一个安全、干净的应用运行环境。

服务器初始化脚本

直接复制以下脚本,保存为 init_server.sh,用 root 用户执行即可。它只安装生产环境必需的依赖,没有多余工具:

:::code-group

#!/bin/bash
set -e  # 遇到错误立即退出

# 更新系统并升级所有软件包
sudo apt update && sudo apt upgrade -y

# 安装生产核心依赖
sudo apt install -y \
    python3-pip python3-venv python3-dev \
    build-essential libpq-dev libssl-dev \
    nginx supervisor git curl

# 创建专门的非root应用用户(安全必做)
sudo useradd --system --home /app --shell /bin/bash app
sudo mkdir -p /app /var/log/myapp /var/run/myapp
sudo chown -R app:app /app /var/log/myapp /var/run/myapp

# 防火墙只开放必需的端口
sudo ufw allow ssh
sudo ufw allow 'Nginx Full'   # 包含 80 和 443
sudo ufw --force enable

echo "✅ 服务器初始化完成!请切换到 app 用户继续部署:sudo -iu app"

几个关键点解释:

  • python3-venv:用来创建虚拟环境。
  • libpq-dev:编译 psycopg2 连接PostgreSQL所必需。
  • supervisor:如果你不想依赖Docker的重启策略,可以用它管理Gunicorn进程。
  • 防火墙:只允许SSH和Web流量进入,其他端口全部关闭。
  • 非root用户:从安全角度,应用代码永远不要用root执行。

应用环境变量模板

django 生产配置通常通过环境变量注入,避免将敏感信息硬编码在代码里。请在你的项目根目录创建 .env 文件(切记不要提交到Git仓库):

:::code-group

# 基础安全配置
DEBUG=False
SECRET_KEY=请生成一个足够长且随机的字符串
ALLOWED_HOSTS=yourdomain.com,www.yourdomain.com

# 数据库连接
DB_NAME=myapp
DB_USER=myappuser
DB_PASSWORD=请设置高强度数据库密码
DB_HOST=db          # Docker Compose中的服务名
DB_PORT=5432
DB_CONN_MAX_AGE=60  # 数据库连接保持时间,减少频繁建立连接的开销

# Redis 连接
REDIS_URL=redis://redis:6379/0

:::

安全警告

生产环境的 SECRET_KEY 一定要足够随机(至少50个字符,包含大小写字母、数字和符号),且不能与开发环境共用。你可以用 openssl rand -base64 48 生成一个强随机值。


Docker Compose全栈部署

如果你希望几分钟内把整个应用栈在本地或服务器上跑起来,Docker Compose 是最佳选择。下面我们一步步构建django应用的容器化部署。

准备应用 Dockerfile

这个 Dockerfile 遵循多层构建、最小权限等最佳实践,可以有效减小镜像体积并提升安全性:

:::code-group

FROM python:3.11-slim

# 设置环境变量,避免生成 .pyc 文件,并使日志输出不缓冲
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
# 指定django使用的生产配置模块
ENV DJANGO_SETTINGS_MODULE=myproject.settings.production

# 单层安装系统依赖并清理缓存,减小镜像层大小
RUN apt-get update && apt-get install -y --no-install-recommends \
    gcc libpq-dev libjpeg-dev zlib1g-dev \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app

# 先复制依赖文件,利用Docker缓存加速后续构建
COPY requirements.txt .
RUN pip install --no-cache-dir --upgrade pip setuptools wheel && \
    pip install --no-cache-dir -r requirements.txt

# 复制应用代码
COPY . .

# 创建非root用户并更改文件所有权
RUN useradd --create-home --shell /bin/bash app && \
    chown -R app:app /app
USER app

# 收集静态文件到 STATIC_ROOT
RUN python manage.py collectstatic --noinput

EXPOSE 8000
CMD ["gunicorn", "--config", "gunicorn.conf.py", "myproject.wsgi:application"]

:::

生产环境依赖清单

把下面内容保存为 requirements.txt,使用锁定版本来避免因依赖升级引发的意外问题:

:::code-group

django>=4.2,<5.0
gunicorn==21.2.0
psycopg2-binary==2.9.7
redis==5.0.1
celery==5.3.4
django-redis==5.4.0
Pillow==10.0.1
python-decouple==3.8
whitenoise==6.6.0

:::

完整的 Docker Compose 配置

下面这个 docker-compose.yml 定义了多个服务:PostgreSQL数据库、Redis缓存、Web应用和Nginx反向代理。通过卷挂载和网络桥接,它们能够高效通信:

:::code-group

version: '3.8'

services:
  db:
    image: postgres:15-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data/
      - ./postgres/init:/docker-entrypoint-initdb.d/ # 可选:初始化SQL脚本目录
    environment:
      - POSTGRES_DB=${DB_NAME}
      - POSTGRES_USER=${DB_USER}
      - POSTGRES_PASSWORD=${DB_PASSWORD}
    networks:
      - app-network
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    # 开启AOF持久化并设置密码(如果设置了REDIS_PASSWORD环境变量)
    command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD:-}
    volumes:
      - redis_data:/data
    networks:
      - app-network
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5

  web:
    build: .
    ports:
      - "127.0.0.1:8000:8000"     # 仅允许来自本机的Nginx访问
    environment:
      - DB_HOST=db
      - REDIS_URL=${REDIS_URL}
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
    volumes:
      - static_volume:/app/static
      - media_volume:/app/media
    networks:
      - app-network
    restart: unless-stopped

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf               # 主配置
      - ./nginx/conf.d:/etc/nginx/conf.d                       # 虚拟主机配置目录
      - static_volume:/app/static:ro                           # 只读挂载静态文件
      - media_volume:/app/media:ro                             # 只读挂载用户上传文件
      - ./ssl:/etc/ssl:ro                                      # SSL证书目录
    depends_on:
      - web
    networks:
      - app-network
    restart: unless-stopped

volumes:
  postgres_data:
  redis_data:
  static_volume:
  media_volume:

networks:
  app-network:
    driver: bridge

:::

为什么这么设计?

  • web 容器端口映射到 127.0.0.1:即使服务器公网IP暴露了8000端口也访问不到,只有本地Nginx能转发,多一层安全。
  • 健康检查与依赖条件:确保数据库和Redis启动就绪后,Web服务才启动,避免启动顺序导致的错误。
  • 卷(volumes):静态文件和媒体文件通过命名卷在web和nginx之间共享,既持久化又隔离。
  • 重启策略 unless-stopped:除非显式停止,否则进程崩溃都会自动重启,基本满足高可用。

基础但高效的Nginx配置

Nginx是整条请求链路的“门面”,好的配置能让响应速度提升不少,也能挡住很多恶意扫描。

主配置文件 nginx.conf

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
    worker_connections 1024;
    use epoll;            # Linux下高性能事件模型
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    
    # 自定义日志格式,包含请求处理时间
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$request_time"';
    
    access_log /var/log/nginx/access.log main;
    
    # 基础性能调优
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    client_max_body_size 16M;   # 允许上传的最大文件大小
    
    # 开启Gzip,减少传输数据量
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_types text/plain text/css text/javascript application/json application/javascript image/svg+xml;
    
    include /etc/nginx/conf.d/*.conf;   # 引入站点配置
}

站点虚拟主机配置

我们将分别处理HTTP到HTTPS的重定向,以及HTTPS的正式服务。请注意替换 yourdomain.com 为你的实际域名。

# nginx/conf.d/myapp.conf
upstream django {
    server web:8000;   # 指向 docker-compose 中的 web 服务
}

# 强制HTTPS重定向
server {
    listen 80;
    listen [::]:80;
    server_name yourdomain.com www.yourdomain.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name yourdomain.com www.yourdomain.com;
    
    # SSL证书路径(建议使用Certbot通过Let's Encrypt自动申请)
    ssl_certificate /etc/ssl/certs/fullchain.pem;
    ssl_certificate_key /etc/ssl/private/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;     # 只允许安全的TLS协议版本
    ssl_session_cache shared:SSL:10m;
    
    # 重要安全响应头
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    
    # 静态文件(缓存1年,访问日志可关掉以减轻I/O)
    location /static/ {
        alias /app/static/;
        expires 1y;
        add_header Cache-Control "public, immutable";
        access_log off;
    }
    
    # 媒体文件(禁止执行任何脚本)
    location /media/ {
        alias /app/media/;
        expires 30d;
        add_header Cache-Control "public";
        location ~* \.(php|py|sh)$ { deny all; return 404; }
    }
    
    # 代理到Gunicorn
    location / {
        proxy_pass http://django;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 60s;
        proxy_buffering on;   # 开启缓冲提升后端响应速度
    }
    
    # 健康检查简单端点,监控系统可以用
    location /health/ {
        access_log off;
        return 200 "healthy\n";
        add_header Content-Type text/plain;
    }
}

这份配置生产就绪,但记得去申请SSL证书并放入对应路径(./ssl目录),否则Nginx会因找不到证书而启动失败。


Gunicorn配置与优化

Gunicorn 是 Python WSGI 服务器的事实标准,配置文件让调优非常方便。

# gunicorn.conf.py
import multiprocessing

# 核心配置
bind = "0.0.0.0:8000"
workers = multiprocessing.cpu_count() * 2 + 1   # 经典公式:CPU核数 × 2 + 1
worker_class = "sync"                            # 普通应用就用同步Worker,如果IO密集可考虑 gevent
worker_connections = 1000
timeout = 30                                     # Worker处理请求的超时秒数
max_requests = 1000                              # 处理1000个请求后Worker自动重启,避免内存泄漏
max_requests_jitter = 100                        # 随机增减最多100个请求,防止所有Worker同时重启

# 性能优化
preload_app = True                               # 预加载应用代码,减少每个Worker的内存占用
tmp_upload_dir = "/dev/shm"                      # 临时文件放在内存文件系统中,加速上传处理

几点微调建议:

  • worker数量CPU核数 × 2 + 1 适合大部分场景。如果你的视图大部分是CPU密集型(很少见),可以降到 核数 + 1,避免过多上下文切换。
  • sync vs gevent:对大多数标准的django应用,sync 模型简单可靠;只有在大量第三方API调用或WebSocket场景才需要切换到gevent,但要额外安装依赖并注意代码兼容性。
  • max_requests:强制Worker定期重启,可以有效防止长时间运行导致的内存增长(即便Python没有明显内存泄漏,碎片也会累积)。

PostgreSQL生产安全与备份

数据库是业务的心脏,生产环境必须做到“跑得稳”和“丢不了”。除了前面Docker Compose里设置了强密码外,我们还必须配置自动备份

数据库备份管理命令

下面是一个可被 django 管理命令调用的备份脚本,你也可以单独作为 Python 脚本运行,甚至配置到 Celery 定时任务中。

# myapp/management/commands/backup_db.py
import os
import datetime
import subprocess
from django.conf import settings
from django.core.management.base import BaseCommand

class Command(BaseCommand):
    help = "备份 PostgreSQL 数据库,并自动清理旧备份"

    def handle(self, *args, **options):
        timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
        backup_dir = '/backups'
        os.makedirs(backup_dir, exist_ok=True)
        backup_file = f"{backup_dir}/myapp_{timestamp}.sql.gz"

        try:
            # 构造 pg_dump 命令
            cmd = [
                'pg_dump',
                '-h', settings.DATABASES['default']['HOST'],
                '-U', settings.DATABASES['default']['USER'],
                '-d', settings.DATABASES['default']['NAME'],
            ]
            # 通过环境变量传递数据库密码
            env = os.environ.copy()
            env['PGPASSWORD'] = settings.DATABASES['default']['PASSWORD']

            # 使用管道直接压缩,减少I/O
            with open(backup_file, 'wb') as f:
                proc1 = subprocess.Popen(cmd, env=env, stdout=subprocess.PIPE)
                proc2 = subprocess.Popen(['gzip'], stdin=proc1.stdout, stdout=f)
                proc1.stdout.close()
                proc2.wait()

            # 删除7天前的旧备份
            cutoff = datetime.datetime.now() - datetime.timedelta(days=7)
            for filename in os.listdir(backup_dir):
                file_path = os.path.join(backup_dir, filename)
                if os.path.getmtime(file_path) < cutoff.timestamp():
                    os.remove(file_path)

            self.stdout.write(self.style.SUCCESS(f"备份成功: {backup_file}"))
        except Exception as e:
            self.stderr.write(self.style.ERROR(f"备份失败: {str(e)}"))

你可以通过 python manage.py backup_db 手动运行,或者稍后集成到 Celery Beat 定时调度(例如每天凌晨3点执行),并将备份目录挂载到宿主机或云存储。

:::tip 生产小贴士

  • 备份目录 (/backups) 应当通过 Docker Compose 的卷映射到宿主机某路径,或者定时同步到远程对象存储(如 AWS S3)。
  • 定期进行恢复演练,确保备份文件真的能用。 :::

本章小结

今天我们完整走了一遍django单机生产环境的部署闭环,主要涵盖了以下几个环节:

  1. 理解极简生产架构:Nginx → Gunicorn → PostgreSQL/Redis,并牢记5条铁则。
  2. 轻量级环境准备:服务器初始化、创建非root用户、配置防火墙,以及安全的环境变量设置。
  3. Docker Compose 全栈部署:一个 docker-compose.yml 将Web、数据库、缓存、反向代理全部串联,健康检查和自动重启一个不少。
  4. Nginx 与 Gunicorn 配置优化:HTTPS、安全头、静态文件加速、Worker 数量调优等实用技巧。
  5. PostgreSQL 自动备份:通过 django 管理命令实现压缩备份和旧文件清理,简单又可靠。

:::warning 上线前的最后检查清单

  • 申请并配置免费且能自动续期的 Let's Encrypt SSL 证书
  • 彻底关闭 DEBUG 并检查所有 ALLOWED_HOSTS
  • 修改默认管理员密码,设置数据库强密码。
  • 使用 docker compose logs/health/ 端点确认所有服务正常。
  • 如果需要更精细的进程守护(不依赖Docker重启),可以额外配置 Supervisor。
  • 接入基础监控(如 Prometheus + Grafana 或简单的 Uptime Kuma),对CPU、内存、磁盘和请求错误率有个大致感知。 :::

完成这些后,你的 django 应用就已经具备生产级稳定运行的能力。后续可以在此基础上逐步加入 CI/CD 管线、水平扩展和服务拆分,但核心骨架足够可靠,撑起早期业务完全没问题。