部署
将 Django 应用部署到生产环境需要考虑安全性、性能和可靠性。本节介绍常见的部署方案。
部署前准备
安全检查清单
# settings.py
# 1. 关闭调试模式
DEBUG = False
# 2. 设置允许的主机
ALLOWED_HOSTS = ['example.com', 'www.example.com']
# 3. 安全密钥
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')
# 4. 安全设置
SECURE_SSL_REDIRECT = True
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
X_FRAME_OPTIONS = 'DENY'
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
# 5. 数据库
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.environ.get('DB_NAME'),
'USER': os.environ.get('DB_USER'),
'PASSWORD': os.environ.get('DB_PASSWORD'),
'HOST': os.environ.get('DB_HOST'),
'PORT': os.environ.get('DB_PORT', '5432'),
}
}
# 6. 静态文件
STATIC_ROOT = BASE_DIR / 'staticfiles'
STATIC_URL = '/static/'
# 7. 媒体文件
MEDIA_ROOT = BASE_DIR / 'media'
MEDIA_URL = '/media/'
环境变量管理
# .env
DJANGO_SECRET_KEY=your-secret-key-here
DEBUG=False
ALLOWED_HOSTS=example.com,www.example.com
DB_NAME=mydb
DB_USER=myuser
DB_PASSWORD=mypassword
DB_HOST=localhost
DB_PORT=5432
# settings.py
import os
from pathlib import Path
from dotenv import load_dotenv
load_dotenv()
BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')
DEBUG = os.environ.get('DEBUG', 'False') == 'True'
ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', '').split(',')
requirements.txt
Django==5.0
gunicorn==21.2.0
psycopg2-binary==2.9.9
django-environ==0.11.2
whitenoise==6.6.0
redis==5.0.1
celery==5.3.4
Gunicorn + Nginx 部署
Gunicorn 配置
# gunicorn.conf.py
import multiprocessing
bind = '127.0.0.1:8000'
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = 'sync'
timeout = 30
keepalive = 2
errorlog = '-'
accesslog = '-'
loglevel = 'info'
Nginx 配置
# /etc/nginx/sites-available/myproject
upstream myproject {
server 127.0.0.1:8000;
}
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location /static/ {
alias /var/www/myproject/staticfiles/;
expires 30d;
}
location /media/ {
alias /var/www/myproject/media/;
expires 7d;
}
location / {
proxy_pass http://myproject;
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_connect_timeout 30s;
proxy_read_timeout 60s;
}
}
Systemd 服务
# /etc/systemd/system/myproject.service
[Unit]
Description=Gunicorn daemon for MyProject
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/myproject
ExecStart=/var/www/myproject/venv/bin/gunicorn \
--config gunicorn.conf.py \
myproject.wsgi:application
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
# 启用并启动服务
sudo systemctl enable myproject
sudo systemctl start myproject
Docker 部署
Dockerfile
# Dockerfile
FROM python:3.12-slim
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
WORKDIR /app
# 安装系统依赖
RUN apt-get update && apt-get install -y \
postgresql-client \
libpq-dev \
gcc \
&& rm -rf /var/lib/apt/lists/*
# 安装 Python 依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 复制项目文件
COPY . .
# 收集静态文件
RUN python manage.py collectstatic --noinput
# 创建非 root 用户
RUN useradd -m myuser
USER myuser
EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "myproject.wsgi:application"]
docker-compose.yml
version: '3.8'
services:
web:
build: .
command: gunicorn --bind 0.0.0.0:8000 myproject.wsgi:application
volumes:
- static_volume:/app/staticfiles
- media_volume:/app/media
expose:
- 8000
environment:
- DEBUG=False
- DJANGO_SECRET_KEY=${DJANGO_SECRET_KEY}
- DB_NAME=postgres
- DB_USER=postgres
- DB_PASSWORD=postgres
- DB_HOST=db
- DB_PORT=5432
depends_on:
- db
- redis
db:
image: postgres:15
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
- POSTGRES_DB=postgres
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
redis:
image: redis:7-alpine
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- static_volume:/app/staticfiles
- media_volume:/app/media
depends_on:
- web
volumes:
postgres_data:
static_volume:
media_volume:
启动服务
# 构建并启动
docker-compose up -d --build
# 查看日志
docker-compose logs -f
# 运行迁移
docker-compose exec web python manage.py migrate
# 创建超级用户
docker-compose exec web python manage.py createsuperuser
静态文件处理
使用 Whitenoise
# settings.py
MIDDLEWARE = [
# ...
'whitenoise.middleware.WhiteNoiseMiddleware',
# ...
]
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
收集静态文件
python manage.py collectstatic --noinput
数据库配置
PostgreSQL
# settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.environ.get('DB_NAME'),
'USER': os.environ.get('DB_USER'),
'PASSWORD': os.environ.get('DB_PASSWORD'),
'HOST': os.environ.get('DB_HOST'),
'PORT': os.environ.get('DB_PORT', '5432'),
'OPTIONS': {
'connect_timeout': 10,
},
}
}
连接池
# settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
# ...
'CONN_MAX_AGE': 600, # 连接池
}
}
缓存配置
Redis 缓存
# settings.py
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/1',
}
}
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
SESSION_CACHE_ALIAS = 'default'
日志配置
# settings.py
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '{levelname} {asctime} {module} {message}',
'style': '{',
},
},
'handlers': {
'file': {
'level': 'ERROR',
'class': 'logging.FileHandler',
'filename': '/var/log/django/error.log',
'formatter': 'verbose',
},
'console': {
'class': 'logging.StreamHandler',
'formatter': 'verbose',
},
},
'loggers': {
'django': {
'handlers': ['file', 'console'],
'level': 'ERROR',
'propagate': True,
},
},
}
监控和错误追踪
Sentry 错误追踪
pip install sentry-sdk
# settings.py
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration
if not DEBUG:
sentry_sdk.init(
dsn=os.environ.get('SENTRY_DSN'),
integrations=[DjangoIntegration()],
traces_sample_rate=1.0,
)
部署检查清单
# 运行部署检查
python manage.py check --deploy
检查项目:
DEBUG = FalseALLOWED_HOSTS已设置SECRET_KEY安全且保密- 数据库连接安全
- 静态文件正确配置
- HTTPS 已启用
- 安全中间件已配置
- 日志已配置
- 错误追踪已配置
- 备份策略已制定
常用部署命令
# 拉取最新代码
git pull origin main
# 安装依赖
pip install -r requirements.txt
# 运行迁移
python manage.py migrate
# 收集静态文件
python manage.py collectstatic --noinput
# 清除缓存
python manage.py clearcache
# 重启服务
sudo systemctl restart myproject
sudo systemctl restart nginx