Appearance
Gunicorn 原生 ASGI Worker
Gunicorn 25.1.0 起內建穩定的原生 ASGI Worker,讓 FastAPI、Starlette 等 async 框架不再需要依賴 Uvicorn 即可生產部署。
概述
長期以來,在 Kubernetes 或 VM 上部署 FastAPI 應用的標準做法是 Gunicorn + UvicornWorker:Gunicorn 負責 process management(multi-process、graceful reload、signal handling),Uvicorn 作為 worker class 處理 ASGI 協定。這個組合運作良好,但需要同時維護兩個套件,且版本相容性偶爾造成困擾。
Gunicorn 25.0.0(2025)以 Beta 引入原生 asyncio ASGI Worker;25.1.0(2026-02-13)正式升級為 stable。現在只需要 gunicorn 單一套件,指定 --worker-class asgi 即可完整執行 ASGI 應用程式。
原生 ASGI Worker 最大的優勢是「零學習成本」:process management、logging、graceful reload、signal handling 全部沿用 WSGI 時代的行為,現有的 gunicorn.conf.py 只需改一個參數即可遷移。
核心內容
功能矩陣
原生 ASGI Worker 支援的協定與特性:
| 能力 | 說明 |
|---|---|
| HTTP/1.1 | 含 keepalive 連線 |
| HTTP/2 | 需搭配 SSL 與 h2 套件 |
| WebSocket | 內建支援,零額外設定 |
| Lifespan protocol | 支援 startup / shutdown hooks |
| Fast HTTP parser | 可選安裝 gunicorn[fast](基於 picohttpparser + SIMD) |
| uvloop | 可選用,預設 auto(有 uvloop 就自動啟用) |
與其他 ASGI Server 的比較:
| 特性 | Gunicorn ASGI | Uvicorn | Hypercorn |
|---|---|---|---|
| Process management | 內建 | 需外部管理 | 內建 |
| HTTP/2 | ✔️ | ✖️ | ✔️ |
| WebSocket | ✔️ | ✔️ | ✔️ |
| Lifespan | ✔️ | ✔️ | ✔️ |
從 Uvicorn Worker 遷移
遷移只需改一個參數:
bash
# Before
gunicorn main:app -k uvicorn.workers.UvicornWorker --workers 4 --bind 0.0.0.0:8000
# After
gunicorn main:app --worker-class asgi --workers 4 --bind 0.0.0.0:8000gunicorn.conf.py 同樣只需一行更改:
python
# Before
worker_class = "uvicorn.workers.UvicornWorker"
# After
worker_class = "asgi"uvloop 行為: 預設 asgi_loop = "auto",有 uvloop 就自動使用,行為與 Uvicorn 一致,不需額外設定。若需明確控制:
bash
gunicorn main:app --worker-class asgi --asgi-loop uvloop # 強制使用 uvloop
gunicorn main:app --worker-class asgi --asgi-loop asyncio # 強制使用 asyncioLifespan 行為: 預設 asgi_lifespan = "auto",自動偵測應用是否支援 lifespan。FastAPI / Starlette 的 lifespan handler 無需修改。
遷移後可從 requirements.txt 移除的依賴:
uvicorn
httptools # 除非其他地方有用到
websockets # 除非其他地方有用到改用效能更高的原生 C 解析器:
bash
pip install gunicorn[fast] # 安裝 gunicorn_h1c(picohttpparser + SIMD)注意事項與已知限制
--threads無效:ASGI Worker 是 async 架構,並行度由--worker-connections控制,而非 threads- Parser 錯誤回應:部分 malformed request 的錯誤回應可能不符合預期,建議在 staging 環境測試
- uWSGI protocol 不支援 WebSocket:若透過 nginx
uwsgi_pass與 Gunicorn 溝通,WebSocket 端點需改用 HTTP proxy 模式
Production 設定建議
gunicorn.conf.py 推薦設定:
python
# Worker
worker_class = "asgi"
workers = 4 # 通常 = nproc(CPU 核心數)
worker_connections = 1000 # 每個 worker 的最大並行連線數
# Timeouts
keepalive = 5
timeout = 120
graceful_timeout = 30
# Performance
asgi_loop = "auto" # 有 uvloop 就用
asgi_lifespan = "auto" # 自動偵測 lifespan 支援效能調校原則:
| 場景 | 建議 |
|---|---|
| CPU-bound(計算密集) | 增加 --workers,搭配 gunicorn[fast] |
| I/O-bound(DB/API 呼叫密集) | 維持 workers = nproc,增加 --worker-connections |
| 大量 WebSocket 長連線 | 顯著增加 --worker-connections(如 5000+) |
| 追求極致 throughput | 安裝 uvloop + gunicorn[fast] |
框架相容性
官方測試結果(25.x 系列),整體通過率 438/444(98%):
| 框架 | HTTP Scope | HTTP Messages | WebSocket | Lifespan | Streaming | 總計 |
|---|---|---|---|---|---|---|
| FastAPI | 19/19 | 18/19 | 19/19 | 8/8 | 9/9 | 73/74 |
| Django + Channels | 19/19 | 18/19 | 19/19 | 8/8 | 9/9 | 73/74 |
| Starlette | 19/19 | 18/19 | 19/19 | 8/8 | 9/9 | 73/74 |
| Quart | 19/19 | 18/19 | 19/19 | 8/8 | 9/9 | 73/74 |
| Litestar | 19/19 | 18/19 | 19/19 | 8/8 | 9/9 | 73/74 |
| BlackSheep | 19/19 | 18/19 | 19/19 | 8/8 | 9/9 | 73/74 |
Nginx 整合
HTTP Proxy 模式(推薦,支援 WebSocket):
nginx
upstream gunicorn {
server 127.0.0.1:8000;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://gunicorn;
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 endpoint
location /ws {
proxy_pass http://gunicorn;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}uWSGI Protocol 模式(更高效能,但不支援 WebSocket):
bash
# Gunicorn 端
gunicorn main:app --worker-class asgi --protocol uwsgi --bind 127.0.0.1:8000nginx
# Nginx 端
location / {
uwsgi_pass gunicorn;
include uwsgi_params;
}Docker 部署
dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["gunicorn", "main:app", \
"--worker-class", "asgi", \
"--workers", "4", \
"--worker-connections", "1000", \
"--bind", "0.0.0.0:8000"]子路徑部署(Root Path)
若應用掛載在 reverse proxy 的子路徑下:
bash
gunicorn main:app --worker-class asgi --root-path /api等同 WSGI 的 SCRIPT_NAME,框架會自動調整 OpenAPI docs 路徑等行為。
Troubleshooting
| 症狀 | 原因 | 解法 |
|---|---|---|
worker_class = "asgi" 找不到 | gunicorn 版本 < 25.1.0 | 升級 gunicorn>=25.1.0 |
| FastAPI lifespan 沒執行 | 缺少 asgi_lifespan 設定 | 加入 asgi_lifespan = "auto" |
| Lifespan startup failed | lifespan handler 有未捕捉的 exception | 暫時設 asgi_lifespan = "off" 繞過,修正後改回 "auto" |
| Loguru 沒攔截 gunicorn 錯誤日誌 | INTERCEPT_LOGGERS 未包含 gunicorn | 加入 "gunicorn" 和 "gunicorn.error" |
| StatsD 指標沒送出 | STATSD_HOST 格式錯誤或接收端未啟動 | 確認格式 host:port;確認接收端監聽中 |
| 高併發拒絕連線 | worker-connections 不足 | 增加 worker_connections = 2000 與 workers = 8 |
| 高負載回應緩慢 | 預設使用 asyncio | 安裝 uvloop,設定 asgi_loop = "uvloop" |
關鍵要點
- Gunicorn 25.1.0+ 內建穩定 ASGI Worker,只需
--worker-class asgi一個參數 - 從 Uvicorn Worker 遷移極低成本:只改
worker_class一行,其他設定不變 asgi_lifespan = "auto"是確保 FastAPI lifespan(startup/shutdown)正常觸發的必要設定accesslog目錄需在 Dockerfile 中mkdir -p,否則啟動時報錯--threads對 ASGI Worker 無效,並行度改用--worker-connections- 安裝
gunicorn[fast]+uvloop可獲得最高效能 - WebSocket 端點必須使用 HTTP proxy 模式,不支援 uWSGI protocol
- Kubernetes 的
command陣列不需分args,可統一一行:["uv", "run", "gunicorn", "app.main:app", "-c", "gunicorn.conf.py"]
實際應用
對於已在 Kubernetes 上以 Gunicorn + UvicornWorker 運行的 FastAPI 服務,Gunicorn 25.1.0 提供了無痛遷移路徑:修改 gunicorn.conf.py、移除 uvicorn 依賴、安裝 gunicorn[fast],在 staging 環境驗證 lifespan 與 WebSocket 行為後即可上線。對於全新 Python 微服務,原生 ASGI Worker 是統一 WSGI/ASGI 技術棧的最佳選擇。
若需要採集 Gunicorn 運行指標(請求數、回應時間、Worker 數量),可搭配 Prometheus StatsD Exporter 部署 的 Sidecar 模式,在 gunicorn.conf.py 設定 statsd_host = "127.0.0.1:9125" 即可自動推送指標。
部署設定參考
以下為實際部署時使用的完整設定,供日後查詢與複製使用。
環境參數
| 項目 | 值 |
|---|---|
| Gunicorn 最低版本 | 25.1.0 |
| Bind 位址 | 0.0.0.0:8000 |
| Workers | 2(建議依 CPU 核心數 × 2 + 1 調整) |
| Timeout | 120s |
| Graceful Timeout | 30s |
| Access Log 路徑 | logs/access.log |
| STATSD_HOST 格式 | host:port(例如 10.30.196.60:9125) |
依賴套件(pyproject.toml)
toml
# 移除
"uvicorn>=0.x.x"
# 新增
"gunicorn>=25.1.0"
"uvloop>=0.17.0" # 選用:高效能 event loopgunicorn.conf.py 完整設定(FastAPI)
python
import os
# === Core ===
worker_class = "asgi" # Gunicorn 25.1.0+ 原生 ASGI worker
workers = 2 # 建議:CPU 核心數 * 2 + 1,視記憶體調整
worker_connections = 1000
bind = "0.0.0.0:8000"
# === Timeout ===
timeout = 120 # 單一請求最長處理時間(秒)
graceful_timeout = 30 # 關閉前等待 in-flight 請求完成的時間
keepalive = 5
# === ASGI ===
asgi_loop = "auto" # 優先使用 uvloop(若有安裝),否則用 asyncio
asgi_lifespan = "auto" # 自動偵測並執行 FastAPI lifespan context manager
# === Logging ===
accesslog = "logs/access.log" # 需確保目錄存在(Dockerfile 中 mkdir -p)
# === Metrics(選用,搭配 StatsD)===
statsd_host = os.environ.get("STATSD_HOST", "") # 例如 "10.x.x.x:9125"
statsd_prefix = os.environ.get("APP_NAME", "") # 例如 "my-app"Dockerfile 遷移
dockerfile
# Before
COPY log_config.json ./log_config.json
RUN mkdir -p /app/logs
EXPOSE 8000
CMD ["uv", "run", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--log-config", "log_config.json"]
# After
COPY gunicorn.conf.py ./gunicorn.conf.py
RUN mkdir -p /app/logs
EXPOSE 8000
CMD ["uv", "run", "gunicorn", "app.main:app", "-c", "gunicorn.conf.py"]docker-compose.yml 遷移
yaml
# Before
command:
["uv", "run", "uvicorn", "app.main:app",
"--host", "0.0.0.0", "--port", "8000",
"--log-config", "log_config.json"]
# After
command: ["uv", "run", "gunicorn", "app.main:app", "-c", "gunicorn.conf.py"]Kubernetes Deployment 遷移
yaml
# Before
containers:
- name: my-app-server
command: ["uv"]
args: ["run", "uvicorn", "app.main:app",
"--host", "0.0.0.0", "--port", "8000",
"--log-config", "log_config.json"]
# After
containers:
- name: my-app-server
command: ["uv", "run", "gunicorn", "app.main:app", "-c", "gunicorn.conf.py"]驗證指令
bash
# 1. 本地 docker-compose 啟動
docker compose up --build
# 2. 確認 Gunicorn master + worker 啟動訊息
# 應看到:[INFO] Using worker: asgi
# 3. 打 API request 確認正常回應
curl http://localhost:4449/api/health
# 4. 確認 access log 寫入
docker compose exec backend cat logs/access.log
# 5. 執行測試
cd backend && uv run pytest
# 6. 確認 gunicorn 版本(需 ≥ 25.1.0)
uv run gunicorn --version相關概念
- Prometheus StatsD Exporter 部署 — 採集 Gunicorn 運行指標(請求數、Worker 數)的 Sidecar 模式
- Nginx Reverse Proxy to Self(反向代理回自身) — Nginx 作為 Gunicorn 前端 Reverse Proxy 的進階路由模式
- Nginx 檔案服務 Server — 同樣以 Nginx 作為服務入口的部署參考