Sync fastapi docs from 11614be9 on 2026-03-11

This commit is contained in:
The Librarian
2026-03-11 04:00:06 +00:00
parent 95d1a394e6
commit 18a95756ed
1722 changed files with 91405 additions and 23556 deletions
+16 -5
View File
@@ -1,13 +1,24 @@
# 在雲端部署 FastAPI
# 在雲端供應商上部署 FastAPI { #deploy-fastapi-on-cloud-providers }
你幾乎可以使用**任何雲端供應商**來部署你的 FastAPI 應用程式。
在大多數情況下,主要的雲端供應商都有部署 FastAPI 的指南。
## 雲端供應商 - 贊助商
## FastAPI Cloud { #fastapi-cloud }
一些雲端供應商 ✨ [**贊助 FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨,這確保了 FastAPI 及其**生態系統**持續健康地**發展**
**<a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>** 由 **FastAPI** 的同一位作者與團隊打造
這也展現了他們對 FastAPI 和其**社群**(包括你)的真正承諾,他們不僅希望為你提供**優質的服務**,還希望確保你擁有一個**良好且健康的框架**:FastAPI。🙇
它讓你以最少的投入,簡化 **建置**、**部署** 與 **存取** API 的流程。
你可能會想嘗試他們的服務,以下有他們的指南.
它把使用 FastAPI 開發應用的同樣**優秀的開發者體驗**,帶到將它們**部署**到雲端的過程中。🎉
FastAPI Cloud 是 *FastAPI and friends* 開源專案的主要贊助與資金提供者。✨
## 雲端供應商 - 贊助商 { #cloud-providers-sponsors }
其他一些雲端供應商也會 ✨ [**贊助 FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨。🙇
你也可以參考他們的指南並試用其服務:
* <a href="https://docs.render.com/deploy-fastapi?utm_source=deploydoc&utm_medium=referral&utm_campaign=fastapi" class="external-link" target="_blank">Render</a>
* <a href="https://docs.railway.com/guides/fastapi?utm_medium=integration&utm_source=docs&utm_campaign=fastapi" class="external-link" target="_blank">Railway</a>
+321
View File
@@ -0,0 +1,321 @@
# 部署概念 { #deployments-concepts }
當你要部署一個 FastAPI 應用,或其實任何類型的 Web API 時,有幾個你可能在意的概念。掌握這些概念後,你就能找出最適合部署你應用的方式。
一些重要的概念包括:
- 安全性 - HTTPS
- 開機自動執行
- 重新啟動
- 複本(執行中的行程數量)
- 記憶體
- 啟動前的前置步驟
我們將看看它們如何影響部署。
最終目標是能夠以安全、避免中斷,並盡可能高效使用運算資源(例如遠端伺服器/虛擬機)的方式來服務你的 API 用戶端。🚀
我會在這裡多介紹一些這些觀念,希望能幫你建立必要的直覺,讓你能在非常不同、甚至尚未出現的未來環境中決定要如何部署你的 API。
在思考這些概念之後,你將能夠評估與設計最適合部署你自己 API 的方式。
在接下來的章節,我會提供更具體的部署 FastAPI 應用的食譜。
但現在,先來看看這些重要的概念想法。這些概念同樣適用於任何其他類型的 Web API。💡
## 安全性 - HTTPS { #security-https }
在[前一章關於 HTTPS](https.md){.internal-link target=_blank} 中,我們學到 HTTPS 如何為你的 API 提供加密。
我們也看到,HTTPS 通常由應用伺服器外部的元件提供,即 TLS Termination Proxy。
而且必須有某個東西負責續期 HTTPS 憑證,可能是同一個元件,也可能是不同的東西。
### HTTPS 工具範例 { #example-tools-for-https }
你可以用來作為 TLS Termination Proxy 的工具包括:
- Traefik
- 自動處理憑證續期 ✨
- Caddy
- 自動處理憑證續期 ✨
- Nginx
- 搭配像 Certbot 這類外部元件進行憑證續期
- HAProxy
- 搭配像 Certbot 這類外部元件進行憑證續期
- Kubernetes,使用如 Nginx 的 Ingress Controller
- 搭配像 cert-manager 這類外部元件進行憑證續期
- 由雲端供應商在其服務內部處理(見下文 👇)
另一個選項是使用能幫你做更多事情的雲端服務(包含設定 HTTPS)。它可能有一些限制或要額外付費等。但在那種情況下,你就不必自己設定 TLS Termination Proxy。
我會在後續章節展示一些具體例子。
---
接下來要考慮的概念都與實際執行你的 API 的程式(例如 Uvicorn)有關。
## 程式與行程 { #program-and-process }
我們會常提到執行中的「行程(process)」,因此先釐清它的意思,以及與「程式(program)」的差異很有幫助。
### 什麼是程式 { #what-is-a-program }
「程式(program)」一詞常用來描述許多東西:
- 你寫的原始碼,也就是 Python 檔案。
- 可由作業系統執行的檔案,例如:`python``python.exe``uvicorn`
- 在作業系統上執行中的特定程式,使用 CPU 並將資料存於記憶體。這也稱為「行程」。
### 什麼是行程 { #what-is-a-process }
「行程(process)」通常以更特定的方式使用,只指作業系統中正在執行的東西(如上面最後一點):
- 在作業系統上「執行中」的特定程式。
- 這不是指檔案或原始碼,而是特指正在被作業系統執行並管理的那個東西。
- 任何程式、任何程式碼,只有在「被執行」時才能做事。所以,當有「行程在執行」時才能運作。
- 行程可以被你或作業系統終止(kill)。此時它就停止執行,無法再做任何事。
- 你電腦上執行的每個應用程式、每個視窗等,背後都有一些行程。而且在電腦開機時,通常會同時有許多行程在跑。
- 同一個程式可以同時有多個行程在執行。
如果你打開作業系統的「工作管理員」或「系統監控器」(或類似工具),就能看到許多正在執行的行程。
例如,你大概會看到同一個瀏覽器(Firefox、Chrome、Edge 等)會有多個行程在執行。它們通常每個分頁一個行程,外加其他一些額外行程。
<img class="shadow" src="/img/deployment/concepts/image01.png">
---
現在我們知道「行程」與「程式」的差異了,繼續談部署。
## 開機自動執行 { #running-on-startup }
多數情況下,當你建立一個 Web API,你會希望它「一直在執行」,不中斷,讓客戶端隨時可用。除非你有特定理由只在某些情況下才執行,但大部分時候你會希望它持續運作並且可用。
### 在遠端伺服器上 { #in-a-remote-server }
當你設定一台遠端伺服器(雲端伺服器、虛擬機等),最簡單的作法就是像本機開發時一樣,手動使用 `fastapi run`(它使用 Uvicorn)或類似的方式。
這在「開發期間」會運作良好而且有用。
但如果你與伺服器的連線中斷,正在執行的行程很可能會死掉。
而如果伺服器被重新啟動(例如更新後、或雲端供應商進行遷移),你大概「不會注意到」。因此你甚至不知道要手動重啟行程。你的 API 就會一直掛著。😱
### 開機自動啟動 { #run-automatically-on-startup }
通常你會希望伺服器程式(例如 Uvicorn)在伺服器開機時自動啟動,且不需任何「人工介入」,讓你的 API(例如 Uvicorn 執行你的 FastAPI 應用)總是有行程在跑。
### 獨立程式 { #separate-program }
為了達成這點,你通常會有一個「獨立的程式」來確保你的應用在開機時會被啟動。很多情況下,它也會確保其他元件或應用一併啟動,例如資料庫。
### 開機自動啟動的工具範例 { #example-tools-to-run-at-startup }
能做到這件事的工具包括:
- Docker
- Kubernetes
- Docker Compose
- Docker 的 Swarm 模式
- Systemd
- Supervisor
- 由雲端供應商在其服務內部處理
- 其他...
我會在後續章節給出更具體的例子。
## 重新啟動 { #restarts }
和確保你的應用在開機時會執行一樣,你大概也會希望在發生失敗之後,它能「自動重新啟動」。
### 人都會犯錯 { #we-make-mistakes }
我們身為人,常常會犯錯。軟體幾乎總是有藏在各處的「臭蟲(bugs)」🐛
而我們開發者會在發現這些 bug 後持續改進程式碼、實作新功能(也可能順便加進新的 bug 😅)。
### 小錯誤自動處理 { #small-errors-automatically-handled }
使用 FastAPI 建構 Web API 時,如果我們的程式碼出錯,FastAPI 通常會把它限制在觸發該錯誤的單次請求之中。🛡
用戶端會收到「500 Internal Server Error」,但應用會繼續處理之後的請求,而不是整個崩潰。
### 更嚴重的錯誤 - 當機 { #bigger-errors-crashes }
然而,仍可能有一些情況,我們寫的程式碼「讓整個應用當機」,使 Uvicorn 與 Python 都崩潰。💥
即便如此,你大概也不會希望應用因為某處錯誤就一直處於死亡狀態,你可能會希望它「繼續運作」,至少讓沒有壞掉的「路徑操作(path operations)」能持續服務。
### 當機後重新啟動 { #restart-after-crash }
在這些會讓「執行中行程」整個崩潰的嚴重錯誤案例裡,你會希望有個外部元件負責「重新啟動」該行程,至少嘗試幾次...
/// tip
...不過,如果整個應用「一啟動就立刻」崩潰,那持續無止境地重啟大概沒有意義。但在這類情況下,你很可能會在開發過程中就發現,或至少在部署後馬上注意到。
所以讓我們專注在主要情境:應用在未來某些特定案例中可能會整體崩潰,但此時重新啟動仍然是有意義的。
///
你大概會希望把負責重新啟動應用的東西放在「外部元件」,因為那個時候,應用本身連同 Uvicorn 與 Python 都已經掛了,在同一個應用的程式碼裡也無法做什麼。
### 自動重新啟動的工具範例 { #example-tools-to-restart-automatically }
多數情況下,用來「在開機時啟動程式」的同一套工具,也會負責處理自動「重新啟動」。
例如,可以由下列工具處理:
- Docker
- Kubernetes
- Docker Compose
- Docker 的 Swarm 模式
- Systemd
- Supervisor
- 由雲端供應商在其服務內部處理
- 其他...
## 複本:行程與記憶體 { #replication-processes-and-memory }
在 FastAPI 應用中,使用像 `fastapi` 指令(執行 Uvicorn)的伺服器程式,即使只在「一個行程」中執行,也能同時服務多個客戶端。
但很多情況下,你會想同時執行多個工作行程(workers)。
### 多個行程 - Workers { #multiple-processes-workers }
如果你的客戶端比單一行程所能處理的更多(例如虛擬機規格不大),而伺服器 CPU 有「多核心」,那你可以同時執行「多個行程」載入相同的應用,並把所有請求分散給它們。
當你執行同一個 API 程式的「多個行程」時,通常稱為「workers(工作行程)」。
### 工作行程與連接埠 { #worker-processes-and-ports }
還記得文件中[關於 HTTPS](https.md){.internal-link target=_blank} 的說明嗎:在一台伺服器上,一組 IP 與連接埠的組合只能由「一個行程」監聽?
這裡同樣適用。
因此,若要同時擁有「多個行程」,就必須有「單一行程」在該連接埠上監聽,然後以某種方式把通信傳遞給各個工作行程。
### 每個行程的記憶體 { #memory-per-process }
當程式把東西載入記憶體時,例如把機器學習模型存到變數中,或把大型檔案內容讀到變數中,這些都會「消耗一些伺服器的記憶體(RAM)」。
而多個行程通常「不共享記憶體」。這表示每個執行中的行程都有自己的東西、變數與記憶體。如果你的程式碼會用掉大量記憶體,「每個行程」都會消耗等量的記憶體。
### 伺服器記憶體 { #server-memory }
例如,如果你的程式碼載入一個「1 GB 大小」的機器學習模型,當你用一個行程執行你的 API,它就會至少吃掉 1 GB 的 RAM。若你啟動「4 個行程」(4 個 workers),每個會吃 1 GB RAM。總計你的 API 會吃掉「4 GB RAM」。
如果你的遠端伺服器或虛擬機只有 3 GB RAM,嘗試載入超過 4 GB 的 RAM 就會出問題。🚨
### 多個行程 - 範例 { #multiple-processes-an-example }
在這個例子中,有一個「管理行程(Manager Process)」會啟動並控制兩個「工作行程(Worker Processes)」。
這個管理行程大概就是在 IP 的「連接埠」上監聽的那個。它會把所有通信轉發到各個工作行程。
那些工作行程才是實際執行你的應用的,它們會完成主要的計算,接收「請求」並回傳「回應」,也會把你放在變數中的東西載入 RAM。
<img src="/img/deployment/concepts/process-ram.drawio.svg">
當然,同一台機器上除了你的應用之外,通常也會有「其他行程」在執行。
有個有趣的細節是,每個行程的「CPU 使用率」百分比會隨時間大幅「變動」,但「記憶體(RAM)」通常維持相對「穩定」。
如果你的 API 每次做的計算量相近,且客戶很多,那「CPU 使用率」也可能「相對穩定」(而不是快速上下起伏)。
### 複本與擴展的工具與策略範例 { #examples-of-replication-tools-and-strategies }
要達成這些有很多種作法。我會在後續章節(例如談到 Docker 與容器時)介紹更具體的策略。
主要的限制是:必須有「單一」元件負責處理「公開 IP」上的「連接埠」。接著它必須能把通信「轉發」到被複製的「行程/workers」。
以下是一些可能的組合與策略:
- Uvicorn 搭配 `--workers`
- 一個 Uvicorn「管理行程」會在「IP」與「連接埠」上監聽,並啟動「多個 Uvicorn 工作行程」。
- Kubernetes 與其他分散式「容器系統」
- 由「Kubernetes」層在「IP」與「連接埠」上監聽。複本的方式是有「多個容器」,每個容器內執行「一個 Uvicorn 行程」。
- 由「雲端服務」替你處理
- 雲端服務很可能「替你處理複本」。它可能讓你定義「要執行的行程」或「容器映像」,無論如何,多半會是「單一 Uvicorn 行程」,而由雲端服務負責進行複製。
/// tip
先別擔心這裡提到的「容器」、Docker 或 Kubernetes 如果現在還不太懂。
我會在未來的章節進一步說明容器映像、Docker、Kubernetes 等等:[容器中的 FastAPI - Docker](docker.md){.internal-link target=_blank}。
///
## 啟動前的前置步驟 { #previous-steps-before-starting }
很多情況下,你會希望在應用「啟動之前」先執行一些步驟。
例如,你可能想先執行「資料庫遷移」。
但在多數情況下,你會希望這些步驟只執行「一次」。
所以,你會希望用「單一行程」來執行那些「前置步驟」,在啟動應用之前完成。
而且即使之後你要為應用本身啟動「多個行程」(多個 workers),你也必須確保只有一個行程在跑那些前置步驟。若由「多個行程」去跑,會在「平行」中重複同樣的工作;而如果那些步驟像是資料庫遷移這類敏感操作,它們之間可能會互相衝突。
當然,也有一些情況,重複執行前置步驟不會有問題;在那種情況下就容易處理得多。
/// tip
另外請記住,依照你的設定,在某些情況下你「甚至可能不需要任何前置步驟」才能啟動應用。
這種情況下,你就不用為此費心了。🤷
///
### 前置步驟策略範例 { #examples-of-previous-steps-strategies }
這會「高度取決於」你「部署系統」的方式,而且很可能與你如何啟動程式、處理重新啟動等有關。
以下是一些可能的做法:
- 在 Kubernetes 中使用一個「Init Container」,它會在你的應用容器之前先執行
- 一個 bash 腳本先跑前置步驟,然後再啟動你的應用
- 你仍然需要有機制來啟動/重新啟動「那個」bash 腳本、偵測錯誤等
/// tip
我會在未來關於容器的章節提供更具體的範例:[容器中的 FastAPI - Docker](docker.md){.internal-link target=_blank}。
///
## 資源使用率 { #resource-utilization }
你的伺服器(群)是可以被「消耗/利用」的「資源」,你的程式會使用 CPU 的計算時間,以及可用的 RAM 記憶體。
你想要消耗/利用多少系統資源?直覺上可能會想「不要太多」,但實際上,你大概會希望在「不當機」的前提下「盡可能用多一點」。
如果你花錢租了 3 台伺服器,卻只用了它們少量的 RAM 與 CPU,那你可能是在「浪費金錢」💸、也「浪費伺服器電力」🌎 等。
在那種情況下,可能更好的是只用 2 台伺服器,並以更高的比例使用它們的資源(CPU、記憶體、磁碟、網路頻寬等)。
另一方面,如果你有 2 台伺服器,且你使用了它們「100% 的 CPU 與 RAM」,某個時刻一個行程會要求更多記憶體,伺服器就得用磁碟當作「記憶體」(這可能慢上數千倍),甚至「當機」。或是某個行程需要做計算時,必須等到 CPU 再度空閒。
這種情況下,最好是再加一台伺服器,並在上面跑部分行程,讓所有行程都有「足夠的 RAM 與 CPU 時間」。
也有機會因為某些原因,你的 API 使用量出現「尖峰」。也許它爆紅了,或是其他服務或機器人開始使用它。在這些情況下,你可能會希望留有一些額外資源以策安全。
你可以設定一個「目標數字」,例如資源使用率落在「50% 到 90%」之間。重點是,這些大概就是你會想測量並用來調校部署的主要指標。
你可以用像 `htop` 這樣的簡單工具,查看伺服器的 CPU 與 RAM 使用量,或各行程的使用量。也可以用更複雜的監控工具,分散式地監看多台伺服器,等等。
## 重點回顧 { #recap }
這裡介紹了一些你在決定如何部署應用時應該記住的主要概念:
- 安全性 - HTTPS
- 開機自動執行
- 重新啟動
- 複本(執行中的行程數量)
- 記憶體
- 啟動前的前置步驟
理解這些想法與如何套用它們,應能給你足夠的直覺,在設定與調整部署時做出各種決策。🤓
在接下來的章節,我會提供更多可行策略的具體範例。🚀
+618
View File
@@ -0,0 +1,618 @@
# 在容器中使用 FastAPI - Docker { #fastapi-in-containers-docker }
部署 FastAPI 應用時,一個常見做法是建置一個「Linux 容器映像(container image)」。通常使用 <a href="https://www.docker.com/" class="external-link" target="_blank">Docker</a> 來完成。之後你可以用多種方式部署該容器映像。
使用 Linux 容器有多種優點,包括安全性、可重現性、簡單性等。
/// tip | 提示
趕時間而且已經懂這些?直接跳到下面的 [`Dockerfile` 👇](#build-a-docker-image-for-fastapi)。
///
<details>
<summary>Dockerfile 預覽 👀</summary>
```Dockerfile
FROM python:3.14
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
COPY ./app /code/app
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
# 若在 Nginx 或 Traefik 等代理伺服器後方執行,請加入 --proxy-headers
# CMD ["fastapi", "run", "app/main.py", "--port", "80", "--proxy-headers"]
```
</details>
## 什麼是容器 { #what-is-a-container }
容器(主要是 Linux 容器)是一種非常輕量的方式,用來封裝應用及其所有相依與必要檔案,並讓其與同一系統中的其他容器(其他應用或元件)隔離。
Linux 容器使用與主機(機器、虛擬機、雲端伺服器等)相同的 Linux kernel。這意味著它們非常輕量(相較於完整模擬整個作業系統的虛擬機)。
因此,容器只消耗很少的資源,與直接執行行程相當(而虛擬機會消耗更多)。
容器也有其各自隔離的執行行程(通常只有一個行程)、檔案系統與網路,簡化部署、安全性與開發等。
## 什麼是容器映像 { #what-is-a-container-image }
容器是由容器映像啟動執行的。
容器映像是所有檔案、環境變數,以及在容器中應該執行的預設指令/程式的靜態版本。這裡的「靜態」意指容器映像不在執行,它只是被封裝的檔案與 metadata。
相對於儲存的靜態內容「容器映像」,「容器」通常指執行中的實例,也就是正在被執行的東西。
當容器啟動並執行時(自容器映像啟動),它可以建立或變更檔案、環境變數等。這些變更只會存在於該容器中,不會持久化回底層的容器映像(不會寫回磁碟)。
容器映像可類比為程式檔與其內容,例如 `python` 與某個 `main.py` 檔案。
而容器本身(相對於容器映像)是映像的實際執行實例,類比為「行程」。事實上,容器只有在有行程執行時才在運作(通常只有單一行程)。當其中沒有行程在執行時,容器就會停止。
## 容器映像 { #container-images }
Docker 是用來建立與管理容器映像與容器的主要工具之一。
也有一個公開的 <a href="https://hub.docker.com/" class="external-link" target="_blank">Docker Hub</a>,內含許多工具、環境、資料庫與應用的預先製作「官方映像」。
例如,有官方的 <a href="https://hub.docker.com/_/python" class="external-link" target="_blank">Python 映像</a>。
也有許多其他針對不同用途的映像,例如資料庫:
* <a href="https://hub.docker.com/_/postgres" class="external-link" target="_blank">PostgreSQL</a>
* <a href="https://hub.docker.com/_/mysql" class="external-link" target="_blank">MySQL</a>
* <a href="https://hub.docker.com/_/mongo" class="external-link" target="_blank">MongoDB</a>
* <a href="https://hub.docker.com/_/redis" class="external-link" target="_blank">Redis</a> 等。
使用預製的容器映像很容易「組合」並使用不同工具。例如,嘗試一個新資料庫。多數情況下,你可以使用官方映像,並僅用環境變數加以設定。
如此,你可以學會關於容器與 Docker 的知識,並將這些知識重複運用到許多不同工具與元件上。
因此,你會執行多個容器,內容各不相同,例如一個資料庫、一個 Python 應用、一個帶有 React 前端應用的網頁伺服器,並透過它們的內部網路把它們連接在一起。
所有容器管理系統(例如 Docker 或 Kubernetes)都內建了這些網路功能。
## 容器與行程 { #containers-and-processes }
容器映像通常在其 metadata 中包含當容器啟動時應執行的預設程式或指令,以及要傳給該程式的參數。這與在命令列要執行的內容非常類似。
當容器啟動時,它會執行該指令/程式(雖然你可以覆寫它,讓它執行不同的指令/程式)。
只要主要行程(指令或程式)在執行,容器就會運作。
容器通常只有單一行程,但也可以由主要行程啟動子行程,如此你會在同一個容器內有多個行程。
但不可能在沒有至少一個執行中行程的情況下讓容器運作。若主要行程停止,容器也會停止。
## 建置 FastAPI 的 Docker 映像 { #build-a-docker-image-for-fastapi }
好了,現在來動手做點東西吧!🚀
我會示範如何從零開始,基於官方的 Python 映像,為 FastAPI 建置一個 Docker 映像。
這是你在多數情況下會想做的事,例如:
* 使用 Kubernetes 或類似工具
* 在 Raspberry Pi 上執行
* 使用會替你執行容器映像的雲端服務等
### 套件需求 { #package-requirements }
你的應用通常會把「套件需求」放在某個檔案中。
這主要取決於你用什麼工具來安裝那些需求。
最常見的方式是準備一個 `requirements.txt` 檔案,逐行列出套件名稱與版本。
當然,你會用與在 [關於 FastAPI 版本](versions.md){.internal-link target=_blank} 中讀到的相同概念,來設定版本範圍。
例如,你的 `requirements.txt` 可能像這樣:
```
fastapi[standard]>=0.113.0,<0.114.0
pydantic>=2.7.0,<3.0.0
```
接著你通常會用 `pip` 來安裝這些套件相依,例如:
<div class="termy">
```console
$ pip install -r requirements.txt
---> 100%
Successfully installed fastapi pydantic
```
</div>
/// info | 資訊
還有其他格式與工具可以用來定義與安裝套件相依。
///
### 建立 FastAPI 程式碼 { #create-the-fastapi-code }
* 建立一個 `app` 目錄並進入。
* 建立一個空的 `__init__.py` 檔案。
* 建立一個 `main.py` 檔案,內容如下:
```Python
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str | None = None):
return {"item_id": item_id, "q": q}
```
### Dockerfile { #dockerfile }
現在在同一個專案目錄建立一個 `Dockerfile` 檔案,內容如下:
```{ .dockerfile .annotate }
# (1)!
FROM python:3.14
# (2)!
WORKDIR /code
# (3)!
COPY ./requirements.txt /code/requirements.txt
# (4)!
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
# (5)!
COPY ./app /code/app
# (6)!
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
```
1. 從官方的 Python 基底映像開始。
2. 將目前工作目錄設為 `/code`。
我們會把 `requirements.txt` 檔案與 `app` 目錄放在這裡。
3. 將需求檔案複製到 `/code` 目錄。
先只複製需求檔案,不要複製其他程式碼。
因為這個檔案不常變動,Docker 能偵測並在此步驟使用快取,也能啟用下一步的快取。
4. 安裝需求檔案中的套件相依。
`--no-cache-dir` 選項告訴 `pip` 不要把下載的套件保存在本機,因為那只在 `pip` 之後還會再次安裝相同套件時才有用,而在使用容器時並非如此。
/// note | 注意
`--no-cache-dir` 只跟 `pip` 有關,與 Docker 或容器無關。
///
`--upgrade` 選項告訴 `pip` 若套件已安裝則升級它們。
因為前一步複製檔案可能被 Docker 快取偵測到,這一步也會在可用時使用 Docker 快取。
在此步驟使用快取可以在開發期間反覆建置映像時,為你省下大量時間,而不必每次都重新下載並安裝所有相依。
5. 將 `./app` 目錄複製到 `/code` 目錄中。
由於這包含了所有程式碼,也是最常變動的部分,Docker 的快取在這一步或之後的步驟將不容易被使用。
因此,重要的是把這一步放在 `Dockerfile` 的接近結尾處,以最佳化容器映像的建置時間。
6. 設定指令使用 `fastapi run`,其底層使用 Uvicorn。
`CMD` 接受字串清單,每個字串對應你在命令列中用空白分隔所輸入的內容。
這個指令會從目前的工作目錄執行,也就是你先前用 `WORKDIR /code` 設定的 `/code` 目錄。
/// tip | 提示
點擊程式碼中的每個數字泡泡來查看每一行在做什麼。👆
///
/// warning | 警告
務必「總是」使用 `CMD` 指令的「exec 形式」,如下所述。
///
#### 使用 `CMD` 的 Exec 形式 { #use-cmd-exec-form }
Docker 的 <a href="https://docs.docker.com/reference/dockerfile/#cmd" class="external-link" target="_blank">`CMD`</a> 指令可以有兩種寫法:
✅ Exec 形式:
```Dockerfile
# ✅ 請這樣做
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
```
⛔️ Shell 形式:
```Dockerfile
# ⛔️ 請不要這樣做
CMD fastapi run app/main.py --port 80
```
務必總是使用 exec 形式,以確保 FastAPI 能夠優雅地關閉,並觸發 [lifespan events](../advanced/events.md){.internal-link target=_blank}。
你可以在 <a href="https://docs.docker.com/reference/dockerfile/#shell-and-exec-form" class="external-link" target="_blank">Docker 關於 shell 與 exec 形式的文件</a>閱讀更多。
使用 `docker compose` 時這會特別明顯。技術細節請見這段 Docker Compose 常見問題:<a href="https://docs.docker.com/compose/faq/#why-do-my-services-take-10-seconds-to-recreate-or-stop" class="external-link" target="_blank">為什麼我的服務要花 10 秒才重新建立或停止?</a>
#### 目錄結構 { #directory-structure }
你現在應該會有如下的目錄結構:
```
.
├── app
│   ├── __init__.py
│ └── main.py
├── Dockerfile
└── requirements.txt
```
#### 位於 TLS 終止代理之後 { #behind-a-tls-termination-proxy }
如果你在 TLS 終止代理(負載平衡器)如 Nginx 或 Traefik 之後執行容器,請加上 `--proxy-headers` 選項,這會告訴 Uvicorn(透過 FastAPI CLI)信任該代理所送來的標頭,表示應用在 HTTPS 後方執行等。
```Dockerfile
CMD ["fastapi", "run", "app/main.py", "--proxy-headers", "--port", "80"]
```
#### Docker 快取 { #docker-cache }
這個 `Dockerfile` 中有個重要技巧:我們先只複製「相依檔案」,而不是其他程式碼。原因如下。
```Dockerfile
COPY ./requirements.txt /code/requirements.txt
```
Docker 與其他工具會「增量式」建置容器映像,從 `Dockerfile` 頂端開始,逐層加入,每個指令所建立的檔案都會形成一層。
Docker 與類似工具在建置映像時也會使用內部快取;如果某檔案自上次建置以來未變更,則會重用上次建立的同一層,而不是再次複製並從零建立新層。
僅僅避免複製檔案本身或許幫助不大,但因為該步驟使用了快取,接下來的步驟也就能「使用快取」。例如,安裝相依的這個指令就能使用快取:
```Dockerfile
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
```
套件相依的檔案「不會經常變動」。因此,只複製該檔案,Docker 就能在此步驟「使用快取」。
接著,Docker 也就能對下一步「下載並安裝這些相依」使用快取。這正是我們能「省下大量時間」的地方。✨ 也能避免無聊的等待。😪😆
下載與安裝套件相依「可能要花好幾分鐘」,但使用「快取」最多只需幾秒。
在開發期間,你會一再建置容器映像以測試程式碼變更是否生效,累積下來這能省下許多時間。
之後,在 `Dockerfile` 的接近結尾處,我們才複製所有程式碼。由於這是「最常變動」的部分,我們把它放在接近結尾,因為幾乎總是此步驟之後的任何步驟都無法使用快取。
```Dockerfile
COPY ./app /code/app
```
### 建置 Docker 映像 { #build-the-docker-image }
現在所有檔案就緒,來建置容器映像。
* 進到專案目錄(你的 `Dockerfile` 所在,且包含 `app` 目錄)。
* 建置你的 FastAPI 映像:
<div class="termy">
```console
$ docker build -t myimage .
---> 100%
```
</div>
/// tip | 提示
注意最後的 `.`,等同於 `./`,它告訴 Docker 要用哪個目錄來建置容器映像。
這裡是目前的目錄(`.`)。
///
### 啟動 Docker 容器 { #start-the-docker-container }
* 以你的映像為基礎執行一個容器:
<div class="termy">
```console
$ docker run -d --name mycontainer -p 80:80 myimage
```
</div>
## 檢查 { #check-it }
你應該可以透過 Docker 容器的網址檢查,例如:<a href="http://192.168.99.100/items/5?q=somequery" class="external-link" target="_blank">http://192.168.99.100/items/5?q=somequery</a> 或 <a href="http://127.0.0.1/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1/items/5?q=somequery</a>(或等效的、使用你的 Docker 主機)。
你會看到類似這樣:
```JSON
{"item_id": 5, "q": "somequery"}
```
## 互動式 API 文件 { #interactive-api-docs }
現在你可以前往 <a href="http://192.168.99.100/docs" class="external-link" target="_blank">http://192.168.99.100/docs</a> 或 <a href="http://127.0.0.1/docs" class="external-link" target="_blank">http://127.0.0.1/docs</a>(或等效的、使用你的 Docker 主機)。
你會看到自動產生的互動式 API 文件(由 <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a> 提供):
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png)
## 替代的 API 文件 { #alternative-api-docs }
你也可以前往 <a href="http://192.168.99.100/redoc" class="external-link" target="_blank">http://192.168.99.100/redoc</a> 或 <a href="http://127.0.0.1/redoc" class="external-link" target="_blank">http://127.0.0.1/redoc</a>(或等效的、使用你的 Docker 主機)。
你會看到另一種自動產生的文件(由 <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a> 提供):
![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png)
## 為單檔 FastAPI 建置 Docker 映像 { #build-a-docker-image-with-a-single-file-fastapi }
如果你的 FastAPI 是單一檔案,例如沒有 `./app` 目錄的 `main.py`,你的檔案結構可能像這樣:
```
.
├── Dockerfile
├── main.py
└── requirements.txt
```
接著你只需要在 `Dockerfile` 中調整對應的路徑以複製該檔案:
```{ .dockerfile .annotate hl_lines="10 13" }
FROM python:3.14
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
# (1)!
COPY ./main.py /code/
# (2)!
CMD ["fastapi", "run", "main.py", "--port", "80"]
```
1. 將 `main.py` 直接複製到 `/code` 目錄(不需要 `./app` 目錄)。
2. 使用 `fastapi run` 來服務單檔的 `main.py` 應用。
當你把檔案傳給 `fastapi run`,它會自動偵測這是一個單一檔案而非套件的一部分,並知道如何匯入並服務你的 FastAPI 應用。😎
## 部署概念 { #deployment-concepts }
我們用容器的角度再談一次部分相同的[部署概念](concepts.md){.internal-link target=_blank}。
容器主要是簡化應用「建置與部署」流程的工具,但它們不強制特定的方式來處理這些「部署概念」,而是有多種策略可選。
好消息是,每種不同的策略都能涵蓋所有部署概念。🎉
讓我們用容器的角度回顧這些部署概念:
* HTTPS
* 開機自動執行
* 失敗重啟
* 複本(執行的行程數量)
* 記憶體
* 啟動前的前置步驟
## HTTPS { #https }
若僅聚焦於 FastAPI 應用的「容器映像」(以及稍後的執行中「容器」),HTTPS 通常會由另一個工具在「外部」處理。
它可以是另一個容器,例如使用 <a href="https://traefik.io/" class="external-link" target="_blank">Traefik</a>,來處理「HTTPS」以及「自動」取得「憑證」。
/// tip | 提示
Traefik 與 Docker、Kubernetes 等整合良好,因此為你的容器設定與配置 HTTPS 非常容易。
///
或者,HTTPS 也可能由雲端供應商以其服務來處理(同時應用仍以容器執行)。
## 開機自動執行與重啟 { #running-on-startup-and-restarts }
通常會有另一個工具負責「啟動並執行」你的容器。
可能是直接用 Docker、Docker Compose、Kubernetes、某個雲端服務等。
在大多數(或全部)情況下,都有簡單的選項可以在開機時自動執行容器,並在失敗時重啟。例如,在 Docker 中,可用命令列選項 `--restart`。
如果不使用容器,讓應用在開機時自動執行並支援重啟可能既繁瑣又困難。但在「使用容器」時,這類功能在多數情況下都是預設包含的。✨
## 複本 - 行程數量 { #replication-number-of-processes }
如果你在有 Kubernetes、Docker Swarm Mode、Nomad,或其他類似的分散式容器管理系統的「叢集」上運作,那你大概會希望在「叢集層級」處理「複本」,而不是在每個容器內使用「行程管理器」(例如帶有 workers 的 Uvicorn)。
像 Kubernetes 這類的分散式容器管理系統,通常內建處理「容器複本」以及支援進入請求的「負載平衡」的能力——全部都在「叢集層級」。
在這些情況下,你大概會想要如[上面所述](#dockerfile)從零開始建置一個 Docker 映像,安裝你的相依,並且只執行「單一 Uvicorn 行程」,而不是使用多個 Uvicorn workers。
### 負載平衡器 { #load-balancer }
使用容器時,通常會有某個元件在「主埠口」上監聽。它也可能是另一個同時做為「TLS 終止代理」的容器來處理「HTTPS」,或類似的工具。
由於這個元件會承接請求的「負載」,並將其分配給 workers,使其(希望)「平衡」,因此也常被稱為「負載平衡器(Load Balancer)」。
/// tip | 提示
用於 HTTPS 的同一個「TLS 終止代理」元件通常也會是「負載平衡器」。
///
而在使用容器時,你用來啓動與管理它們的系統,已內建把「網路通訊」(例如 HTTP 請求)從該「負載平衡器」(也可能是「TLS 終止代理」)傳遞到你的應用容器的工具。
### 一個負載平衡器 - 多個工作容器 { #one-load-balancer-multiple-worker-containers }
使用 Kubernetes 或類似的分散式容器管理系統時,使用其內部網路機制可以讓在主「埠口」上監聽的單一「負載平衡器」,把通訊(請求)傳遞給可能的「多個執行你應用的容器」。
每個執行你應用的容器通常只有「單一行程」(例如執行你的 FastAPI 應用的 Uvicorn 行程)。它們都是「相同的容器」,執行相同的東西,但各自擁有自己的行程、記憶體等。如此即可在 CPU 的「不同核心」、甚至是「不同機器」上發揮「平行化」的效益。
而分散式容器系統中的「負載平衡器」會「輪流」把請求分配給各個執行你應用的容器。因此,每個請求都可能由多個「複製的容器」中的其中一個來處理。
通常這個「負載平衡器」也能處理送往叢集中「其他」應用的請求(例如不同網域,或不同 URL 路徑前綴),並把通訊轉送到該「其他」應用對應的容器。
### 每個容器一個行程 { #one-process-per-container }
在這種情境中,你大概會希望「每個容器只有一個(Uvicorn)行程」,因為你已在叢集層級處理了複本。
所以這種情況下,你「不會」想在容器中使用多個 workers(例如用 `--workers` 命令列選項)。你會想每個容器只執行「一個 Uvicorn 行程」(但可能有多個容器)。
在容器內再放一個行程管理器(如同多 workers 的情況)只會增加「不必要的複雜度」,而你很可能已用叢集系統處理好了。
### 多行程容器與特殊情境 { #containers-with-multiple-processes-and-special-cases }
當然,有些「特殊情況」你可能會想在「一個容器內」執行數個「Uvicorn worker 行程」。
在這些情況中,你可以用 `--workers` 命令列選項來設定要啟動的 workers 數量:
```{ .dockerfile .annotate }
FROM python:3.14
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
COPY ./app /code/app
# (1)!
CMD ["fastapi", "run", "app/main.py", "--port", "80", "--workers", "4"]
```
1. 這裡我們使用 `--workers` 命令列選項把 worker 數量設定為 4。
以下是一些合理的例子:
#### 簡單應用 { #a-simple-app }
如果你的應用「足夠簡單」,可以在「單一伺服器」而非叢集上執行,你可能會希望在容器內使用行程管理器。
#### Docker Compose { #docker-compose }
如果你部署到「單一伺服器」(非叢集),且使用「Docker Compose」,那麼你無法輕易(用 Docker Compose)在保有共用網路與「負載平衡」的同時管理容器複本。
那你可能會想要「單一容器」搭配「行程管理器」,在其中啟動「多個 worker 行程」。
---
重點是,這些「都不是」必須盲目遵守的「鐵律」。你可以用這些想法來「評估你的使用情境」,並決定對你的系統最好的做法,看看如何管理以下概念:
* 安全性 - HTTPS
* 開機自動執行
* 失敗重啟
* 複本(執行的行程數量)
* 記憶體
* 啟動前的前置步驟
## 記憶體 { #memory }
如果你採用「每個容器一個行程」,那每個容器(若有複本則多個容器)所消耗的記憶體會是相對明確、穩定且有限的。
接著你可以在容器管理系統(例如 Kubernetes)的設定中為容器設定相同的記憶體限制與需求。如此,它就能在「可用的機器」上「複製容器」,並考量容器所需的記憶體量與叢集中機器的可用記憶體。
若你的應用「很簡單」,這可能「不是問題」,你可能不需要指定嚴格的記憶體限制。但如果你「使用大量記憶體」(例如使用機器學習模型),你應該檢查實際消耗的記憶體,並調整「每台機器上執行的容器數量」(也許還要為叢集加機器)。
若你採用「每個容器多個行程」,你就得確保啟動的行程數量不會「超過可用記憶體」。
## 啟動前的前置步驟與容器 { #previous-steps-before-starting-and-containers }
如果你使用容器(例如 Docker、Kubernetes),那有兩種主要做法可用。
### 多個容器 { #multiple-containers }
如果你有「多個容器」,且每個容器大概都只執行「單一行程」(例如在一個 Kubernetes 叢集中),那你可能會想要一個「獨立的容器」來完成「前置步驟」的工作,並只在單一容器、單一行程中執行,接著才啟動多個複本的工作容器。
/// info | 資訊
如果你使用 Kubernetes,這大概會是一個 <a href="https://kubernetes.io/docs/concepts/workloads/pods/init-containers/" class="external-link" target="_blank">Init Container</a>。
///
如果你的情境中,讓那些前置步驟「平行重複執行多次」沒有問題(例如不是在跑資料庫遷移,而只是檢查資料庫是否就緒),那也可以把這些步驟放在每個容器中、在啟動主要行程前執行。
### 單一容器 { #single-container }
如果你的架構很簡單,只有「單一容器」,接著在其中啟動多個「worker 行程」(或也可能就一個行程),那你可以在相同的容器中、於啟動應用行程前先執行這些前置步驟。
### 基底 Docker 映像 { #base-docker-image }
曾經有一個官方的 FastAPI Docker 映像:<a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>。但現在已被棄用。⛔️
你大概「不應該」使用這個基底 Docker 映像(或其他類似的)。
如果你使用 Kubernetes(或其他)並已在叢集層級設定「複本」、使用多個「容器」。在這些情況下,更好的做法是如上所述[從零建置映像](#build-a-docker-image-for-fastapi)。
若你需要多個 workers,只要使用 `--workers` 命令列選項即可。
/// note | 技術細節
這個 Docker 映像是在 Uvicorn 尚未支援管理與重啟死亡 workers 的年代所建立,因此需要用 Gunicorn 搭配 Uvicorn,為了讓 Gunicorn 管理並重啟 Uvicorn workers,而引入了相當多的複雜度。
但現在 Uvicorn(以及 `fastapi` 指令)已支援使用 `--workers`,因此沒有理由使用一個基底 Docker 映像,而不是建置你自己的(而且實際上程式碼量也差不多 😅)。
///
## 部署容器映像 { #deploy-the-container-image }
擁有容器(Docker)映像後,有多種部署方式。
例如:
* 在單一伺服器上使用 Docker Compose
* 使用 Kubernetes 叢集
* 使用 Docker Swarm Mode 叢集
* 使用像 Nomad 之類的其他工具
* 使用會接收你的容器映像並代為部署的雲端服務
## 使用 `uv` 的 Docker 映像 { #docker-image-with-uv }
如果你使用 <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">uv</a> 來安裝與管理專案,你可以參考他們的 <a href="https://docs.astral.sh/uv/guides/integration/docker/" class="external-link" target="_blank">uv Docker 指南</a>。
## 總結 { #recap }
使用容器系統(例如 Docker 與 Kubernetes)可以相對直接地處理所有「部署概念」:
* HTTPS
* 開機自動執行
* 失敗重啟
* 複本(執行的行程數量)
* 記憶體
* 啟動前的前置步驟
多數情況下,你大概不會想用任何基底映像,而是「從零建置容器映像」,以官方的 Python Docker 映像為基礎。
善用 `Dockerfile` 中指令的「順序」與「Docker 快取」,你可以「最小化建置時間」,提升生產力(並避免無聊)。😎
@@ -0,0 +1,65 @@
# FastAPI Cloud { #fastapi-cloud }
你可以用「一行指令」把你的 FastAPI 應用程式部署到 <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>。如果你還沒加入,快去登記等候名單吧!🚀
## 登入 { #login }
請先確認你已經有 **FastAPI Cloud** 帳號(我們已從等候名單邀請你 😉)。
然後登入:
<div class="termy">
```console
$ fastapi login
You are logged in to FastAPI Cloud 🚀
```
</div>
## 部署 { #deploy }
現在用「一行指令」部署你的應用:
<div class="termy">
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
</div>
就這樣!現在你可以透過該 URL 造訪你的應用。✨
## 關於 FastAPI Cloud { #about-fastapi-cloud }
**<a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>** 由 **FastAPI** 的作者與團隊打造。
它以最少的心力,精簡化建立、部署與存取 API 的流程。
它把使用 FastAPI 開發應用的優異開發體驗,延伸到將它們部署到雲端。🎉
它也會為你處理部署應用時多數需要面對的事項,例如:
* HTTPS
* 多副本,並可依據請求自動擴縮
* 等等。
FastAPI Cloud 是 *FastAPI and friends* 開源專案的主要贊助者與資金提供者。✨
## 部署到其他雲端供應商 { #deploy-to-other-cloud-providers }
FastAPI 是基於標準的開源專案。你可以把 FastAPI 應用部署到你選擇的任何雲端供應商。
請依照你的雲端供應商的指南,使用他們的方式部署 FastAPI 應用。🤓
## 部署到你自己的伺服器 { #deploy-your-own-server }
在這份「部署」指南的後續內容中,我也會教你所有細節,讓你了解背後發生了什麼、需要做哪些事,以及如何自行部署 FastAPI 應用,包括在你自己的伺服器上。🤓
+231
View File
@@ -0,0 +1,231 @@
# 關於 HTTPS { #about-https }
人們很容易以為 HTTPS 只是「啟用或未啟用」的功能。
但實際上複雜得多。
/// tip
如果你趕時間或不在意細節,可以直接看後續章節,依照逐步指引用不同方式完成設定。
///
想從使用者角度學習 HTTPS 基礎,請參考 <a href="https://howhttps.works/" class="external-link" target="_blank">https://howhttps.works/</a>。
接著以開發者角度,談幾個關於 HTTPS 需要注意的重點:
* 對於 HTTPS,伺服器需要擁有由**第三方**簽發的**「憑證」**。
* 這些憑證實際上是向第三方**取得**,不是「自己產生」。
* 憑證有**有效期**。
* 會**過期**。
* 過期後需要**續期**,也就是再向第三方**重新取得**。
* 連線加密發生在 **TCP 層**
* 那是在 **HTTP 的下一層**
* 因此,**憑證與加密**的處理會在 **進入 HTTP 之前**完成。
* **TCP 不知道「網域」**,只知道 IP 位址。
* 關於**特定網域**的資訊會放在 **HTTP 資料**中。
* **HTTPS 憑證**是為某個**特定網域**背書,但通訊協定與加密在 TCP 層進行,發生在**尚未知道**要處理哪個網域之前。
* **預設**情況下,這表示你每個 IP 位址**只能**使用**一張 HTTPS 憑證**。
* 不論你的伺服器多強或你在上面跑的應用多小。
* 不過,這是有**解法**的。
***TLS** 協定(在 HTTP 之前於 TCP 層處理加密的協定)上有個**擴充**稱為 **<a href="https://en.wikipedia.org/wiki/Server_Name_Indication" class="external-link" target="_blank"><abbr title="Server Name Indication - 伺服器名稱指示">SNI</abbr></a>**。
* 這個 SNI 擴充讓單一伺服器(單一 IP 位址)可以擁有**多張 HTTPS 憑證**,並服務**多個 HTTPS 網域/應用**。
* 要讓它運作,伺服器上必須有一個**單一**的元件(程式)在**公用 IP**上監聽,且持有伺服器上的**所有 HTTPS 憑證**。
* 在取得安全連線**之後**,通訊協定**仍然是 HTTP**。
* 雖然透過 **HTTP 協定**傳送,但內容是**加密**的。
常見做法是讓伺服器(機器、主機等)上跑**一個程式/HTTP 伺服器**來**管理所有 HTTPS 相關工作**:接收**加密的 HTTPS 請求**、將其**解密**成**純 HTTP 請求**轉交給同台伺服器上實際運行的 HTTP 應用(本例為 **FastAPI** 應用)、從應用取得 **HTTP 回應**、再用合適的 **HTTPS 憑證**將其**加密**並以 **HTTPS** 傳回給用戶端。這類伺服器常被稱為 **<a href="https://en.wikipedia.org/wiki/TLS_termination_proxy" class="external-link" target="_blank">TLS 終止代理 (TLS Termination Proxy)</a>**。
可作為 TLS 終止代理的選項包括:
* Traefik(也可處理憑證續期)
* Caddy(也可處理憑證續期)
* Nginx
* HAProxy
## Let's Encrypt { #lets-encrypt }
在 Let's Encrypt 之前,這些 **HTTPS 憑證**是由受信任的第三方販售。
取得這些憑證的流程過去相當繁瑣,需要許多手續,且憑證相當昂貴。
之後出現了 **<a href="https://letsencrypt.org/" class="external-link" target="_blank">Let's Encrypt</a>**。
它是 Linux Foundation 的專案,能**免費**且自動化地提供 **HTTPS 憑證**。這些憑證採用標準的密碼學安全機制,且有效期較短(約 3 個月),因此因為壽命短,**安全性其實更好**。
網域會被安全驗證,憑證會自動產生。這也讓憑證續期得以自動化。
目標是讓憑證的申請與續期自動化,讓你**永遠免費使用安全的 HTTPS**。
## 給開發者的 HTTPS { #https-for-developers }
以下以逐步範例說明一個 HTTPS API 可能長什麼樣子,著重於對開發者重要的概念。
### 網域名稱 { #domain-name }
通常會先**取得**一個**網域名稱**,接著在 DNS 伺服器(可能是同一個雲端供應商)中設定它。
你可能會租一台雲端伺服器(虛擬機)或類似的服務,並擁有一個<dfn title="不會隨時間改變;非動態的">固定</dfn>的**公用 IP 位址**。
在 DNS 伺服器中,你會設定一個紀錄(「`A record`」)指向**你的網域**所對應的**伺服器公用 IP 位址**。
這通常在初次建置時設定一次即可。
/// tip
「網域名稱」是發生在 HTTPS 之前的事情,但一切都依賴網域與 IP 位址,因此在此一併說明。
///
### DNS { #dns }
現在聚焦在實際的 HTTPS 部分。
首先,瀏覽器會向 **DNS 伺服器**查詢該**網域的 IP**,例如 `someapp.example.com`
DNS 伺服器會回覆要使用的**IP 位址**,那就是你在 DNS 伺服器中設定的、伺服器對外的公用 IP 位址。
<img src="/img/deployment/https/https01.drawio.svg">
### TLS 握手開始 { #tls-handshake-start }
接著瀏覽器會連線到該 IP 的 **443 埠**HTTPS 預設埠)。
通訊的第一部分是建立用戶端與伺服器之間的連線,並協商要使用哪些金鑰等密碼參數。
<img src="/img/deployment/https/https02.drawio.svg">
用戶端與伺服器為建立 TLS 連線而進行的這段互動稱為 **TLS 握手**
### 帶 SNI 擴充的 TLS { #tls-with-sni-extension }
在特定的**IP 位址**與特定**埠**上,同一時間**只能有一個行程**在監聽。可以在同一個 IP 上監聽不同埠,但每個 IP 與埠的組合只能有一個行程。
TLSHTTPS)預設使用 `443` 埠,因此我們需要用到這個埠。
由於只能有一個行程監聽該埠,負責監聽的會是 **TLS 終止代理**
TLS 終止代理會存取一張或多張 **TLS 憑證**HTTPS 憑證)。
透過上面提到的 **SNI 擴充**,TLS 終止代理會根據用戶端預期的網域,從可用的 TLS(HTTPS)憑證中挑選本次連線要用的憑證。
在這個例子中,會使用 `someapp.example.com` 的憑證。
<img src="/img/deployment/https/https03.drawio.svg">
用戶端**信任**簽發該 TLS 憑證的單位(本例為 Let's Encrypt,稍後會再談),因此可以**驗證**憑證有效。
接著,用戶端與 TLS 終止代理會以該憑證為基礎,**協商後續如何加密**整段 **TCP 通訊**。至此完成 **TLS 握手**
之後,用戶端與伺服器之間就有一條**已加密的 TCP 連線**,這就是 TLS 所提供的能力。接著他們可以在這條連線上開始實際的 **HTTP** 通訊。
這也就是 **HTTPS** 的本質:在**安全的 TLS 連線**內傳送一般的 **HTTP**,而非在純(未加密)的 TCP 連線上。
/// tip
請注意,加密發生在 **TCP 層**,不是在 HTTP 層。
///
### HTTPS 請求 { #https-request }
現在用戶端與伺服器(更精確地說,是瀏覽器與 TLS 終止代理)之間已有**加密的 TCP 連線**,他們可以開始進行 **HTTP** 通訊。
因此,用戶端送出一個 **HTTPS 請求**。它其實就是透過加密的 TLS 連線發送的一個 HTTP 請求。
<img src="/img/deployment/https/https04.drawio.svg">
### 解密請求 { #decrypt-the-request }
TLS 終止代理會依照先前協商的方式**解密請求**,並將**純(已解密)的 HTTP 請求**轉交給運行應用的行程(例如以 Uvicorn 執行的 FastAPI 應用行程)。
<img src="/img/deployment/https/https05.drawio.svg">
### HTTP 回應 { #http-response }
應用會處理該請求,並將**純(未加密)的 HTTP 回應**送回 TLS 終止代理。
<img src="/img/deployment/https/https06.drawio.svg">
### HTTPS 回應 { #https-response }
TLS 終止代理接著會依照先前協商(起點是 `someapp.example.com` 的憑證)的方式**加密回應**,並傳回給瀏覽器。
接著,瀏覽器會驗證回應是否合法、是否使用正確的金鑰加密等。然後**解密回應**並處理。
<img src="/img/deployment/https/https07.drawio.svg">
用戶端(瀏覽器)會知道回應來自正確的伺服器,因為它使用了先前依據 **HTTPS 憑證**所協商的密碼機制。
### 多個應用 { #multiple-applications }
同一台(或多台)伺服器上可以有**多個應用**,例如其他 API 程式或資料庫。
雖然只有一個行程可以處理特定 IP 與埠的組合(本例中的 TLS 終止代理),但其他應用/行程也都能在伺服器上運行,只要它們不使用相同的**公用 IP 與埠**組合即可。
<img src="/img/deployment/https/https08.drawio.svg">
如此一來,TLS 終止代理就能為**多個網域**、多個應用處理 HTTPS 與憑證,並把請求轉發到對應的應用。
### 憑證續期 { #certificate-renewal }
在未來某個時間點,每張憑證都會**過期**(自取得起約 3 個月)。
之後,會有另一個程式(有時是另一個程式,有時也可能就是同一個 TLS 終止代理)與 Let's Encrypt 溝通並續期憑證。
<img src="/img/deployment/https/https.drawio.svg">
**TLS 憑證**是**綁定網域名稱**,不是綁定 IP 位址。
因此,要續期憑證時,續期程式需要向憑證機構(Let's Encrypt**證明**它的確**擁有並控制該網域**。
為了達成這點、並兼顧不同應用情境,有幾種常見方式:
* **修改部分 DNS 紀錄**。
* 為此,續期程式需要支援該 DNS 供應商的 API,因此依你使用的 DNS 供應商不同,這方式可能可行或不可行。
* **作為伺服器運行**(至少在憑證申請過程中)於該網域對應的公用 IP 上。
* 如前所述,同一時間只有一個行程能在特定 IP 與埠上監聽。
* 這也是為什麼讓同一個 TLS 終止代理一併處理憑證續期非常實用的原因之一。
* 否則你可能得暫停 TLS 終止代理、啟動續期程式申請憑證、將憑證配置到 TLS 終止代理,然後再重啟 TLS 終止代理。這並不理想,因為在 TLS 終止代理停機期間,你的應用將不可用。
在不中斷服務的同時完成整個續期流程,是你會想用**獨立系統(TLS 終止代理)來處理 HTTPS**、而不是直接把 TLS 憑證掛在應用伺服器(例如 Uvicorn)上的主要原因之一。
## 代理轉發標頭 { #proxy-forwarded-headers }
當你使用代理處理 HTTPS 時,你的**應用伺服器**(例如透過 FastAPI CLI 啟動的 Uvicorn)其實不知道任何 HTTPS 的處理流程,它是用純 HTTP 與 **TLS 終止代理**通訊。
這個**代理**通常會在把請求轉發給**應用伺服器**之前,臨時加入一些 HTTP 標頭,讓應用伺服器知道該請求是由代理**轉發**過來的。
/// note | 技術細節
這些代理標頭包括:
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For" class="external-link" target="_blank">X-Forwarded-For</a>
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto" class="external-link" target="_blank">X-Forwarded-Proto</a>
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host" class="external-link" target="_blank">X-Forwarded-Host</a>
///
然而,因為**應用伺服器**並不知道自己在受信任的**代理**之後,預設情況下它不會信任這些標頭。
但你可以設定**應用伺服器**去信任由**代理**送來的「轉發」標頭。若你使用 FastAPI CLI,可以用 *CLI 參數* `--forwarded-allow-ips` 指定應信任哪些 IP 來的「轉發」標頭。
例如,如果**應用伺服器**只會接收來自受信任**代理**的連線,你可以設定 `--forwarded-allow-ips="*"`,也就是信任所有來源 IP,因為實際上它只會收到**代理**那個 IP 送來的請求。
如此一來,應用就能知道自己的對外 URL、是否使用 HTTPS、網域為何等資訊。
這在正確處理重新導向等情境時很有用。
/// tip
你可以在文件 [在代理後方 - 啟用代理轉發標頭](../advanced/behind-a-proxy.md#enable-proxy-forwarded-headers){.internal-link target=_blank} 中了解更多。
///
## 重點回顧 { #recap }
擁有 **HTTPS** 非常重要,而且在多數情況都相當**關鍵**。作為開發者,你在 HTTPS 上的大部分投入其實是**理解這些概念**及其運作方式。
一旦掌握了**給開發者的 HTTPS 基礎**,你就能輕鬆組合並設定不同工具,讓一切管理變得簡單。
在接下來的章節中,我會示範幾個為 **FastAPI** 應用設定 **HTTPS** 的具體例子。🔒
+6 -4
View File
@@ -1,21 +1,23 @@
# 部署
# 部署 { #deployment }
部署 **FastAPI** 應用程式相對容易。
## 部署是什麼意思
## 部署是什麼意思 { #what-does-deployment-mean }
**部署**應用程式指的是執行一系列必要的步驟,使其能夠**讓使用者存取和使用**
對於一個 **Web API**,部署通常涉及將其放置在**遠端伺服器**上,並使用性能優良且穩定的**伺服器程式**,確保使用者能夠高效、無中斷地存取應用程式,且不會遇到問題。
這與**開發**階段形成鮮明對比,在**開發**階段,你會不斷更改程式碼、破壞程式碼、修復程式碼,然後停止和重新啟動伺服器等。
這與**開發**階段形成鮮明對比,在**開發**階段,你會不斷更改程式碼、破壞程式碼、修復程式碼,然後停止和重新啟動開發伺服器等。
## 部署策略
## 部署策略 { #deployment-strategies }
根據你的使用場景和使用工具,有多種方法可以實現此目的。
你可以使用一些工具自行**部署伺服器**,你也可以使用能為你完成部分工作的**雲端服務**,或其他可能的選項。
例如,我們(FastAPI 的團隊)打造了 <a href="https://fastapicloud.com" class="external-link" target="_blank">**FastAPI Cloud**</a>,讓將 FastAPI 應用程式部署到雲端變得盡可能流暢,並保持與使用 FastAPI 開發時相同的開發者體驗。
我將向你展示在部署 **FastAPI** 應用程式時你可能應該記住的一些主要概念(儘管其中大部分適用於任何其他類型的 Web 應用程式)。
在接下來的部分中,你將看到更多需要記住的細節以及一些技巧。 ✨
+157
View File
@@ -0,0 +1,157 @@
# 手動執行伺服器 { #run-a-server-manually }
## 使用 `fastapi run` 指令 { #use-the-fastapi-run-command }
簡而言之,使用 `fastapi run` 來啟動你的 FastAPI 應用:
<div class="termy">
```console
$ <font color="#4E9A06">fastapi</font> run <u style="text-decoration-style:solid">main.py</u>
<span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> Starting production server 🚀
Searching for package file structure from directories
with <font color="#3465A4">__init__.py</font> files
Importing from <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
<span style="background-color:#007166"><font color="#D3D7CF"> module </font></span> 🐍 main.py
<span style="background-color:#007166"><font color="#D3D7CF"> code </font></span> Importing the FastAPI app object from the module with
the following code:
<u style="text-decoration-style:solid">from </u><u style="text-decoration-style:solid"><b>main</b></u><u style="text-decoration-style:solid"> import </u><u style="text-decoration-style:solid"><b>app</b></u>
<span style="background-color:#007166"><font color="#D3D7CF"> app </font></span> Using import string: <font color="#3465A4">main:app</font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Server started at <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000</u></font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Documentation at <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000/docs</u></font>
Logs:
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>2306215</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Uvicorn running on <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000</u></font> <b>(</b>Press CTRL+C
to quit<b>)</b>
```
</div>
這在多數情況下都適用。😎
你可以用這個指令在容器、伺服器等環境中啟動你的 FastAPI 應用。
## ASGI 伺服器 { #asgi-servers }
我們再深入一些細節。
FastAPI 採用建立 Python 網頁框架與伺服器的標準 <abbr title="Asynchronous Server Gateway Interface - 非同步伺服器閘道介面">ASGI</abbr>。FastAPI 是一個 ASGI 網頁框架。
在遠端伺服器機器上執行 FastAPI 應用(或任何 ASGI 應用)所需的關鍵是 ASGI 伺服器程式,例如 Uvicorn`fastapi` 指令預設就是使用它。
有數個替代方案,包括:
* <a href="https://www.uvicorn.dev/" class="external-link" target="_blank">Uvicorn</a>:高效能 ASGI 伺服器。
* <a href="https://hypercorn.readthedocs.io/" class="external-link" target="_blank">Hypercorn</a>:支援 HTTP/2 與 Trio 等功能的 ASGI 伺服器。
* <a href="https://github.com/django/daphne" class="external-link" target="_blank">Daphne</a>:為 Django Channels 打造的 ASGI 伺服器。
* <a href="https://github.com/emmett-framework/granian" class="external-link" target="_blank">Granian</a>:針對 Python 應用的 Rust HTTP 伺服器。
* <a href="https://unit.nginx.org/howto/fastapi/" class="external-link" target="_blank">NGINX Unit</a>NGINX Unit 是輕量且多功能的網頁應用執行環境。
## 伺服器機器與伺服器程式 { #server-machine-and-server-program }
有個命名上的小細節請留意。💡
「server(伺服器)」一詞常同時用來指遠端/雲端電腦(實體或虛擬機器),也用來指在該機器上執行的程式(例如 Uvicorn)。
因此看到「server」時,文意可能指這兩者之一。
指涉遠端機器時,常稱為 server、machine、VM(虛擬機器)、node 等,這些都指某種遠端機器(通常執行 Linux),你會在其上執行程式。
## 安裝伺服器程式 { #install-the-server-program }
安裝 FastAPI 時會附帶一個可用於生產環境的伺服器 Uvicorn,你可以用 `fastapi run` 來啟動它。
但你也可以手動安裝 ASGI 伺服器。
請先建立並啟用一個 [虛擬環境](../virtual-environments.md){.internal-link target=_blank},接著再安裝伺服器程式。
例如,安裝 Uvicorn
<div class="termy">
```console
$ pip install "uvicorn[standard]"
---> 100%
```
</div>
其他 ASGI 伺服器的安裝流程也大致相同。
/// tip
加入 `standard` 會讓 Uvicorn 安裝並使用一些建議的額外相依套件。
其中包含 `uvloop`,它是 `asyncio` 的高效能替代實作,可大幅提升並行效能。
當你用 `pip install "fastapi[standard]"` 安裝 FastAPI 時,也會一併取得 `uvicorn[standard]`
///
## 執行伺服器程式 { #run-the-server-program }
如果你是手動安裝 ASGI 伺服器,通常需要提供特定格式的 import 字串,讓它能匯入你的 FastAPI 應用:
<div class="termy">
```console
$ uvicorn main:app --host 0.0.0.0 --port 80
<span style="color: green;">INFO</span>: Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit)
```
</div>
/// note
指令 `uvicorn main:app` 指的是:
* `main`:檔案 `main.py`Python「模組」)。
* `app`:在 `main.py` 中以 `app = FastAPI()` 建立的物件。
等同於:
```Python
from main import app
```
///
其他 ASGI 伺服器也有類似的指令,詳見各自的文件。
/// warning
Uvicorn 與其他伺服器支援 `--reload` 選項,對開發期間很有幫助。
`--reload` 會消耗更多資源,也較不穩定等。
它在開發階段很實用,但在生產環境中不應使用。
///
## 部署觀念 { #deployment-concepts }
上述範例會啟動伺服器程式(如 Uvicorn),以單一行程在指定連接埠(如 `80`)上監聽所有 IP`0.0.0.0`)。
這是基本概念。但你很可能還需要處理一些額外事項,例如:
* 安全性 - HTTPS
* 開機自動啟動
* 自動重啟
* 多副本(執行的行程數量)
* 記憶體
* 啟動前需要執行的前置步驟
在下一章節我會進一步說明這些觀念、思考方式,以及對應的處理策略與實作範例。🚀
@@ -0,0 +1,139 @@
# 伺服器工作處理序 - 使用 Uvicorn Workers { #server-workers-uvicorn-with-workers }
我們回顧一下先前提到的部署概念:
* 安全 - HTTPS
* 系統啟動時執行
* 重啟
* **副本(正在執行的處理序數量)**
* 記憶體
* 啟動前的前置作業
到目前為止,依照文件中的教學,你大多是透過 `fastapi` 指令啟動一個執行 Uvicorn 的伺服器程式,且只跑單一處理序。
在部署應用時,你通常會希望有一些處理序的複製來善用多核心,並能處理更多請求。
如同前一章關於 [部署概念](concepts.md){.internal-link target=_blank} 所示,你可以採用多種策略。
這裡會示範如何使用 `fastapi` 指令或直接使用 `uvicorn` 指令,搭配 Uvicorn 的工作處理序(worker processes)。
/// info
如果你使用容器(例如 Docker 或 Kubernetes),我會在下一章說明更多:[容器中的 FastAPI - Docker](docker.md){.internal-link target=_blank}。
特別是,在 **Kubernetes** 上執行時,你多半會選擇不要使用 workers,而是每個容器只跑一個 **Uvicorn 單一處理序**。我會在該章節中進一步說明。
///
## 多個工作處理序 { #multiple-workers }
你可以用命令列選項 `--workers` 來啟動多個 workers
//// tab | `fastapi`
如果你使用 `fastapi` 指令:
<div class="termy">
```console
$ <font color="#4E9A06">fastapi</font> run --workers 4 <u style="text-decoration-style:solid">main.py</u>
<span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> Starting production server 🚀
Searching for package file structure from directories with
<font color="#3465A4">__init__.py</font> files
Importing from <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
<span style="background-color:#007166"><font color="#D3D7CF"> module </font></span> 🐍 main.py
<span style="background-color:#007166"><font color="#D3D7CF"> code </font></span> Importing the FastAPI app object from the module with the
following code:
<u style="text-decoration-style:solid">from </u><u style="text-decoration-style:solid"><b>main</b></u><u style="text-decoration-style:solid"> import </u><u style="text-decoration-style:solid"><b>app</b></u>
<span style="background-color:#007166"><font color="#D3D7CF"> app </font></span> Using import string: <font color="#3465A4">main:app</font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Server started at <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000</u></font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Documentation at <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000/docs</u></font>
Logs:
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Uvicorn running on <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000</u></font> <b>(</b>Press CTRL+C to
quit<b>)</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started parent process <b>[</b><font color="#34E2E2"><b>27365</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>27368</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>27369</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>27370</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>27367</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
```
</div>
////
//// tab | `uvicorn`
如果你偏好直接使用 `uvicorn` 指令:
<div class="termy">
```console
$ uvicorn main:app --host 0.0.0.0 --port 8080 --workers 4
<font color="#A6E22E">INFO</font>: Uvicorn running on <b>http://0.0.0.0:8080</b> (Press CTRL+C to quit)
<font color="#A6E22E">INFO</font>: Started parent process [<font color="#A1EFE4"><b>27365</b></font>]
<font color="#A6E22E">INFO</font>: Started server process [<font color="#A1EFE4">27368</font>]
<font color="#A6E22E">INFO</font>: Waiting for application startup.
<font color="#A6E22E">INFO</font>: Application startup complete.
<font color="#A6E22E">INFO</font>: Started server process [<font color="#A1EFE4">27369</font>]
<font color="#A6E22E">INFO</font>: Waiting for application startup.
<font color="#A6E22E">INFO</font>: Application startup complete.
<font color="#A6E22E">INFO</font>: Started server process [<font color="#A1EFE4">27370</font>]
<font color="#A6E22E">INFO</font>: Waiting for application startup.
<font color="#A6E22E">INFO</font>: Application startup complete.
<font color="#A6E22E">INFO</font>: Started server process [<font color="#A1EFE4">27367</font>]
<font color="#A6E22E">INFO</font>: Waiting for application startup.
<font color="#A6E22E">INFO</font>: Application startup complete.
```
</div>
////
這裡唯一新增的選項是 `--workers`,告訴 Uvicorn 要啟動 4 個工作處理序。
你也會看到它顯示每個處理序的 **PID**`27365` 是父處理序(這是**處理序管理器**),另外每個工作處理序各有一個:`27368``27369``27370``27367`
## 部署概念 { #deployment-concepts }
你已經看到如何使用多個 **workers** 來將應用的執行進行**平行化**,善用 CPU 的**多核心**,並能服務**更多請求**。
在上面的部署概念清單中,使用 workers 主要能幫助到**副本**這一塊,並對**重啟**也有一點幫助,但你仍需要處理其他部分:
* **安全 - HTTPS**
* **系統啟動時執行**
* ***重啟***
* 副本(正在執行的處理序數量)
* **記憶體**
* **啟動前的前置作業**
## 容器與 Docker { #containers-and-docker }
在下一章 [容器中的 FastAPI - Docker](docker.md){.internal-link target=_blank} 我會說明一些策略,幫你處理其他的**部署概念**。
我會示範如何**從零建立你的映像檔**來執行單一 Uvicorn 處理序。這個流程相當簡單,而且在使用像 **Kubernetes** 這類分散式容器管理系統時,大多情況也會這麼做。
## 重點回顧 { #recap }
你可以在 `fastapi``uvicorn` 指令中使用 `--workers` 這個 CLI 選項來啟動多個工作處理序,以善用**多核心 CPU**,**平行**執行多個處理序。
如果你要自行建置**自己的部署系統**,你可以運用這些工具與想法,同時自行處理其他部署概念。
接著看看下一章關於在容器(例如 Docker 與 Kubernetes)中使用 **FastAPI**。你會看到那些工具也有簡單的方法來解決其他**部署概念**。✨
+93
View File
@@ -0,0 +1,93 @@
# 關於 FastAPI 版本 { #about-fastapi-versions }
**FastAPI** 已經在許多應用與系統的生產環境中使用,且測試涵蓋率維持在 100%。同時開發仍在快速推進。
經常加入新功能、定期修復錯誤,程式碼也在持續改進。
這就是為什麼目前版本仍為 `0.x.x`,這表示每個版本都可能包含破壞性變更。這遵循 <a href="https://semver.org/" class="external-link" target="_blank">語意化版本(Semantic Versioning</a> 的慣例。
你現在就可以用 **FastAPI** 建置生產環境的應用(而且你可能已經這麼做一段時間了),只要確保你使用的版本能與其餘程式碼正確相容。
## 鎖定你的 `fastapi` 版本 { #pin-your-fastapi-version }
首先,你應該將你使用的 **FastAPI** 版本「鎖定(pin)」到你知道對你的應用可正常運作的最新特定版本。
例如,假設你的應用使用 `0.112.0` 版本。
如果你使用 `requirements.txt` 檔案,可以這樣指定版本:
```txt
fastapi[standard]==0.112.0
```
這表示你會使用完全相同的 `0.112.0` 版本。
或你也可以這樣鎖定:
```txt
fastapi[standard]>=0.112.0,<0.113.0
```
這表示會使用 `0.112.0`(含)以上但小於 `0.113.0` 的版本,例如 `0.112.2` 也會被接受。
如果你使用其他安裝管理工具,例如 `uv`、Poetry、Pipenv 等,它們也都有可用來指定套件特定版本的方法。
## 可用版本 { #available-versions }
你可以在 [發行說明](../release-notes.md){.internal-link target=_blank} 查看可用版本(例如用來確認目前最新版本)。
## 關於版本 { #about-versions }
依照語意化版本的慣例,任何低於 `1.0.0` 的版本都可能加入破壞性變更。
FastAPI 也遵循慣例:任何「PATCH」版本變更僅用於修正錯誤與非破壞性變更。
/// tip
「PATCH」是最後一個數字,例如在 `0.2.3` 中,PATCH 版本是 `3`
///
因此,你可以將版本鎖定為如下形式:
```txt
fastapi>=0.45.0,<0.46.0
```
破壞性變更與新功能會在「MINOR」版本加入。
/// tip
「MINOR」是中間的數字,例如在 `0.2.3` 中,MINOR 版本是 `2`
///
## 升級 FastAPI 版本 { #upgrading-the-fastapi-versions }
你應該為你的應用撰寫測試。
**FastAPI** 中這很容易(感謝 Starlette),請參考文件:[測試](../tutorial/testing.md){.internal-link target=_blank}
有了測試之後,你就可以將 **FastAPI** 升級到較新的版本,並透過執行測試來確保所有程式碼都能正確運作。
如果一切正常,或在完成必要調整且所有測試通過之後,就可以把你的 `fastapi` 鎖定到該新的版本。
## 關於 Starlette { #about-starlette }
你不應鎖定 `starlette` 的版本。
不同的 **FastAPI** 版本會使用特定較新的 Starlette 版本。
因此,你可以直接讓 **FastAPI** 使用正確的 Starlette 版本。
## 關於 Pydantic { #about-pydantic }
Pydantic 在其測試中涵蓋了 **FastAPI** 的測試,因此 Pydantic 的新版本(高於 `1.0.0`)一律與 FastAPI 相容。
你可以將 Pydantic 鎖定到任何高於 `1.0.0`、適合你的版本。
例如:
```txt
pydantic>=2.7.0,<3.0.0
```