Python Django生产环境部署:Gunicorn + Nginx + PostgreSQL香港VPS完整配置

Django是Python最成熟的全栈Web框架,广泛用于SaaS平台、内容管理系统和API后端。本文给出香港VPS上Django生产环境的完整部署方案,包括进程管理、数据库优化和异步任务处理。

一、环境准备

<code"># 安装Python 3.11+和系统依赖
apt update
apt install python3.11 python3.11-venv python3.11-dev \
    postgresql postgresql-contrib libpq-dev \
    nginx redis-server -y

# 创建项目专用用户(安全隔离)
useradd -m -s /bin/bash django
su - django

# 创建虚拟环境
python3.11 -m venv /srv/myapp/venv
source /srv/myapp/venv/bin/activate

# 安装Django和生产依赖
pip install django gunicorn psycopg2-binary \
    django-redis celery redis whitenoise \
    django-storages boto3 sentry-sdk

二、Django生产环境配置

<code"># myapp/settings/production.py

import os
import sentry_sdk
from .base import *

DEBUG = False
ALLOWED_HOSTS = ['yourdomain.com', 'www.yourdomain.com']

SECRET_KEY = os.environ['DJANGO_SECRET_KEY']

# ============ 数据库(PostgreSQL)============
DATABASES = {
    'default': {
        'ENGINE':   'django.db.backends.postgresql',
        'NAME':     os.environ['DB_NAME'],
        'USER':     os.environ['DB_USER'],
        'PASSWORD': os.environ['DB_PASSWORD'],
        'HOST':     os.environ.get('DB_HOST', 'localhost'),
        'PORT':     os.environ.get('DB_PORT', '5432'),
        'CONN_MAX_AGE': 60,        # 连接池复用60秒
        'OPTIONS': {
            'sslmode': 'prefer',
        },
    }
}

# ============ 缓存(Redis)============
CACHES = {
    'default': {
        'BACKEND': 'django_redis.cache.RedisCache',
        'LOCATION': f"redis://:{os.environ['REDIS_PASSWORD']}@127.0.0.1:6379/1",
        'OPTIONS': {
            'CLIENT_CLASS': 'django_redis.client.DefaultClient',
            'SOCKET_CONNECT_TIMEOUT': 5,
            'SOCKET_TIMEOUT': 5,
        },
        'TIMEOUT': 300,
    }
}
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
SESSION_CACHE_ALIAS = 'default'

# ============ 静态文件 ============
STATIC_URL = '/static/'
STATIC_ROOT = '/srv/myapp/staticfiles'
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'

# ============ 媒体文件(Cloudflare R2)============
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
AWS_ACCESS_KEY_ID     = os.environ['R2_ACCESS_KEY']
AWS_SECRET_ACCESS_KEY = os.environ['R2_SECRET_KEY']
AWS_STORAGE_BUCKET_NAME = os.environ['R2_BUCKET']
AWS_S3_ENDPOINT_URL  = os.environ['R2_ENDPOINT']
MEDIA_URL = f"https://media.yourdomain.com/"

# ============ 安全配置 ============
SECURE_SSL_REDIRECT          = True
SECURE_HSTS_SECONDS          = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD          = True
SECURE_CONTENT_TYPE_NOSNIFF  = True
X_FRAME_OPTIONS              = 'DENY'
CSRF_COOKIE_SECURE           = True
SESSION_COOKIE_SECURE        = True

# ============ 日志 ============
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {
            'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
            'style': '{',
        },
    },
    'handlers': {
        'file': {
            'level': 'WARNING',
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': '/var/log/django/error.log',
            'maxBytes': 50 * 1024 * 1024,
            'backupCount': 5,
            'formatter': 'verbose',
        },
        'console': {
            'class': 'logging.StreamHandler',
            'formatter': 'verbose',
        },
    },
    'root': {
        'handlers': ['file', 'console'],
        'level': 'WARNING',
    },
}

# ============ Celery(异步任务)============
CELERY_BROKER_URL = f"redis://:{os.environ['REDIS_PASSWORD']}@127.0.0.1:6379/0"
CELERY_RESULT_BACKEND = CELERY_BROKER_URL
CELERY_TASK_SERIALIZER = 'json'
CELERY_ACCEPT_CONTENT = ['json']
CELERY_TIMEZONE = 'Asia/Shanghai'

# ============ Sentry错误监控 ============
sentry_sdk.init(
    dsn=os.environ.get('SENTRY_DSN', ''),
    traces_sample_rate=0.1,
    profiles_sample_rate=0.1,
)

三、Gunicorn配置

<code"># /srv/myapp/gunicorn.conf.py

import multiprocessing

# Worker数量:CPU核心数 × 2 + 1
workers = multiprocessing.cpu_count() * 2 + 1

# Worker类型:gevent适合I/O密集型应用
worker_class = 'sync'   # 同步(无额外依赖)
# worker_class = 'gevent'   # 异步(需要pip install gevent)

# 请求超时
timeout = 120
graceful_timeout = 30

# 绑定地址(Unix Socket性能优于TCP)
bind = 'unix:/run/gunicorn/myapp.sock'

# 日志
accesslog = '/var/log/gunicorn/access.log'
errorlog  = '/var/log/gunicorn/error.log'
loglevel  = 'warning'
access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s" %(L)ss'

# 安全
limit_request_line = 4096
limit_request_fields = 100

# 性能
keepalive = 5
max_requests = 1000
max_requests_jitter = 100
<code"># /etc/systemd/system/gunicorn.service

[Unit]
Description=Gunicorn Django Application Server
After=network.target postgresql.service redis.service

[Service]
Type=notify
User=django
Group=www-data
RuntimeDirectory=gunicorn
WorkingDirectory=/srv/myapp
EnvironmentFile=/srv/myapp/.env.production
ExecStart=/srv/myapp/venv/bin/gunicorn \
          --config /srv/myapp/gunicorn.conf.py \
          myapp.wsgi:application
ExecReload=/bin/kill -s HUP $MAINPID
KillMode=mixed
TimeoutStopSec=5
PrivateTmp=true
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target
<code"># 创建运行目录并设置权限
mkdir -p /run/gunicorn /var/log/gunicorn /var/log/django
chown django:www-data /run/gunicorn /var/log/gunicorn /var/log/django

systemctl daemon-reload
systemctl enable gunicorn
systemctl start gunicorn

四、Nginx配置

<code">server {
    listen 443 ssl http2;
    server_name yourdomain.com www.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;

    client_max_body_size 20M;

    # 静态文件由Nginx直接提供(不经过Gunicorn)
    location /static/ {
        alias /srv/myapp/staticfiles/;
        expires 1y;
        add_header Cache-Control "public, immutable";
        access_log off;
    }

    # 动态请求转发到Gunicorn
    location / {
        proxy_pass http://unix:/run/gunicorn/myapp.sock;
        proxy_http_version 1.1;

        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 120s;
        proxy_connect_timeout 10s;

        # Django CSRF
        proxy_set_header X-CSRFToken $http_x_csrftoken;
    }
}

五、PostgreSQL优化

<code"># 查看推荐配置(根据服务器内存自动计算)
# 访问 https://pgtune.leopard.in.ua 填入服务器规格获取推荐值

# /etc/postgresql/15/main/postgresql.conf 关键参数

# 内存(4G服务器参考值)
shared_buffers = 1GB             # 25%物理内存
effective_cache_size = 3GB       # 75%物理内存
work_mem = 16MB                  # 每个查询的内存
maintenance_work_mem = 256MB     # 维护操作内存

# 连接
max_connections = 100

# 写入性能
wal_buffers = 16MB
checkpoint_completion_target = 0.9
default_statistics_target = 100

# 慢查询日志
log_min_duration_statement = 1000   # 记录超过1秒的查询
log_line_prefix = '%t [%p]: [%l-1] user=%u,db=%d,app=%a,client=%h '

六、Celery异步任务

<code"># myapp/tasks.py
from celery import shared_task
from django.core.mail import send_mail
import time

@shared_task(bind=True, max_retries=3, default_retry_delay=60)
def send_order_email(self, order_id: int) -> dict:
    """发送订单确认邮件(异步任务)"""
    try:
        from orders.models import Order
        order = Order.objects.select_related('user').get(pk=order_id)

        send_mail(
            subject=f'订单确认 #{order.order_number}',
            message=f'您的订单已确认,总金额 ¥{order.total}',
            from_email='noreply@yourdomain.com',
            recipient_list=[order.user.email],
        )
        return {'status': 'sent', 'order_id': order_id}

    except Exception as exc:
        raise self.retry(exc=exc)

# 视图中调用(非阻塞,立即返回)
# send_order_email.delay(order_id=123)
<code"># /etc/supervisor/conf.d/celery.conf
[program:celery-worker]
command=/srv/myapp/venv/bin/celery -A myapp worker -l WARNING -c 4
directory=/srv/myapp
user=django
environment=DJANGO_SETTINGS_MODULE="myapp.settings.production"
autostart=true
autorestart=true
stderr_logfile=/var/log/celery/worker.err.log
stdout_logfile=/var/log/celery/worker.out.log

[program:celery-beat]
command=/srv/myapp/venv/bin/celery -A myapp beat -l WARNING --scheduler django_celery_beat.schedulers:DatabaseScheduler
directory=/srv/myapp
user=django
autostart=true
autorestart=true
stderr_logfile=/var/log/celery/beat.err.log

七、部署流程脚本

<code">#!/bin/bash
# /usr/local/bin/django-deploy.sh

set -e
APP_DIR="/srv/myapp"
VENV="$APP_DIR/venv"

echo "🚀 开始部署Django应用..."

cd "$APP_DIR"

# 拉取最新代码
git pull origin main

# 激活虚拟环境,安装/更新依赖
source "$VENV/bin/activate"
pip install -r requirements/production.txt --quiet

# 数据库迁移
python manage.py migrate --settings=myapp.settings.production --no-input

# 收集静态文件
python manage.py collectstatic --settings=myapp.settings.production --no-input --clear

# 零停机重载Gunicorn(发送HUP信号)
systemctl reload gunicorn

# 重启Celery Worker(让其加载新代码)
supervisorctl restart celery-worker

echo "✅ 部署完成 $(date)"

八、总结

Django生产部署的标准栈:Gunicorn(WSGI进程管理)+ Nginx(静态文件+反向代理)+ PostgreSQL(主数据库)+ Redis(缓存+Session+Celery Broker)。这套组合在香港VPS上经过大量生产验证,4核8G配置可支撑每秒数百个Django请求。IDC.Net的香港VPS预装Ubuntu 22.04,Python 3.11和PostgreSQL均可快速安装,是部署Django应用的理想平台。

THE END