部署概念¶
當你要部署一個 FastAPI 應用,或其實任何類型的 Web API 時,有幾個你可能在意的概念。掌握這些概念後,你就能找出最適合部署你應用的方式。
一些重要的概念包括:
- 安全性 - HTTPS
- 開機自動執行
- 重新啟動
- 複本(執行中的行程數量)
- 記憶體
- 啟動前的前置步驟
我們將看看它們如何影響部署。
最終目標是能夠以安全、避免中斷,並盡可能高效使用運算資源(例如遠端伺服器/虛擬機)的方式來服務你的 API 用戶端。🚀
我會在這裡多介紹一些這些觀念,希望能幫你建立必要的直覺,讓你能在非常不同、甚至尚未出現的未來環境中決定要如何部署你的 API。
在思考這些概念之後,你將能夠評估與設計最適合部署你自己 API 的方式。
在接下來的章節,我會提供更具體的部署 FastAPI 應用的食譜。
但現在,先來看看這些重要的概念想法。這些概念同樣適用於任何其他類型的 Web API。💡
安全性 - HTTPS¶
在前一章關於 HTTPS 中,我們學到 HTTPS 如何為你的 API 提供加密。
我們也看到,HTTPS 通常由應用伺服器外部的元件提供,即 TLS Termination Proxy。
而且必須有某個東西負責續期 HTTPS 憑證,可能是同一個元件,也可能是不同的東西。
HTTPS 工具範例¶
你可以用來作為 TLS Termination Proxy 的工具包括:
- Traefik
- 自動處理憑證續期 ✨
- Caddy
- 自動處理憑證續期 ✨
- Nginx
- 搭配像 Certbot 這類外部元件進行憑證續期
- HAProxy
- 搭配像 Certbot 這類外部元件進行憑證續期
- Kubernetes,使用如 Nginx 的 Ingress Controller
- 搭配像 cert-manager 這類外部元件進行憑證續期
- 由雲端供應商在其服務內部處理(見下文 👇)
另一個選項是使用能幫你做更多事情的雲端服務(包含設定 HTTPS)。它可能有一些限制或要額外付費等。但在那種情況下,你就不必自己設定 TLS Termination Proxy。
我會在後續章節展示一些具體例子。
接下來要考慮的概念都與實際執行你的 API 的程式(例如 Uvicorn)有關。
程式與行程¶
我們會常提到執行中的「行程(process)」,因此先釐清它的意思,以及與「程式(program)」的差異很有幫助。
什麼是程式¶
「程式(program)」一詞常用來描述許多東西:
- 你寫的原始碼,也就是 Python 檔案。
- 可由作業系統執行的檔案,例如:
python、python.exe或uvicorn。 - 在作業系統上執行中的特定程式,使用 CPU 並將資料存於記憶體。這也稱為「行程」。
什麼是行程¶
「行程(process)」通常以更特定的方式使用,只指作業系統中正在執行的東西(如上面最後一點):
- 在作業系統上「執行中」的特定程式。
- 這不是指檔案或原始碼,而是特指正在被作業系統執行並管理的那個東西。
- 任何程式、任何程式碼,只有在「被執行」時才能做事。所以,當有「行程在執行」時才能運作。
- 行程可以被你或作業系統終止(kill)。此時它就停止執行,無法再做任何事。
- 你電腦上執行的每個應用程式、每個視窗等,背後都有一些行程。而且在電腦開機時,通常會同時有許多行程在跑。
- 同一個程式可以同時有多個行程在執行。
如果你打開作業系統的「工作管理員」或「系統監控器」(或類似工具),就能看到許多正在執行的行程。
例如,你大概會看到同一個瀏覽器(Firefox、Chrome、Edge 等)會有多個行程在執行。它們通常每個分頁一個行程,外加其他一些額外行程。

現在我們知道「行程」與「程式」的差異了,繼續談部署。
開機自動執行¶
多數情況下,當你建立一個 Web API,你會希望它「一直在執行」,不中斷,讓客戶端隨時可用。除非你有特定理由只在某些情況下才執行,但大部分時候你會希望它持續運作並且可用。
在遠端伺服器上¶
當你設定一台遠端伺服器(雲端伺服器、虛擬機等),最簡單的作法就是像本機開發時一樣,手動使用 fastapi run(它使用 Uvicorn)或類似的方式。
這在「開發期間」會運作良好而且有用。
但如果你與伺服器的連線中斷,正在執行的行程很可能會死掉。
而如果伺服器被重新啟動(例如更新後、或雲端供應商進行遷移),你大概「不會注意到」。因此你甚至不知道要手動重啟行程。你的 API 就會一直掛著。😱
開機自動啟動¶
通常你會希望伺服器程式(例如 Uvicorn)在伺服器開機時自動啟動,且不需任何「人工介入」,讓你的 API(例如 Uvicorn 執行你的 FastAPI 應用)總是有行程在跑。
獨立程式¶
為了達成這點,你通常會有一個「獨立的程式」來確保你的應用在開機時會被啟動。很多情況下,它也會確保其他元件或應用一併啟動,例如資料庫。
開機自動啟動的工具範例¶
能做到這件事的工具包括:
- Docker
- Kubernetes
- Docker Compose
- Docker 的 Swarm 模式
- Systemd
- Supervisor
- 由雲端供應商在其服務內部處理
- 其他...
我會在後續章節給出更具體的例子。
重新啟動¶
和確保你的應用在開機時會執行一樣,你大概也會希望在發生失敗之後,它能「自動重新啟動」。
人都會犯錯¶
我們身為人,常常會犯錯。軟體幾乎總是有藏在各處的「臭蟲(bugs)」🐛
而我們開發者會在發現這些 bug 後持續改進程式碼、實作新功能(也可能順便加進新的 bug 😅)。
小錯誤自動處理¶
使用 FastAPI 建構 Web API 時,如果我們的程式碼出錯,FastAPI 通常會把它限制在觸發該錯誤的單次請求之中。🛡
用戶端會收到「500 Internal Server Error」,但應用會繼續處理之後的請求,而不是整個崩潰。
更嚴重的錯誤 - 當機¶
然而,仍可能有一些情況,我們寫的程式碼「讓整個應用當機」,使 Uvicorn 與 Python 都崩潰。💥
即便如此,你大概也不會希望應用因為某處錯誤就一直處於死亡狀態,你可能會希望它「繼續運作」,至少讓沒有壞掉的「路徑操作(path operations)」能持續服務。
當機後重新啟動¶
在這些會讓「執行中行程」整個崩潰的嚴重錯誤案例裡,你會希望有個外部元件負責「重新啟動」該行程,至少嘗試幾次...
Tip
...不過,如果整個應用「一啟動就立刻」崩潰,那持續無止境地重啟大概沒有意義。但在這類情況下,你很可能會在開發過程中就發現,或至少在部署後馬上注意到。
所以讓我們專注在主要情境:應用在未來某些特定案例中可能會整體崩潰,但此時重新啟動仍然是有意義的。
你大概會希望把負責重新啟動應用的東西放在「外部元件」,因為那個時候,應用本身連同 Uvicorn 與 Python 都已經掛了,在同一個應用的程式碼裡也無法做什麼。
自動重新啟動的工具範例¶
多數情況下,用來「在開機時啟動程式」的同一套工具,也會負責處理自動「重新啟動」。
例如,可以由下列工具處理:
- Docker
- Kubernetes
- Docker Compose
- Docker 的 Swarm 模式
- Systemd
- Supervisor
- 由雲端供應商在其服務內部處理
- 其他...
複本:行程與記憶體¶
在 FastAPI 應用中,使用像 fastapi 指令(執行 Uvicorn)的伺服器程式,即使只在「一個行程」中執行,也能同時服務多個客戶端。
但很多情況下,你會想同時執行多個工作行程(workers)。
多個行程 - Workers¶
如果你的客戶端比單一行程所能處理的更多(例如虛擬機規格不大),而伺服器 CPU 有「多核心」,那你可以同時執行「多個行程」載入相同的應用,並把所有請求分散給它們。
當你執行同一個 API 程式的「多個行程」時,通常稱為「workers(工作行程)」。
工作行程與連接埠¶
還記得文件中關於 HTTPS 的說明嗎:在一台伺服器上,一組 IP 與連接埠的組合只能由「一個行程」監聽?
這裡同樣適用。
因此,若要同時擁有「多個行程」,就必須有「單一行程」在該連接埠上監聽,然後以某種方式把通信傳遞給各個工作行程。
每個行程的記憶體¶
當程式把東西載入記憶體時,例如把機器學習模型存到變數中,或把大型檔案內容讀到變數中,這些都會「消耗一些伺服器的記憶體(RAM)」。
而多個行程通常「不共享記憶體」。這表示每個執行中的行程都有自己的東西、變數與記憶體。如果你的程式碼會用掉大量記憶體,「每個行程」都會消耗等量的記憶體。
伺服器記憶體¶
例如,如果你的程式碼載入一個「1 GB 大小」的機器學習模型,當你用一個行程執行你的 API,它就會至少吃掉 1 GB 的 RAM。若你啟動「4 個行程」(4 個 workers),每個會吃 1 GB RAM。總計你的 API 會吃掉「4 GB RAM」。
如果你的遠端伺服器或虛擬機只有 3 GB RAM,嘗試載入超過 4 GB 的 RAM 就會出問題。🚨
多個行程 - 範例¶
在這個例子中,有一個「管理行程(Manager Process)」會啟動並控制兩個「工作行程(Worker Processes)」。
這個管理行程大概就是在 IP 的「連接埠」上監聽的那個。它會把所有通信轉發到各個工作行程。
那些工作行程才是實際執行你的應用的,它們會完成主要的計算,接收「請求」並回傳「回應」,也會把你放在變數中的東西載入 RAM。
當然,同一台機器上除了你的應用之外,通常也會有「其他行程」在執行。
有個有趣的細節是,每個行程的「CPU 使用率」百分比會隨時間大幅「變動」,但「記憶體(RAM)」通常維持相對「穩定」。
如果你的 API 每次做的計算量相近,且客戶很多,那「CPU 使用率」也可能「相對穩定」(而不是快速上下起伏)。
複本與擴展的工具與策略範例¶
要達成這些有很多種作法。我會在後續章節(例如談到 Docker 與容器時)介紹更具體的策略。
主要的限制是:必須有「單一」元件負責處理「公開 IP」上的「連接埠」。接著它必須能把通信「轉發」到被複製的「行程/workers」。
以下是一些可能的組合與策略:
- Uvicorn 搭配
--workers- 一個 Uvicorn「管理行程」會在「IP」與「連接埠」上監聽,並啟動「多個 Uvicorn 工作行程」。
- Kubernetes 與其他分散式「容器系統」
- 由「Kubernetes」層在「IP」與「連接埠」上監聽。複本的方式是有「多個容器」,每個容器內執行「一個 Uvicorn 行程」。
- 由「雲端服務」替你處理
- 雲端服務很可能「替你處理複本」。它可能讓你定義「要執行的行程」或「容器映像」,無論如何,多半會是「單一 Uvicorn 行程」,而由雲端服務負責進行複製。
Tip
先別擔心這裡提到的「容器」、Docker 或 Kubernetes 如果現在還不太懂。
我會在未來的章節進一步說明容器映像、Docker、Kubernetes 等等:容器中的 FastAPI - Docker。
啟動前的前置步驟¶
很多情況下,你會希望在應用「啟動之前」先執行一些步驟。
例如,你可能想先執行「資料庫遷移」。
但在多數情況下,你會希望這些步驟只執行「一次」。
所以,你會希望用「單一行程」來執行那些「前置步驟」,在啟動應用之前完成。
而且即使之後你要為應用本身啟動「多個行程」(多個 workers),你也必須確保只有一個行程在跑那些前置步驟。若由「多個行程」去跑,會在「平行」中重複同樣的工作;而如果那些步驟像是資料庫遷移這類敏感操作,它們之間可能會互相衝突。
當然,也有一些情況,重複執行前置步驟不會有問題;在那種情況下就容易處理得多。
Tip
另外請記住,依照你的設定,在某些情況下你「甚至可能不需要任何前置步驟」才能啟動應用。
這種情況下,你就不用為此費心了。🤷
前置步驟策略範例¶
這會「高度取決於」你「部署系統」的方式,而且很可能與你如何啟動程式、處理重新啟動等有關。
以下是一些可能的做法:
- 在 Kubernetes 中使用一個「Init Container」,它會在你的應用容器之前先執行
- 一個 bash 腳本先跑前置步驟,然後再啟動你的應用
- 你仍然需要有機制來啟動/重新啟動「那個」bash 腳本、偵測錯誤等
Tip
我會在未來關於容器的章節提供更具體的範例:容器中的 FastAPI - Docker。
資源使用率¶
你的伺服器(群)是可以被「消耗/利用」的「資源」,你的程式會使用 CPU 的計算時間,以及可用的 RAM 記憶體。
你想要消耗/利用多少系統資源?直覺上可能會想「不要太多」,但實際上,你大概會希望在「不當機」的前提下「盡可能用多一點」。
如果你花錢租了 3 台伺服器,卻只用了它們少量的 RAM 與 CPU,那你可能是在「浪費金錢」💸、也「浪費伺服器電力」🌎 等。
在那種情況下,可能更好的是只用 2 台伺服器,並以更高的比例使用它們的資源(CPU、記憶體、磁碟、網路頻寬等)。
另一方面,如果你有 2 台伺服器,且你使用了它們「100% 的 CPU 與 RAM」,某個時刻一個行程會要求更多記憶體,伺服器就得用磁碟當作「記憶體」(這可能慢上數千倍),甚至「當機」。或是某個行程需要做計算時,必須等到 CPU 再度空閒。
這種情況下,最好是再加一台伺服器,並在上面跑部分行程,讓所有行程都有「足夠的 RAM 與 CPU 時間」。
也有機會因為某些原因,你的 API 使用量出現「尖峰」。也許它爆紅了,或是其他服務或機器人開始使用它。在這些情況下,你可能會希望留有一些額外資源以策安全。
你可以設定一個「目標數字」,例如資源使用率落在「50% 到 90%」之間。重點是,這些大概就是你會想測量並用來調校部署的主要指標。
你可以用像 htop 這樣的簡單工具,查看伺服器的 CPU 與 RAM 使用量,或各行程的使用量。也可以用更複雜的監控工具,分散式地監看多台伺服器,等等。
重點回顧¶
這裡介紹了一些你在決定如何部署應用時應該記住的主要概念:
- 安全性 - HTTPS
- 開機自動執行
- 重新啟動
- 複本(執行中的行程數量)
- 記憶體
- 啟動前的前置步驟
理解這些想法與如何套用它們,應能給你足夠的直覺,在設定與調整部署時做出各種決策。🤓
在接下來的章節,我會提供更多可行策略的具體範例。🚀