Gunicorn 与 Nginx:生产环境 Web 服务器配置

📂 所属阶段:第六阶段 — 上线部署(生产篇)
🔗 相关章节:Docker 部署全流程 · 环境变量与安全配置


1. 为什么需要 Gunicorn?

Flask 内置服务器:
✅ 开发方便
❌ 单线程、单进程
❌ 不能处理并发
❌ 不能在生产环境使用

Gunicorn:
✅ 多 Worker(多进程)
✅ 预处理请求
✅ 稳定可靠
✅ 工业级生产服务器

2. 安装与使用

pip install gunicorn

2.1 基本启动

# 运行(app = Flask 实例名, run = 应用入口文件名)
gunicorn -w 4 -b 0.0.0.0:8000 "app:create_app()"

# 或者指定 WSGI 文件
gunicorn -w 4 -b 0.0.0.0:8000 app:app

2.2 配置文件

# gunicorn.conf.py
import multiprocessing

bind = "127.0.0.1:8000"
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = "sync"  # 或 "gevent"、"uvicorn.gunicorn.workers.UvicornWorker"
keepalive = 65
timeout = 30
graceful_timeout = 30
max_requests = 1000
max_requests_jitter = 50

# 日志
accesslog = "-"
errorlog = "-"
loglevel = "info"
access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s"'

# 钩子
def on_starting(server):
    print("Gunicorn 启动中...")

def on_exit(server):
    print("Gunicorn 关闭中...")

2.3 启动命令

gunicorn -c gunicorn.conf.py "app:create_app()"

# 或
gunicorn \
    -w 4 \
    -b 127.0.0.1:8000 \
    --timeout 30 \
    --access-logfile - \
    --error-logfile - \
    "app:create_app()"

3. Nginx 配置

# /etc/nginx/conf.d/daoman.conf

upstream daoman_app {
    server 127.0.0.1:8000;
    keepalive 32;
}

server {
    listen 80;
    server_name daomanpy.com www.daomanpy.com;

    # HTTP 跳转 HTTPS
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name daomanpy.com www.daomanpy.com;

    # SSL 证书
    ssl_certificate /etc/nginx/ssl/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;
    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;

    # Gzip 压缩
    gzip on;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
    gzip_min_length 1024;

    # 静态文件(由 Nginx 直接服务)
    location /static/ {
        alias /var/www/daoman/static/;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    # 媒体文件
    location /media/ {
        alias /var/www/daoman/media/;
        expires 7d;
    }

    # API 请求代理到 Gunicorn
    location / {
        proxy_pass http://daoman_app;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        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_redirect off;

        proxy_connect_timeout 60s;
        proxy_read_timeout 60s;

        client_max_body_size 20M;
    }
}

4. Systemd 服务管理

# /etc/systemd/system/daoman.service
[Unit]
Description=Daoman Flask Application
After=network.target postgresql.service redis.service

[Service]
Type=notify
User=www-data
Group=www-data
WorkingDirectory=/opt/daoman
Environment="PATH=/opt/daoman/venv/bin"
Environment="FLASK_ENV=production"

ExecStart=/opt/daoman/venv/bin/gunicorn \
    -c /opt/daoman/gunicorn.conf.py "app:create_app()"

ExecReload=/bin/kill -s HUP $MAINPID
TimeoutStopSec=5
PrivateTmp=true
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
# 管理命令
sudo systemctl daemon-reload
sudo systemctl enable --now daoman
sudo systemctl restart daoman
sudo systemctl status daoman
sudo journalctl -u daoman -f  # 查看日志

5. 小结

生产部署架构:

用户 → Nginx (SSL, 静态文件, 压缩) → Gunicorn (Python Worker)

                              PostgreSQL / Redis

💡 最佳实践:Gunicorn worker 数量 = CPU核心数 × 2 + 1。Nginx 必须配置 SSL。静态文件由 Nginx 直接服务,减轻 Python 应用压力。


🔗 扩展阅读