前面的內容主要是在理解部署架構如何從:
單機部署
→ 前後端分離
→ Load Balancer
→ Redis
→ Stateless
→ Docker
→ Kubernetes
→ CI/CD
→ Observability
逐步演進。
接下來不再繼續增加理論,而是實際從一個最簡單的 FastAPI 專案開始,一步一步把它部署成接近 Production 的架構。
最終希望完成:
Developer
│
│ git push
▼
Git Repository
│
▼
CI/CD
│
├── Test
├── Docker Build
└── Push Image
│
▼
Registry
│
▼
Kubernetes
│
▼
Ingress
│
▼
Service
│
▼
FastAPI Pods
│ │
▼ ▼
Redis MariaDB
並且加入:
HPA
Logging
Metrics
Tracing
Health Check
但不會一次把所有東西裝進去。
我們會從最簡單的 Application 開始。
首先建立專案:
mkdir fastapi-deployment-lab
cd fastapi-deployment-lab
建立 Python Virtual Environment:
python3 -m venv .venv
啟用:
source .venv/bin/activate
安裝 FastAPI 與 Uvicorn:
pip install fastapi uvicorn
建立:
fastapi-deployment-lab/
│
├── app/
│ ├── __init__.py
│ └── main.py
│
└── requirements.txt
將目前套件輸出:
pip freeze > requirements.txt
app/main.py:
from fastapi import FastAPI
app = FastAPI(
title="FastAPI Deployment Lab",
)
@app.get("/")
async def root():
return {
"message": "Hello FastAPI"
}
@app.get("/health")
async def health():
return {
"status": "ok"
}
現在專案:
fastapi-deployment-lab/
│
├── app/
│ ├── __init__.py
│ └── main.py
│
└── requirements.txt
啟動:
uvicorn app.main:app \
--host 0.0.0.0 \
--port 8000
看到:
Uvicorn running on http://0.0.0.0:8000
表示 Backend 已經啟動。
另外開一個 Terminal:
curl http://127.0.0.1:8000/
應該得到:
{
"message": "Hello FastAPI"
}
測試 Health Check:
curl http://127.0.0.1:8000/health
得到:
{
"status": "ok"
}
此時架構非常簡單:
Browser / curl
│
│ :8000
▼
Uvicorn
│
▼
FastAPI
目前甚至:
沒有 Nginx
沒有 Docker
沒有 Database
沒有 Redis
沒有 Kubernetes
只有最單純的 FastAPI。
這非常重要。
因為後面每增加一個元件,都可以清楚知道:
它到底是在解決什麼問題?
現在我們可以:
http://server-ip:8000
直接存取 FastAPI。
技術上確實可以。
但 Production 通常不會直接讓:
Uvicorn :8000
暴露到 Internet。
我們會希望:
Internet
│
▼
Nginx
│
▼
FastAPI
理由包括:
統一 HTTP / HTTPS 入口
TLS Certificate
Reverse Proxy
Request Header
Timeout
Static File
Access Log
Rate Limiting
因此下一步先不碰 Docker。
先做一次最傳統的:
Nginx + FastAPI 部署。
假設現在有一台 Ubuntu Server。
FastAPI 執行:
127.0.0.1:8000
我們希望使用者只看到:
http://example.com
而不是:
http://example.com:8000
架構:
Internet
│
│ :80
▼
Nginx
│
│ :8000
▼
FastAPI
Ubuntu 安裝:
sudo apt update
sudo apt install nginx
確認:
sudo systemctl status nginx
建立:
sudo vim /etc/nginx/sites-available/fastapi
內容:
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://127.0.0.1:8000;
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;
}
}
啟用:
sudo ln -s \
/etc/nginx/sites-available/fastapi \
/etc/nginx/sites-enabled/fastapi
檢查設定:
sudo nginx -t
如果:
syntax is ok
test is successful
重新載入:
sudo systemctl reload nginx
現在:
GET http://example.com/
流程是:
Browser
│
│ :80
▼
Nginx
│
│ proxy_pass
▼
127.0.0.1:8000
│
▼
FastAPI
如果:
Nginx
跟:
FastAPI
在同一台 Server,
FastAPI 不一定需要直接暴露給 Internet。
可以只:
127.0.0.1:8000
讓:
Nginx
可以連它。
外部使用者則:
Internet
│
▼
Nginx :80 / :443
不能直接:
Internet
│
X
FastAPI :8000
這讓入口更加集中。
目前 FastAPI 是我們手動執行:
uvicorn app.main:app \
--host 127.0.0.1 \
--port 8000
如果:
Terminal 關掉
FastAPI 就停了。
如果 Server:
Reboot
也不會自動啟動。
因此下一個問題是:
誰負責管理 FastAPI Process?
在傳統 Linux Server 上,可以使用:
systemd
建立:
sudo vim \
/etc/systemd/system/fastapi.service
例如:
[Unit]
Description=FastAPI Deployment Lab
After=network.target
[Service]
User=www-data
WorkingDirectory=/var/www/fastapi-deployment-lab
ExecStart=/var/www/fastapi-deployment-lab/.venv/bin/uvicorn \
app.main:app \
--host 127.0.0.1 \
--port 8000
Restart=always
[Install]
WantedBy=multi-user.target
重新讀取:
sudo systemctl daemon-reload
啟動:
sudo systemctl start fastapi
設定開機啟動:
sudo systemctl enable fastapi
查看:
sudo systemctl status fastapi
Log:
journalctl -u fastapi
即時查看:
journalctl -u fastapi -f
現在:
systemd
│
▼
FastAPI Process
如果 Application Crash:
FastAPI X
因為:
Restart=always
systemd 可以重新啟動。
現在已經是一套最傳統的 Linux Web Deployment:
Ubuntu Server
Internet
│
▼
Nginx :80
│
▼
FastAPI :8000
▲
│
systemd
部署流程可能是:
Developer
│
▼
Git Push
│
▼
SSH Server
│
▼
git pull
│
▼
pip install
│
▼
systemctl restart fastapi
這其實就是很多中小型系統真正在使用的部署方式。
假設今天只有一台 Server:
Server A
Python 3.12
FastAPI
Uvicorn
還很好處理。
但現在有:
Server A
Server B
Server C
我們就必須確保:
Python Version
System Package
Python Package
Application Version
Startup Command
全部一致。
如果:
Server A
Python 3.12
Server B
Python 3.11
Server C
Python 3.10
就可能產生不同結果。
因此下一個實作才開始導入:
Docker。
也就是把現在:
Python
+
FastAPI
+
Dependencies
+
Startup Command
包成:
Docker Image
從這裡開始,我們就會逐漸離開:
SSH Server
手動安裝環境
走向:
Build Image
→ Run Container
→ Container Registry
→ Kubernetes
的部署方式。