iT邦幫忙

2026 iThome 鐵人賽

DAY 13
0
Kubernetes

探討k8s部署方式系列 第 13 篇

從零開始實作部署[Day13]

  • 分享至 

  • xImage
  •  

Part 2:從零開始實作 FastAPI Production Deployment

前面的內容主要是在理解部署架構如何從:

單機部署
→ 前後端分離
→ 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 開始。


實作一:建立最小 FastAPI 專案

首先建立專案:

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

建立第一支 FastAPI

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 已經啟動。


測試 API

另外開一個 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。

這非常重要。

因為後面每增加一個元件,都可以清楚知道:

它到底是在解決什麼問題?


第一個問題:為什麼不能就這樣上 Production?

現在我們可以:

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 部署。


實作二:加入 Nginx

假設現在有一台 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

建立 Nginx Reverse Proxy

建立:

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

為什麼 FastAPI 綁 127.0.0.1?

如果:

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

實作三:使用 systemd 管理 FastAPI

建立:

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

的部署方式。


上一篇
如何將log實際接入到後端?以FastAPI為例[Day12]
下一篇
把FastAPI包成Docker image[Day14]
系列文
探討k8s部署方式 共 17 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言