03-4. 生產環境部署
Nginx + Gunicorn 完整部署指南與最佳實踐
目錄
🎯 本章重點
學習如何將 Django + Gunicorn 應用部署到生產環境,包含完整的配置、監控和維護。
🏗️ 生產環境架構
標準架構圖
Internet
↓
┌─────────────────────────────────────────────┐
│ Cloudflare / CDN │
│ • DDoS 防護 │
│ • SSL/TLS │
│ • 靜態資源緩存 │
└─────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────┐
│ Nginx (反向代理 + 負載均衡) │
│ • 端口:80 (HTTP) / 443 (HTTPS) │
│ • 功能: │
│ - 反向代理到 Gunicorn │
│ - 靜態文件服務 │
│ - SSL 終止 │
│ - Gzip 壓縮 │
│ - 請求限流 │
│ - 健康檢查 │
└─────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────┐
│ Gunicorn (WSGI/ASGI Server) │
│ • 端口:8000 (內部) │
│ • 配置: │
│ - workers = CPU 核心數 │
│ - worker_class = "uvicorn.workers.UvicornWorker" │
│ • 管理:Systemd │
└─────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────┐
│ Django Application │
│ • Python 虛擬環境 │
│ • 配置文件 │
│ • 靜態文件收集 │
└─────────────────────────────────────────────┘
↓
┌──────────────┬──────────────┬──────────────┐
│ PostgreSQL │ Redis │ Celery │
│ (資料庫) │ (緩存/隊列) │ (異步任務) │
└──────────────┴──────────────┴──────────────┘📦 環境準備
系統要求
# 推薦環境
OS: Ubuntu 22.04 LTS / 24.04 LTS
CPU: 2 核以上
RAM: 4GB 以上
Disk: 20GB 以上(根據數據量調整)安裝基礎軟體
# 更新系統
sudo apt update
sudo apt upgrade -y
# 安裝 Python 3.11 和相關工具
sudo apt install -y python3.11 python3.11-venv python3.11-dev
sudo apt install -y python3-pip build-essential
# 安裝 Nginx
sudo apt install -y nginx
# 安裝 PostgreSQL
sudo apt install -y postgresql postgresql-contrib
# 安裝 Redis
sudo apt install -y redis-server
# 安裝其他工具
sudo apt install -y git curl wget htop supervisor🔧 應用部署
1. 創建專用用戶
# 創建專用用戶(安全性)
sudo adduser --system --group --home /opt/myapp myapp
# 設定目錄權限
sudo chown -R myapp:myapp /opt/myapp2. 部署應用代碼
# 切換到應用用戶
sudo su - myapp
# 克隆代碼
cd /opt/myapp
git clone https://github.com/yourcompany/myapp.git app
cd app
# 創建虛擬環境
python3.11 -m venv venv
# 啟動虛擬環境
source venv/bin/activate
# 安裝依賴
pip install --upgrade pip
pip install -r requirements.txt
# 安裝 Gunicorn 和 Uvicorn
pip install gunicorn uvicorn[standard]3. Django 配置
# myapp/settings/production.py
import os
from .base import *
# 安全設定
DEBUG = False
SECRET_KEY = os.environ.get('SECRET_KEY')
ALLOWED_HOSTS = [
'example.com',
'www.example.com',
'api.example.com',
]
# 資料庫配置
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': 'localhost',
'PORT': '5432',
'CONN_MAX_AGE': 600, # 連接池
}
}
# Redis 緩存
CACHES = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/1',
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
'CONNECTION_POOL_KWARGS': {
'max_connections': 50,
},
}
}
}
# 靜態文件
STATIC_ROOT = '/opt/myapp/app/staticfiles'
STATIC_URL = '/static/'
MEDIA_ROOT = '/opt/myapp/app/media'
MEDIA_URL = '/media/'
# 安全設定
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
# 日誌配置
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '{levelname} {asctime} {module} {message}',
'style': '{',
},
},
'handlers': {
'file': {
'level': 'WARNING',
'class': 'logging.handlers.RotatingFileHandler',
'filename': '/var/log/myapp/django.log',
'maxBytes': 100 * 1024 * 1024, # 100MB
'backupCount': 10,
'formatter': 'verbose',
},
},
'root': {
'handlers': ['file'],
'level': 'WARNING',
},
}4. 初始化應用
# 設定環境變數
cat > /opt/myapp/.env << EOF
SECRET_KEY=your-secret-key-here
DB_NAME=myapp_db
DB_USER=myapp_user
DB_PASSWORD=your-db-password
DJANGO_SETTINGS_MODULE=myapp.settings.production
EOF
# 創建資料庫
sudo -u postgres psql << EOF
CREATE DATABASE myapp_db;
CREATE USER myapp_user WITH PASSWORD 'your-db-password';
ALTER ROLE myapp_user SET client_encoding TO 'utf8';
ALTER ROLE myapp_user SET default_transaction_isolation TO 'read committed';
ALTER ROLE myapp_user SET timezone TO 'Asia/Taipei';
GRANT ALL PRIVILEGES ON DATABASE myapp_db TO myapp_user;
EOF
# 執行遷移
source venv/bin/activate
export $(cat /opt/myapp/.env | xargs)
python manage.py migrate
# 收集靜態文件
python manage.py collectstatic --noinput
# 創建超級用戶
python manage.py createsuperuser5. Gunicorn 配置
# /opt/myapp/app/gunicorn.conf.py
import multiprocessing
import os
# 基本配置
bind = "127.0.0.1:8000" # 只監聽本地(Nginx 反向代理)
workers = multiprocessing.cpu_count()
worker_class = "uvicorn.workers.UvicornWorker"
# 超時配置
timeout = 120
graceful_timeout = 30
keepalive = 20
# 防止記憶體洩漏
max_requests = 2000
max_requests_jitter = 200
# 性能優化
preload_app = True
worker_tmp_dir = "/dev/shm"
# 日誌配置
accesslog = "/var/log/myapp/gunicorn-access.log"
errorlog = "/var/log/myapp/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" %(D)s'
# Hooks
def post_fork(server, worker):
from django import db
from django.core.cache import cache
db.connections.close_all()
try:
cache.get('health_check')
except Exception as e:
server.log.error(f"Redis connection failed: {e}")
server.log.info(f"Worker {worker.pid} spawned")
def worker_exit(server, worker):
from django import db
db.connections.close_all()
server.log.info(f"Worker {worker.pid} exited")
def on_starting(server):
server.log.info("Gunicorn server starting")
def on_reload(server):
server.log.info("Gunicorn server reloading")6. 創建日誌目錄
# 創建日誌目錄
sudo mkdir -p /var/log/myapp
# 設定權限
sudo chown -R myapp:myapp /var/log/myapp🔄 Systemd 服務配置
Gunicorn Service
# /etc/systemd/system/myapp-gunicorn.service
[Unit]
Description=Gunicorn daemon for Django myapp
After=network.target
[Service]
Type=notify
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp/app
EnvironmentFile=/opt/myapp/.env
# 虛擬環境路徑
Environment="PATH=/opt/myapp/app/venv/bin"
# Gunicorn 啟動命令
ExecStart=/opt/myapp/app/venv/bin/gunicorn \
-c /opt/myapp/app/gunicorn.conf.py \
myapp.asgi:application
# 重載設定
ExecReload=/bin/kill -s HUP $MAINPID
# 優雅停止
KillMode=mixed
TimeoutStopSec=60
# 重啟策略
Restart=always
RestartSec=5
# 安全加固
PrivateTmp=true
NoNewPrivileges=true
[Install]
WantedBy=multi-user.targetCelery Worker Service(可選)
# /etc/systemd/system/myapp-celery.service
[Unit]
Description=Celery worker for Django myapp
After=network.target redis.service
[Service]
Type=forking
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp/app
EnvironmentFile=/opt/myapp/.env
ExecStart=/opt/myapp/app/venv/bin/celery -A myapp worker \
--pool=prefork \
--concurrency=4 \
--loglevel=info \
--logfile=/var/log/myapp/celery-worker.log \
--pidfile=/var/run/celery/worker.pid
ExecStop=/bin/kill -s TERM $MAINPID
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target啟動服務
# 重新載入 systemd
sudo systemctl daemon-reload
# 啟動 Gunicorn
sudo systemctl start myapp-gunicorn
sudo systemctl enable myapp-gunicorn
# 啟動 Celery(可選)
sudo systemctl start myapp-celery
sudo systemctl enable myapp-celery
# 查看狀態
sudo systemctl status myapp-gunicorn
sudo systemctl status myapp-celery
# 查看日誌
sudo journalctl -u myapp-gunicorn -f
sudo journalctl -u myapp-celery -f🌐 Nginx 配置
完整配置範例
# /etc/nginx/sites-available/myapp.conf
# 上游 Gunicorn 服務器
upstream gunicorn_backend {
# Gunicorn 地址
server 127.0.0.1:8000 fail_timeout=0;
# 如果有多個 Gunicorn 實例(負載均衡)
# server 127.0.0.1:8001 fail_timeout=0;
# server 127.0.0.1:8002 fail_timeout=0;
}
# HTTP → HTTPS 重定向
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
# Let's Encrypt 證書驗證
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
# 重定向到 HTTPS
location / {
return 301 https://$server_name$request_uri;
}
}
# HTTPS 主服務器
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com www.example.com;
# SSL 證書
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# SSL 配置(Mozilla Modern)
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;
ssl_session_timeout 10m;
ssl_stapling on;
ssl_stapling_verify on;
# 安全頭
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# 上傳大小限制
client_max_body_size 100M;
client_body_buffer_size 1m;
# 靜態文件
location /static/ {
alias /opt/myapp/app/staticfiles/;
expires 1y;
add_header Cache-Control "public, immutable";
# Gzip 壓縮
gzip on;
gzip_types text/css application/javascript image/svg+xml;
gzip_vary on;
}
# 媒體文件
location /media/ {
alias /opt/myapp/app/media/;
expires 7d;
add_header Cache-Control "public";
}
# 健康檢查
location /health/ {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
# 反向代理到 Gunicorn
location / {
# 代理設定
proxy_pass http://gunicorn_backend;
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;
# WebSocket 支援(如果需要)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# 超時設定
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# 緩衝設定
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
}
# 請求限流(防止暴力攻擊)
location /api/login/ {
limit_req zone=login_limit burst=5 nodelay;
proxy_pass http://gunicorn_backend;
# ... 其他 proxy 設定
}
# 日誌
access_log /var/log/nginx/myapp-access.log combined;
error_log /var/log/nginx/myapp-error.log warn;
}
# 請求限流配置
limit_req_zone $binary_remote_addr zone=login_limit:10m rate=5r/m;啟用配置
# 創建軟連接
sudo ln -s /etc/nginx/sites-available/myapp.conf /etc/nginx/sites-enabled/
# 測試配置
sudo nginx -t
# 重載 Nginx
sudo systemctl reload nginx
# 查看狀態
sudo systemctl status nginx🔒 SSL 證書配置
使用 Let’s Encrypt(免費)
# 安裝 Certbot
sudo apt install -y certbot python3-certbot-nginx
# 申請證書
sudo certbot --nginx -d example.com -d www.example.com
# 測試自動更新
sudo certbot renew --dry-run
# 設定自動更新(cron)
sudo crontab -e
# 添加以下行(每天凌晨 2 點檢查)
0 2 * * * certbot renew --quiet --post-hook "systemctl reload nginx"📊 監控和日誌
1. 日誌輪轉配置
# /etc/logrotate.d/myapp
/var/log/myapp/*.log {
daily
rotate 30
compress
delaycompress
notifempty
create 0640 myapp myapp
sharedscripts
postrotate
systemctl reload myapp-gunicorn > /dev/null 2>&1 || true
endscript
}
# 測試
sudo logrotate -f /etc/logrotate.d/myapp2. 性能監控
# 安裝 Prometheus Node Exporter
wget https://github.com/prometheus/node_exporter/releases/download/v1.6.1/node_exporter-1.6.1.linux-amd64.tar.gz
tar xvfz node_exporter-*.tar.gz
sudo mv node_exporter-*/node_exporter /usr/local/bin/
sudo useradd -rs /bin/false node_exporter
# 創建 systemd 服務
sudo tee /etc/systemd/system/node_exporter.service << EOF
[Unit]
Description=Node Exporter
After=network.target
[Service]
User=node_exporter
Group=node_exporter
Type=simple
ExecStart=/usr/local/bin/node_exporter
[Install]
WantedBy=multi-user.target
EOF
# 啟動服務
sudo systemctl daemon-reload
sudo systemctl start node_exporter
sudo systemctl enable node_exporter3. 應用監控腳本
# /opt/myapp/scripts/monitor.sh
#!/bin/bash
# 檢查 Gunicorn 狀態
if ! systemctl is-active --quiet myapp-gunicorn; then
echo "Gunicorn is down!" | mail -s "Alert: Gunicorn Down" admin@example.com
systemctl restart myapp-gunicorn
fi
# 檢查 Nginx 狀態
if ! systemctl is-active --quiet nginx; then
echo "Nginx is down!" | mail -s "Alert: Nginx Down" admin@example.com
systemctl restart nginx
fi
# 檢查磁碟空間
DISK_USAGE=$(df -h / | awk 'NR==2 {print $5}' | sed 's/%//')
if [ $DISK_USAGE -gt 80 ]; then
echo "Disk usage is ${DISK_USAGE}%" | mail -s "Alert: High Disk Usage" admin@example.com
fi
# 檢查記憶體
MEM_USAGE=$(free | awk 'NR==2 {printf "%.0f", $3/$2 * 100}')
if [ $MEM_USAGE -gt 90 ]; then
echo "Memory usage is ${MEM_USAGE}%" | mail -s "Alert: High Memory Usage" admin@example.com
fi# 設定 cron(每 5 分鐘檢查一次)
*/5 * * * * /opt/myapp/scripts/monitor.sh🚀 零停機部署
部署腳本
# /opt/myapp/scripts/deploy.sh
#!/bin/bash
set -e # 遇到錯誤立即退出
echo "🚀 Starting deployment..."
# 切換到應用目錄
cd /opt/myapp/app
# 拉取最新代碼
echo "📥 Pulling latest code..."
git fetch origin
git reset --hard origin/main
# 啟動虛擬環境
source venv/bin/activate
# 安裝/更新依賴
echo "📦 Installing dependencies..."
pip install -r requirements.txt --quiet
# 執行遷移
echo "🗄️ Running migrations..."
python manage.py migrate --noinput
# 收集靜態文件
echo "📁 Collecting static files..."
python manage.py collectstatic --noinput
# 零停機重載 Gunicorn
echo "🔄 Reloading Gunicorn (zero-downtime)..."
sudo systemctl reload myapp-gunicorn
# 等待重載完成
sleep 2
# 檢查服務狀態
if systemctl is-active --quiet myapp-gunicorn; then
echo "✅ Deployment successful!"
else
echo "❌ Deployment failed! Rolling back..."
git reset --hard HEAD@{1}
sudo systemctl restart myapp-gunicorn
exit 1
fi
# 重啟 Celery(如果有)
if systemctl is-active --quiet myapp-celery; then
echo "🔄 Restarting Celery workers..."
sudo systemctl restart myapp-celery
fi
echo "🎉 Deployment completed!"# 設定執行權限
chmod +x /opt/myapp/scripts/deploy.sh
# 執行部署
sudo -u myapp /opt/myapp/scripts/deploy.shGunicorn 零停機重載原理
# HUP 信號觸發零停機重載
sudo systemctl reload myapp-gunicorn
# 等同於
sudo kill -HUP $(cat /var/run/gunicorn.pid)
# 重載流程:
# 1. Master 進程接收 HUP 信號
# 2. Master 創建新的 worker 進程
# 3. 新 workers 開始接收請求
# 4. 舊 workers 處理完當前請求後退出
# 5. 所有舊 workers 退出,重載完成
# 用戶完全無感知!✅🛡️ 安全加固
1. 防火牆配置
# 使用 ufw
sudo apt install -y ufw
# 允許 SSH
sudo ufw allow 22/tcp
# 允許 HTTP/HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# 啟用防火牆
sudo ufw enable
# 查看狀態
sudo ufw status2. Fail2ban(防止暴力攻擊)
# 安裝 Fail2ban
sudo apt install -y fail2ban
# 配置
sudo tee /etc/fail2ban/jail.local << EOF
[nginx-limit-req]
enabled = true
filter = nginx-limit-req
action = iptables-multiport[name=ReqLimit, port="http,https", protocol=tcp]
logpath = /var/log/nginx/myapp-error.log
findtime = 600
bantime = 7200
maxretry = 10
EOF
# 重啟 Fail2ban
sudo systemctl restart fail2ban
# 查看狀態
sudo fail2ban-client status3. 最小權限原則
# Django 應用目錄權限
sudo chown -R myapp:myapp /opt/myapp/app
sudo chmod -R 750 /opt/myapp/app
# 靜態文件目錄(Nginx 需要讀取)
sudo chown -R myapp:www-data /opt/myapp/app/staticfiles
sudo chmod -R 755 /opt/myapp/app/staticfiles
# 日誌目錄
sudo chown -R myapp:myapp /var/log/myapp
sudo chmod -R 750 /var/log/myapp
# 環境變數文件(敏感)
sudo chmod 600 /opt/myapp/.env🔧 故障排查
常見問題
問題 1:Gunicorn 無法啟動
# 查看詳細錯誤
sudo journalctl -u myapp-gunicorn -n 50 --no-pager
# 常見原因:
# 1. 端口被佔用
sudo lsof -i :8000
# 2. 權限問題
ls -la /opt/myapp/app
# 3. Python 路徑問題
which python
/opt/myapp/app/venv/bin/python --version
# 4. 依賴缺失
/opt/myapp/app/venv/bin/pip list
# 手動測試啟動
sudo -u myapp /opt/myapp/app/venv/bin/gunicorn \
-c /opt/myapp/app/gunicorn.conf.py \
myapp.asgi:application問題 2:502 Bad Gateway
# 原因:Nginx 無法連接到 Gunicorn
# 檢查 Gunicorn 是否運行
sudo systemctl status myapp-gunicorn
# 檢查端口監聽
sudo netstat -tlnp | grep 8000
# 檢查 Nginx 配置
sudo nginx -t
# 查看 Nginx 錯誤日誌
sudo tail -f /var/log/nginx/myapp-error.log
# 測試直接訪問 Gunicorn
curl http://127.0.0.1:8000問題 3:Workers 頻繁重啟
# 查看日誌
sudo journalctl -u myapp-gunicorn -f
# 常見原因:
# 1. 記憶體不足
free -h
sudo dmesg | grep -i 'out of memory'
# 2. Worker 超時
# 檢查 gunicorn.conf.py 中的 timeout 設定
# 3. 代碼錯誤
# 查看應用日誌
tail -f /var/log/myapp/django.log問題 4:靜態文件 404
# 檢查靜態文件路徑
ls -la /opt/myapp/app/staticfiles
# 重新收集靜態文件
sudo -u myapp bash -c "cd /opt/myapp/app && source venv/bin/activate && python manage.py collectstatic --noinput"
# 檢查 Nginx 配置
sudo nginx -t
grep -A 5 "location /static/" /etc/nginx/sites-available/myapp.conf
# 檢查權限
ls -ld /opt/myapp/app/staticfiles📈 性能優化
1. Nginx 緩存
# 在 http 區塊中添加
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=1g inactive=60m use_temp_path=off;
# 在 location / 中添加
proxy_cache my_cache;
proxy_cache_valid 200 10m;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_background_update on;
proxy_cache_lock on;
add_header X-Cache-Status $upstream_cache_status;2. 資料庫連接池
# 使用 django-db-connection-pool
pip install django-db-connection-pool
# settings.py
DATABASES = {
'default': {
'ENGINE': 'dj_db_conn_pool.backends.postgresql',
'NAME': 'myapp_db',
'POOL_OPTIONS': {
'POOL_SIZE': 10,
'MAX_OVERFLOW': 5,
}
}
}3. 啟用 HTTP/2
# 在 server 區塊中
listen 443 ssl http2;
listen [::]:443 ssl http2;💡 面試常見問題
Q1:生產環境部署的完整流程是什麼?
完整答案:
部署流程分為準備、部署、驗證三個階段:
1. 環境準備
- 安裝系統依賴(Python、Nginx、PostgreSQL、Redis)
- 創建專用用戶和目錄
- 配置虛擬環境
2. 應用部署
# 拉取代碼 git clone repo # 安裝依賴 pip install -r requirements.txt # 配置環境變數 # 執行遷移 python manage.py migrate # 收集靜態文件 python manage.py collectstatic3. 服務配置
- 配置 Gunicorn(gunicorn.conf.py)
- 配置 Systemd 服務(自動啟動、重啟)
- 配置 Nginx(反向代理、靜態文件)
- 配置 SSL 證書
4. 安全加固
- 防火牆(UFW)
- Fail2ban(防暴力攻擊)
- 最小權限原則
- SSL/TLS 配置
5. 監控和日誌
- 日誌輪轉(Logrotate)
- 監控腳本(健康檢查)
- 性能監控(Prometheus)
6. 驗證
# 檢查服務狀態 systemctl status myapp-gunicorn # 測試訪問 curl https://example.com # 查看日誌 journalctl -u myapp-gunicorn -f
Q2:如何實現零停機部署?
完整答案:
零停機部署的核心:利用 Gunicorn 的 graceful reload
原理:
# 發送 HUP 信號 sudo systemctl reload myapp-gunicorn # 等同於 sudo kill -HUP $(cat /var/run/gunicorn.pid) # 重載流程: 1. Master 進程接收 HUP 信號 2. 創建新的 worker 進程 3. 新 workers 載入最新代碼 4. 新 workers 開始接收請求 5. 舊 workers 處理完當前請求後退出 6. 所有舊 workers 退出,完成重載 # 用戶完全無感知!完整部署腳本:
#!/bin/bash # 1. 拉取最新代碼 git pull origin main # 2. 安裝依賴 pip install -r requirements.txt # 3. 執行遷移 python manage.py migrate # 4. 收集靜態文件 python manage.py collectstatic --noinput # 5. 零停機重載 sudo systemctl reload myapp-gunicorn # 6. 驗證部署 if systemctl is-active --quiet myapp-gunicorn; then echo "Deployment successful" else echo "Deployment failed, rolling back" git reset --hard HEAD@{1} sudo systemctl restart myapp-gunicorn fi注意事項:
- ✅ 適用於代碼更新
- ✅ 適用於配置更新
- ⚠️ 不適用於 Gunicorn 版本升級(需要 restart)
- ⚠️ 資料庫遷移要向後兼容(舊代碼能跑新 schema)
Q3:Nginx 和 Gunicorn 各自的職責是什麼?
完整答案:
Nginx 的職責(前端服務器):
反向代理
- 接收用戶請求,轉發到 Gunicorn
- 負載均衡(如果有多個 Gunicorn 實例)
靜態文件服務
- 直接提供 /static/、/media/ 文件
- Nginx 處理靜態文件比 Python 快 10-100 倍
SSL/TLS 終止
- 處理 HTTPS 加密/解密
- 減輕 Gunicorn 負擔
安全和限流
- 請求限流(Rate Limiting)
- 防止 DDoS
- IP 白名單/黑名單
緩存
- 緩存動態內容
- 減少 Gunicorn 負載
壓縮
- Gzip 壓縮響應
- 節省帶寬
Gunicorn 的職責(應用服務器):
運行 Django 應用
- 執行 Python 代碼
- 處理業務邏輯
管理 Workers
- 創建和管理多個 worker 進程
- 自動重啟崩潰的 workers
處理動態請求
- 執行視圖函數
- 查詢資料庫
- 生成 HTML/JSON
維護連接池
- 資料庫連接池
- Redis 連接池
為什麼需要兩者?
❌ 錯誤架構:直接暴露 Gunicorn Internet → Gunicorn (8000) → Django 問題: - 沒有 SSL - 靜態文件性能差 - 無法限流 - 安全性低 ✅ 正確架構:Nginx + Gunicorn Internet → Nginx (443) → Gunicorn (127.0.0.1:8000) → Django 優點: - SSL 終止 - 靜態文件高效 - 請求限流 - 負載均衡 - 緩存加速類比:
- Nginx = 前台接待(處理常見問題、引導客人)
- Gunicorn = 業務專員(處理複雜業務邏輯)
Q4:如何監控和排查生產環境問題?
完整答案:
監控層次:
1. 系統層監控
# CPU、記憶體、磁碟 htop free -h df -h # 網絡連接 netstat -tlnp # 進程狀態 ps aux | grep gunicorn2. 服務層監控
# Systemd 服務狀態 systemctl status myapp-gunicorn systemctl status nginx # 日誌 journalctl -u myapp-gunicorn -f tail -f /var/log/nginx/myapp-error.log3. 應用層監控
# Django 日誌 tail -f /var/log/myapp/django.log # Gunicorn 日誌 tail -f /var/log/myapp/gunicorn-access.log tail -f /var/log/myapp/gunicorn-error.log4. 常見問題排查
問題:502 Bad Gateway
# 原因:Nginx 無法連接到 Gunicorn # 檢查 Gunicorn 是否運行 systemctl status myapp-gunicorn # 檢查端口監聽 netstat -tlnp | grep 8000 # 測試直接訪問 curl http://127.0.0.1:8000問題:Workers 頻繁重啟
# 查看日誌找原因 journalctl -u myapp-gunicorn -f # 常見原因: 1. 記憶體不足 → free -h 2. Worker 超時 → 調整 timeout 3. 代碼錯誤 → 查看應用日誌問題:響應慢
# 查看 access log tail -f /var/log/myapp/gunicorn-access.log # 看 %(D)s(響應時間) # 分析慢查詢 # 啟用 Django Debug Toolbar(測試環境) # 或使用 django-silk # 檢查資料庫 # PostgreSQL 慢查詢日誌5. 自動化監控
# 監控腳本(Cron 每 5 分鐘) */5 * * * * /opt/myapp/scripts/monitor.sh # 檢查: - 服務是否運行 - 磁碟空間 - 記憶體使用 - CPU 使用 - 響應時間 # 告警: - 郵件通知 - Slack 通知 - 自動重啟(如果可能)
✅ 重點回顧
部署架構
Internet → Nginx (443) → Gunicorn (127.0.0.1:8000) → Django
↓
靜態文件 (/static/, /media/)關鍵配置
# Gunicorn
workers = CPU 核心數
worker_class = "uvicorn.workers.UvicornWorker"
timeout = 120
preload_app = True
# Nginx
upstream gunicorn_backend {
server 127.0.0.1:8000;
}
# Systemd
Type=notify
Restart=always零停機部署
# 核心命令
sudo systemctl reload myapp-gunicorn
# 原理:
# 1. 創建新 workers
# 2. 新 workers 接收請求
# 3. 舊 workers 優雅退出安全加固
1. 防火牆(UFW)
2. Fail2ban(防暴力攻擊)
3. SSL/TLS(Let's Encrypt)
4. 最小權限原則📚 接下來
恭喜完成 03. Gunicorn 實戰篇!
- 常見問題排查
- 記憶體洩漏
- 連接數不足
- CPU 使用率過高
最後更新:2025-10-31