02-4. 配置文件範例
Gunicorn 實戰配置範例與最佳實踐
目錄
🎯 本章重點
通過實際的配置文件範例,學習如何為不同場景配置 Gunicorn。
⭐ 2025 年新專案建議
結論:Django 3.0+ 新專案,請直接使用 ASGI (範例3)
為什麼?
- ✅ 性能更好(比 WSGI 快 10-35 倍)
- ✅ 向後兼容同步代碼(不需要改現有代碼)
- ✅ 支援 WebSocket、SSE 等現代功能
- ✅ Django 官方推薦的未來方向
- ✅ 一個 worker 可處理數千並發
什麼時候才用 WSGI (範例2)?
- ⚠️ 只有 Django 2.x 遺留系統無法升級時
- ⚠️ 極少數第三方套件不支援 ASGI(非常罕見)
📊 Workers 數量計算說明
在配置文件中你會看到不同的 workers 計算方式,這裡先統一說明:
multiprocessing.cpu_count() 是什麼?
import multiprocessing
# 自動取得 CPU 核心數
workers = multiprocessing.cpu_count()
# 例如:
# - 4 核 CPU → workers = 4
# - 8 核 CPU → workers = 8
# - 16 核 CPU → workers = 16不同 Worker 類型的數量公式
| Worker 類型 | Workers 數量公式 | 原因 |
|---|---|---|
| Sync (WSGI) | CPU * 2 + 1 | 需要處理 I/O 等待,需要更多 workers |
| Uvicorn (ASGI) | CPU | 異步處理,一個 worker 可處理數千並發 |
| Gevent | CPU * 2 + 1 | 協程模型,需要多個 workers 分散負載 |
| Gthread | CPU * 2 + 1 | 多線程,需要更多進程配合 |
範例對比
# 假設 4 核 CPU
# 範例 2:Sync Worker (WSGI)
workers = multiprocessing.cpu_count() * 2 + 1 # = 9
# 範例 3:Uvicorn Worker (ASGI) ⭐ 推薦
workers = multiprocessing.cpu_count() # = 4
# 範例 4:大型應用 (ASGI)
workers = multiprocessing.cpu_count() * 2 # = 8為什麼 ASGI 只需要 CPU 核心數?
- ASGI (異步) 一個 worker 可以同時處理數千個請求
- WSGI (同步) 一個 worker 同時只能處理一個請求
- 所以 ASGI 需要的 workers 更少
詳細講解請參考: 02-1. Workers 數量計算
📂 配置文件的兩種方式
方式一:Python 配置文件 (推薦)
# 使用 Python 配置文件
gunicorn -c gunicorn.conf.py myapp.wsgi:application優點:
- 可以使用 Python 語法進行動態配置
- 可以使用條件判斷、函數等
- 支持 Hook Functions
- 配置更靈活
方式二:命令行參數
# 直接使用命令行參數
gunicorn myapp.wsgi:application \
--workers 4 \
--worker-class uvicorn.workers.UvicornWorker \
--bind 0.0.0.0:8000 \
--timeout 120缺點:
- 配置參數多時不易管理
- 不支持 Hook Functions
- 難以進行條件配置
🏗️ 配置文件範例
範例 1:開發環境配置
適用場景:本地開發、快速測試
# gunicorn.dev.conf.py
import multiprocessing
# 基本配置
bind = "127.0.0.1:8000"
workers = 1 # 開發環境只需要一個 worker
worker_class = "sync" # 使用最簡單的同步 worker
reload = True # 開啟自動重載(代碼變更時自動重啟)
reload_extra_files = [
'templates/',
'static/',
]
# 超時配置
timeout = 300 # 開發時可能需要除錯,設定較長的超時時間
graceful_timeout = 30
keepalive = 5
# 日誌配置
loglevel = "debug" # 開發環境使用 debug 級別
accesslog = "-" # 輸出到 stdout
errorlog = "-" # 輸出到 stderr
access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s" %(D)s'
# 其他配置
preload_app = False # 不預載入,方便除錯使用方式:
gunicorn -c gunicorn.dev.conf.py myapp.wsgi:application特點說明:
- ✅ 單 worker 避免多進程除錯困擾
- ✅ 自動重載提高開發效率
- ✅ Debug 日誌級別方便追蹤問題
- ✅ 較長的超時時間方便除錯
- ⚠️ 不適合生產環境使用
範例 2:遺留系統生產環境 (WSGI - Django 2.x)
適用場景:Django 2.x 遺留系統、不支援 ASGI 的舊專案
⚠️ 注意:Django 3.0+ 新專案請優先使用範例3 (ASGI)
# gunicorn.prod.conf.py
import multiprocessing
import os
# 基本配置
bind = "0.0.0.0:8000"
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = "sync"
worker_connections = 1000
# 超時配置
timeout = 30
graceful_timeout = 30
keepalive = 5
# 防止記憶體洩漏
max_requests = 1000
max_requests_jitter = 100
# 日誌配置
loglevel = "info"
accesslog = "/var/log/gunicorn/access.log"
errorlog = "/var/log/gunicorn/error.log"
access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s" %(D)s'
logconfig_dict = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'generic': {
'format': '%(asctime)s [%(process)d] [%(levelname)s] %(message)s',
'datefmt': '%Y-%m-%d %H:%M:%S',
},
},
'handlers': {
'error_file': {
'class': 'logging.handlers.RotatingFileHandler',
'formatter': 'generic',
'filename': '/var/log/gunicorn/error.log',
'maxBytes': 100 * 1024 * 1024, # 100MB
'backupCount': 10,
},
},
'root': {
'level': 'INFO',
'handlers': ['error_file'],
},
}
# 安全配置
limit_request_line = 4096
limit_request_fields = 100
limit_request_field_size = 8190
# 性能優化
preload_app = True
worker_tmp_dir = "/dev/shm"
# Hooks
def post_fork(server, worker):
"""Worker fork 後關閉所有資料庫連接"""
from django import db
db.connections.close_all()
server.log.info(f"Worker {worker.pid} spawned")
def worker_exit(server, worker):
"""Worker 退出時記錄"""
server.log.info(f"Worker {worker.pid} exited")
def on_starting(server):
"""Server 啟動時的初始化"""
server.log.info("Gunicorn server is starting")
def on_reload(server):
"""Server reload 時的處理"""
server.log.info("Gunicorn server is reloading")使用方式:
# 建立日誌目錄
sudo mkdir -p /var/log/gunicorn
sudo chown -R $USER:$USER /var/log/gunicorn
# 啟動服務
gunicorn -c gunicorn.prod.conf.py myapp.wsgi:application適合場景:
- ⚠️ Django 2.x 遺留系統(無法升級至 3.0+)
- 傳統的同步請求-響應模式
- 不需要 WebSocket 支援
- 流量在 100-1000 QPS 之間
不推薦的情況:
- ❌ Django 3.0+ 新專案(請使用範例3)
- ❌ 需要高並發處理(請使用範例3)
範例 3:現代化生產環境 (ASGI + Async) ⭐ 推薦
適用場景:Django 3.0+ 新專案、高並發、WebSocket、現代應用
✅ 2025 年推薦配置:適用於小型到中型生產環境
# gunicorn.async.conf.py
import multiprocessing
import os
# 基本配置
bind = "0.0.0.0:8000"
workers = multiprocessing.cpu_count()
worker_class = "uvicorn.workers.UvicornWorker" # 使用 Uvicorn Worker
worker_connections = 1000
# 超時配置
timeout = 120 # ASGI 應用建議設定較長的超時時間
graceful_timeout = 30
keepalive = 20 # 較長的 keepalive 適合持久連接
# 防止記憶體洩漏
max_requests = 2000
max_requests_jitter = 200
# 日誌配置
loglevel = "info"
accesslog = "/var/log/gunicorn/access.log"
errorlog = "/var/log/gunicorn/error.log"
access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s" %(D)s'
# 安全配置
limit_request_line = 8190 # WebSocket 可能需要較長的請求行
limit_request_fields = 100
limit_request_field_size = 8190
# 性能優化
preload_app = True
worker_tmp_dir = "/dev/shm"
# Uvicorn 特定配置(通過環境變數)
raw_env = [
'WEB_CONCURRENCY=4',
]
# Hooks
def post_fork(server, worker):
"""Worker fork 後的初始化"""
from django import db
db.connections.close_all()
server.log.info(f"Async Worker {worker.pid} spawned")
def worker_exit(server, worker):
"""Worker 退出時的清理"""
server.log.info(f"Async Worker {worker.pid} exited")
def on_starting(server):
"""Server 啟動時初始化 Redis、Cache 等"""
server.log.info("ASGI Gunicorn server is starting")
# 可以在這裡初始化全域資源
# 例如:預熱 Redis 連接池
def when_ready(server):
"""Server 準備就緒"""
server.log.info("Server is ready. Spawning workers")
def pre_request(worker, req):
"""請求前記錄(開發/除錯用)"""
worker.log.debug(f"{req.method} {req.path}")
def post_request(worker, req, environ, resp):
"""請求後記錄(可用於監控)"""
# 可以在這裡記錄請求時間、狀態碼等
pass使用方式:
# 安裝依賴
pip install uvicorn[standard] gunicorn
# 啟動服務
gunicorn -c gunicorn.async.conf.py myapp.asgi:application適合場景:
- ✅ Django 3.0+ 所有新專案(優先推薦)
- ✅ 小型到中型生產環境(< 5000 QPS)
- I/O 密集型應用(API、微服務)
- 需要 WebSocket 支援
- 需要長連接的應用
優點:
- 高並發處理能力(比 sync worker 快 10-35 倍)
- 向後兼容同步代碼
- 支援異步和 WebSocket
- 記憶體使用更少
範例 4:大型高並發環境 (混合型)
適用場景:超大型應用、複雜業務邏輯、混合 CPU 和 I/O 密集型
# gunicorn.enterprise.conf.py
import multiprocessing
import os
# === 基本配置 ===
bind = "0.0.0.0:8000"
workers = multiprocessing.cpu_count() * 2
worker_class = "uvicorn.workers.UvicornWorker"
worker_connections = 2000 # 增加並發連接數
# === 超時配置 ===
timeout = 180
graceful_timeout = 60
keepalive = 30
# === 防止記憶體洩漏 ===
max_requests = 5000
max_requests_jitter = 500
# === 連接配置 ===
backlog = 2048 # 增加連接隊列
# === 日誌配置 ===
loglevel = "warning" # 生產環境使用 warning 減少日誌量
accesslog = "/var/log/gunicorn/access.log"
errorlog = "/var/log/gunicorn/error.log"
# 結構化日誌格式(便於解析)
access_log_format = json.dumps({
'remote_ip': '%(h)s',
'request_id': '%({X-Request-Id}i)s',
'response_code': '%(s)s',
'request_method': '%(m)s',
'request_path': '%(U)s',
'request_querystring': '%(q)s',
'request_time': '%(D)s',
'response_length': '%(B)s',
})
# 進階日誌配置(整合 ELK)
logconfig_dict = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'json': {
'()': 'pythonjsonlogger.jsonlogger.JsonFormatter',
'format': '%(asctime)s %(name)s %(levelname)s %(message)s',
},
},
'handlers': {
'error_file': {
'class': 'logging.handlers.RotatingFileHandler',
'formatter': 'json',
'filename': '/var/log/gunicorn/error.log',
'maxBytes': 500 * 1024 * 1024, # 500MB
'backupCount': 30,
},
'slow_request_file': {
'class': 'logging.handlers.RotatingFileHandler',
'formatter': 'json',
'filename': '/var/log/gunicorn/slow.log',
'maxBytes': 200 * 1024 * 1024, # 200MB
'backupCount': 10,
},
},
'root': {
'level': 'WARNING',
'handlers': ['error_file'],
},
'loggers': {
'slow': {
'level': 'INFO',
'handlers': ['slow_request_file'],
'propagate': False,
},
},
}
# === 安全配置 ===
limit_request_line = 8190
limit_request_fields = 150
limit_request_field_size = 8190
# === 性能優化 ===
preload_app = True
worker_tmp_dir = "/dev/shm"
# === Prometheus 監控配置 ===
statsd_host = "localhost:9125"
statsd_prefix = "gunicorn"
# === Hooks ===
import time
import logging
slow_logger = logging.getLogger('slow')
def post_fork(server, worker):
"""Worker 初始化"""
from django import db
from django.core.cache import cache
# 關閉所有資料庫連接
db.connections.close_all()
# 測試 cache 連接
try:
cache.get('health_check')
except Exception as e:
server.log.error(f"Cache connection failed: {e}")
server.log.info(f"Worker {worker.pid} ready")
def pre_request(worker, req):
"""請求開始時記錄時間"""
worker._request_start_time = time.time()
def post_request(worker, req, environ, resp):
"""請求結束時檢查慢請求"""
request_time = time.time() - worker._request_start_time
# 記錄超過 1 秒的慢請求
if request_time > 1.0:
slow_logger.info({
'request_time': request_time,
'method': req.method,
'path': req.path,
'status': resp.status_code,
'worker_pid': worker.pid,
})
def worker_exit(server, worker):
"""Worker 退出時清理"""
server.log.info(f"Worker {worker.pid} exiting")
def worker_abort(worker):
"""Worker 被強制終止時"""
worker.log.warning(f"Worker {worker.pid} aborted (timeout or crash)")
def on_starting(server):
"""Server 啟動時的全域初始化"""
server.log.info("Enterprise Gunicorn server starting")
# 預熱 Redis 連接
# 初始化監控連接
# 等等...
def when_ready(server):
"""Server 準備好接收請求"""
server.log.info("Server is ready for production traffic")
def on_reload(server):
"""熱重載時"""
server.log.info("Server is reloading configuration")
# 可以在這裡重新載入配置、更新快取等
def on_exit(server):
"""Server 關閉時清理"""
server.log.info("Server is shutting down")
# 清理全域資源使用方式:
# 安裝額外依賴
pip install uvicorn[standard] gunicorn python-json-logger
# 建立必要目錄
sudo mkdir -p /var/log/gunicorn
sudo chown -R $USER:$USER /var/log/gunicorn
# 啟動服務
gunicorn -c gunicorn.enterprise.conf.py myapp.asgi:application適合場景:
- 超大型應用(10000+ QPS)
- 需要詳細監控和日誌
- 複雜的業務邏輯
- 需要慢請求追蹤
- 整合 ELK、Prometheus 等監控系統
特點:
- ✅ 結構化日誌(JSON 格式)
- ✅ 慢請求追蹤
- ✅ 完整的 Hook Functions
- ✅ 監控整合
- ✅ 高並發支援
🔧 Systemd 整合範例
在生產環境中,通常使用 Systemd 管理 Gunicorn 服務。
Systemd Service 文件
# /etc/systemd/system/myapp-gunicorn.service
[Unit]
Description=Gunicorn daemon for Django myapp
After=network.target
[Service]
Type=notify
User=www-data
Group=www-data
WorkingDirectory=/opt/myapp
Environment="PATH=/opt/myapp/venv/bin"
ExecStart=/opt/myapp/venv/bin/gunicorn -c /opt/myapp/gunicorn.conf.py myapp.asgi:application
ExecReload=/bin/kill -s HUP $MAINPID
KillMode=mixed
TimeoutStopSec=60
PrivateTmp=true
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target使用方式:
# 重新載入 systemd
sudo systemctl daemon-reload
# 啟動服務
sudo systemctl start myapp-gunicorn
# 設定開機自動啟動
sudo systemctl enable myapp-gunicorn
# 查看狀態
sudo systemctl status myapp-gunicorn
# 查看日誌
sudo journalctl -u myapp-gunicorn -f
# 熱重載
sudo systemctl reload myapp-gunicorn
# 重啟服務
sudo systemctl restart myapp-gunicorn🐳 Docker 整合範例
Dockerfile
FROM python:3.11-slim
WORKDIR /app
# 安裝依賴
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 複製應用代碼
COPY . .
# 建立日誌目錄
RUN mkdir -p /var/log/gunicorn
# 暴露端口
EXPOSE 8000
# 啟動 Gunicorn
CMD ["gunicorn", "-c", "gunicorn.conf.py", "myapp.asgi:application"]docker-compose.yml
version: '3.8'
services:
web:
build: .
command: gunicorn -c gunicorn.conf.py myapp.asgi:application
volumes:
- .:/app
- logs:/var/log/gunicorn
ports:
- "8000:8000"
environment:
- DJANGO_SETTINGS_MODULE=myapp.settings.production
depends_on:
- db
- redis
restart: unless-stopped
db:
image: postgres:15
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
- POSTGRES_DB=myapp
- POSTGRES_USER=myapp
- POSTGRES_PASSWORD=secret
restart: unless-stopped
redis:
image: redis:7-alpine
restart: unless-stopped
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- web
restart: unless-stopped
volumes:
postgres_data:
logs:📊 配置選擇決策樹
是新專案嗎?
├─ 是 → 使用 範例3 (ASGI + Uvicorn Worker)
└─ 否 → Django 版本?
├─ Django 3.0+ → 需要 WebSocket?
│ ├─ 是 → 範例3 (ASGI)
│ └─ 否 → 流量大小?
│ ├─ < 1000 QPS → 範例2 (WSGI)
│ └─ > 1000 QPS → 範例3 (ASGI) 或 範例4
└─ Django < 3.0 → 範例2 (WSGI)
生產環境規模?
├─ 小型 (< 1000 QPS)
│ ├─ Django 3.0+ → 範例3 (ASGI,推薦)
│ └─ Django 2.x → 範例2 (WSGI)
├─ 中型 (1000-5000 QPS) → 範例3 (ASGI)
└─ 大型 (> 5000 QPS) → 範例4 (ASGI + 監控)🎯 總結
快速選擇指南
| 場景 | 推薦配置 | Worker 類型 | Workers 數量 | 適用場景 |
|---|---|---|---|---|
| 本地開發 | 範例1 | sync | 1 | 快速開發、除錯 |
| 遺留系統 (Django 2.x) | 範例2 | sync | CPU * 2 + 1 | 舊專案、不支援 ASGI |
| 現代生產(推薦) | 範例3 | uvicorn | CPU | Django 3.0+、新專案 |
| 超大型生產 | 範例4 | uvicorn | CPU * 2 | 企業級、複雜監控 |
配置文件管理建議
環境分離:
gunicorn.dev.conf.py- 開發環境gunicorn.staging.conf.py- 測試環境gunicorn.prod.conf.py- 生產環境
版本控制:
- 配置文件應納入版本控制
- 敏感信息使用環境變數
- 建立配置範本文件
監控和日誌:
- 使用結構化日誌(JSON)
- 整合 ELK 或 Prometheus
- 設定日誌輪轉避免磁碟滿
持續優化:
- 定期檢查慢請求日誌
- 根據實際流量調整 workers 數量
- 監控記憶體使用,調整 max_requests
💡 面試常見問題
Q1:如何選擇合適的配置文件?
答: 根據以下因素選擇:
應用類型:
- Django 3.0+ 新專案 → ASGI (範例3)
- Django 2.x 遺留系統 → WSGI (範例2)
流量規模:
- 開發環境 → 範例1
- 小型生產 (< 1000 QPS)
- Django 3.0+ → 範例3 (推薦)
- Django 2.x → 範例2
- 中型生產 (1000-5000 QPS) → 範例3
- 大型生產 (> 5000 QPS) → 範例4
功能需求:
- 需要 WebSocket → 必須使用 ASGI (範例3/4)
- 傳統 HTTP + Django 3.0+ → 優先 ASGI (範例3,向後兼容)
- 傳統 HTTP + Django 2.x → WSGI (範例2)
維運需求:
- 需要詳細監控、慢請求追蹤 → 範例4
- 簡單部署 (Django 3.0+) → 範例3 (推薦)
- 簡單部署 (Django 2.x) → 範例2
Q2:preload_app 什麼時候該開啟?
答:
開啟的情況(preload_app = True):
- ✅ 生產環境(節省記憶體)
- ✅ 應用啟動慢(提高啟動速度)
- ✅ 有大量共享資料
- ✅ 配合 Copy-on-Write 優化記憶體
不開啟的情況(preload_app = False):
- ✅ 開發環境(方便除錯)
- ✅ 應用有全域狀態且不線程安全
- ✅ 需要每個 worker 獨立初始化
注意事項:
- 開啟時必須在
post_forkHook 中關閉資料庫連接 - 開啟時全域變數會在多個 worker 間共享(需要注意線程安全)
Q3:如何驗證配置文件是否正確?
答:
# 1. 檢查語法錯誤
python -c "import gunicorn.conf"
# 2. Dry-run 模式(檢查配置但不啟動)
gunicorn --check-config -c gunicorn.conf.py myapp.asgi:application
# 3. 查看實際載入的配置
gunicorn -c gunicorn.conf.py myapp.asgi:application --print-config
# 4. 短時間測試啟動
timeout 10 gunicorn -c gunicorn.conf.py myapp.asgi:applicationQ4:生產環境最佳配置是什麼?
答: 2025 年推薦的生產環境配置:
# 最佳實踐配置
bind = "0.0.0.0:8000"
workers = multiprocessing.cpu_count() # CPU 核心數
worker_class = "uvicorn.workers.UvicornWorker" # 現代 ASGI
worker_connections = 1000
timeout = 120
graceful_timeout = 30
keepalive = 20
max_requests = 2000
max_requests_jitter = 200
preload_app = True
worker_tmp_dir = "/dev/shm"關鍵要點:
- 使用 Uvicorn Worker(支援 async、WebSocket)
- Workers 數量 = CPU 核心數
- 啟用 preload_app 節省記憶體
- 使用 /dev/shm 提高性能
- 設定 max_requests 防止記憶體洩漏
- 適當的 timeout 和 graceful_timeout