FastAPI 部署與架構 – uvicorn workers 設定
簡介
在開發完 FastAPI 應用之後,最終的挑戰往往是 如何讓服務在生產環境中穩定、效能佳地運行。
Uvicorn 作為 FastAPI 官方推薦的 ASGI 伺服器,提供了 --workers 參數讓我們可以同時啟動多個 worker 進程,充分利用多核心 CPU,提升併發處理能力。
如果對 workers 的概念與設定方式不熟悉,常會出現資源浪費、效能瓶頸,甚至服務不穩的問題。本文將從核心概念、實作範例、常見陷阱與最佳實踐,帶你一步步掌握 uvicorn workers 的設定,讓你的 FastAPI 應用在生產環境中跑得更快、更穩。
核心概念
1. 為什麼需要多個 worker?
- CPU 核心利用:Uvicorn 預設是單執行緒的 asyncio 事件迴圈,只有一個 CPU 核心在工作。透過
--workers N,Uvicorn 會 fork 出 N 個獨立的 Python 進程,每個進程各自擁有自己的事件迴圈與 CPU 時間片,能夠同時處理更多請求。 - 容錯與穩定性:若某個 worker 發生未捕捉例外或崩潰,其他 worker 仍可繼續服務,避免整個服務瞬間斷線。
- 記憶體與 I/O 分離:在 I/O 密集(例如大量資料庫查詢)或 CPU 密集(例如影像處理)情境下,分散工作負載能減少單一進程的記憶體與資源瓶頸。
2. 工作原理 – Pre‑fork 模式
Uvicorn 採用 pre‑fork(先 fork)方式啟動 workers:
- 主進程解析指令列參數,建立
socket(監聽埠)。 - 主進程先
fork()N 次,產生 N 個子進程(worker)。 - 每個 worker 繼承已建立的
socket,各自啟動自己的事件迴圈。 - 主進程只負責監控子進程的生命週期,發生異常時會自動重啟。
注意:因為是
fork,子進程會拷貝主進程的記憶體映射(Copy‑On‑Write),所以在啟動前 不要在全域變數中執行大量 I/O,以免造成不必要的記憶體拷貝。
3. 設定方式
Uvicorn 支援兩種常見的啟動方式:
| 方式 | 範例指令 | 說明 |
|---|---|---|
| CLI | uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 4 |
直接在終端機下指令,適合簡單部署或測試。 |
| Python 程式 | uvicorn.run("app.main:app", host="0.0.0.0", port=8000, workers=4) |
在自訂腳本或 Docker entrypoint 中使用,方便加入前置或後置處理。 |
程式碼範例
以下示範 5 個實務上常見的 uvicorn workers 設定情境,全部使用 Python 語言。
範例 1️⃣:最簡單的 CLI 啟動(4 個 workers)
uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 4
只需在
Dockerfile或 CI/CD pipeline 中加入這行指令,即可讓 FastAPI 使用四核 CPU 完全發揮效能。
範例 2️⃣:在程式內部使用 uvicorn.run
# run_server.py
import uvicorn
if __name__ == "__main__":
# 使用 2 個 workers,適合雙核或低資源的 VM
uvicorn.run(
"app.main:app",
host="0.0.0.0",
port=8000,
workers=2,
log_level="info",
)
這種寫法適合在 Docker entrypoint 或 systemd service 中直接呼叫
python run_server.py。
範例 3️⃣:根據 CPU 核心自動決定 workers 數量
# auto_workers.py
import multiprocessing
import uvicorn
def get_worker_count():
# 留一個核心給作業系統與其他服務
cpu_cnt = multiprocessing.cpu_count()
return max(1, cpu_cnt - 1)
if __name__ == "__main__":
uvicorn.run(
"app.main:app",
host="0.0.0.0",
port=8000,
workers=get_worker_count(),
log_level="warning",
)
最佳實踐:在雲端或容器環境中,CPU 核心數量可能會變動,此程式碼可自動調整 workers,避免手動設定錯誤。
範例 4️⃣:與 Gunicorn 結合使用 uvicorn.workers.UvicornWorker
# 在 Dockerfile 中安裝 gunicorn
pip install gunicorn
# 使用 gunicorn 指定 worker 類別
gunicorn app.main:app \
-w 4 \
-k uvicorn.workers.UvicornWorker \
-b 0.0.0.0:8000 \
--log-level info
Gunicorn 提供了 Graceful shutdown、pre‑load、max‑requests 等額外功能,適合需要更細緻控制的生產環境。
範例 5️⃣:設定 timeout 與 max_requests(防止記憶體泄漏)
gunicorn app.main:app \
-w 3 \
-k uvicorn.workers.UvicornWorker \
-b 0.0.0.0:8000 \
--timeout 30 \
--max-requests 1000 \
--max-requests-jitter 50
--timeout:若 worker 超過 30 秒未回應,Gunicorn 會自動重啟。--max-requests:每處理 1000 個請求後重啟 worker,可減少長時間運行所產生的記憶體泄漏。--max-requests-jitter:在 0~50 之間隨機調整重啟點,避免所有 worker 同時重啟造成短暫斷線。
常見陷阱與最佳實踐
| 陷阱 | 可能的後果 | 解決方案 / 建議 |
|---|---|---|
| Worker 數量過多 | 記憶體耗盡、CPU 爭用、效能下降 | 依照 CPU 核心數 設定,通常 workers = cores - 1 為佳;若容器限制記憶體,需額外測試。 |
| 在全域變數中執行 I/O (例如 DB 連線) | 子進程會拷貝已建立的連線,導致 連線錯誤或資源浪費 | 把 DB、Redis 等連線放在 依賴注入 或 startup 事件 中,讓每個 worker 各自建立連線。 |
忘記設定 --log-level |
日誌過於冗長或無法追蹤錯誤 | 在開發環境使用 debug,在生產環境使用 info 或 warning。 |
直接在 Dockerfile CMD 中使用 uvicorn,未設定 workers |
只跑單一 worker,無法利用多核心 | 在 Dockerfile 中明確加入 --workers ${WORKERS},或使用 entrypoint.sh 讀取環境變數。 |
使用 --reload 與多 worker 同時啟動 |
--reload 只在單 worker 時有效,會產生多個重載監控,浪費資源 |
生產環境 絕不 同時開啟 --reload 與多 worker;開發環境僅使用單 worker。 |
最佳實踐:
- 先測試單 worker,確認應用邏輯與相依套件在多進程環境下不會產生衝突(例如 global state、檔案寫入)。
- 使用
preload_app=True(Gunicorn):在 worker fork 前先載入應用程式,減少每個 worker 的啟動時間,但要確保所有全域資源在 fork 前是安全的。 - 監控與自動重啟:結合 systemd、Docker 的健康檢查或 Kubernetes livenessProbe,確保 worker 異常時能自動恢復。
- 設定
--max-requests:長時間運行的服務容易累積記憶體碎片,定期重啟 worker 可維持穩定性。
實際應用場景
| 場景 | 為什麼需要調整 workers | 推薦設定 |
|---|---|---|
| 小型單機 VM(2 核) | 只需要基本併發,資源有限 | workers=1(或 2-1=1) |
| 中型雲端 VM(4 核) | 多使用者同時呼叫 API,I/O 密集 | workers=3,加上 --max-requests 2000 |
| CPU 密集的影像辨識服務 | 每個請求會佔用大量 CPU,需平衡 | workers=2(保留 2 核給作業系統)+ --timeout 60 |
| Kubernetes 部署 | Pod 可能被水平擴展,需自動調整 | 使用環境變數 WORKERS=$(($(nproc) - 1)),在 entrypoint 中 uvicorn ... --workers $WORKERS |
| 使用 Gunicorn 作為前置 | 想要利用 Gunicorn 的 graceful shutdown、log rotation | gunicorn -w 4 -k uvicorn.workers.UvicornWorker -b :8000 app.main:app |
總結
- workers 是提升 FastAPI 服務併發與容錯的關鍵參數,透過
uvicorn --workers N或結合 Gunicorn 的UvicornWorker可輕鬆達成。 - 正確的 worker 數量 取決於 CPU 核心、記憶體限制與應用類型,建議以
cores - 1為起點,並在正式環境中透過監控調整。 - 避免在全域變數中執行 I/O,使用 依賴注入 或 startup 事件 讓每個 worker 各自建立資源,防止 fork 後的資源衝突。
- 結合 max‑requests、timeout、preload_app 等 Gunicorn 選項,可進一步提升穩定性與效能。
掌握 uvicorn workers 的設定,讓你的 FastAPI 應用在生產環境中 跑得更快、更穩,同時也為未來的垂直或水平擴展奠定堅實基礎。祝開發順利,服務永遠在線!