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%的大坑:
必记原则
- 安全第一:
DEBUG 必须设为 False,数据库和Redis都设置复杂密码,所有流量强制走HTTPS。
- 高可用雏形:Gunicorn多Worker + 进程意外退出自动重启,保证服务持续在线。
- 可快速扩容:当前用单机部署,但架构设计和配置要预留水平扩展可能(比如无状态应用、共享存储与数据库)。
- 基础监控要到位:日志集中、进程存活探测、基本资源监控(CPU、内存、磁盘)一个不能少。
- 自动化半自动化结合:先写好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单机生产环境的部署闭环,主要涵盖了以下几个环节:
- 理解极简生产架构:Nginx → Gunicorn → PostgreSQL/Redis,并牢记5条铁则。
- 轻量级环境准备:服务器初始化、创建非root用户、配置防火墙,以及安全的环境变量设置。
- Docker Compose 全栈部署:一个
docker-compose.yml 将Web、数据库、缓存、反向代理全部串联,健康检查和自动重启一个不少。
- Nginx 与 Gunicorn 配置优化:HTTPS、安全头、静态文件加速、Worker 数量调优等实用技巧。
- PostgreSQL 自动备份:通过 django 管理命令实现压缩备份和旧文件清理,简单又可靠。
:::warning 上线前的最后检查清单
完成这些后,你的 django 应用就已经具备生产级稳定运行的能力。后续可以在此基础上逐步加入 CI/CD 管线、水平扩展和服务拆分,但核心骨架足够可靠,撑起早期业务完全没问题。