iT邦幫忙

2026 iThome 鐵人賽

DAY 9
0
Kubernetes

探討k8s部署方式系列 第 9

實際部署FastAPI試試看[Day9]

  • 分享至 

  • xImage
  •  

一百一十二、實際部署 FastAPI,需要哪些 Kubernetes YAML?

前面已經知道 Kubernetes 裡面幾個重要元件:

Deployment
Service
Ingress
HPA

如果要部署一個 FastAPI Backend,可以先把它們理解成:

Deployment
→ 我要跑幾個 FastAPI Pod

Service
→ 幫這些 Pod 提供固定入口

Ingress
→ 讓外部網路可以透過網域進來

HPA
→ 根據負載自動增加或減少 Pod

所以完整流量大概是:

Internet
   │
   ▼
Ingress
   │
   ▼
Service
   │
   ▼
Deployment
   │
   ▼
Pod
Pod
Pod

而 HPA 則是在旁邊調整 Pod 數量:

Metrics
   │
   ▼
HPA
   │
   ▼
Deployment
   │
   ▼
Pod 數量增加 / 減少

一百一十三、先準備 FastAPI Docker Image

假設 FastAPI 專案:

app/
├── main.py
├── requirements.txt
└── Dockerfile

main.py

from fastapi import FastAPI

app = FastAPI()


@app.get("/")
def root():
    return {
        "message": "Hello Kubernetes"
    }


@app.get("/health")
def health():
    return {
        "status": "ok"
    }

Dockerfile:

FROM python:3.12-slim

WORKDIR /app

COPY requirements.txt .

RUN pip install --no-cache-dir -r requirements.txt

COPY . .

CMD [
    "uvicorn",
    "main:app",
    "--host",
    "0.0.0.0",
    "--port",
    "8000"
]

Build:

docker build -t myregistry.example.com/backend:v1.0 .

Push:

docker push myregistry.example.com/backend:v1.0

現在 Kubernetes 就可以使用:

myregistry.example.com/backend:v1.0

來建立 Pod。


一百一十四、Deployment:我要幾個 FastAPI?

第一份 YAML 可以建立:

deployment.yaml

例如:

apiVersion: apps/v1
kind: Deployment

metadata:
  name: fastapi-backend

spec:
  replicas: 3

  selector:
    matchLabels:
      app: fastapi-backend

  template:
    metadata:
      labels:
        app: fastapi-backend

    spec:
      containers:
        - name: fastapi
          image: myregistry.example.com/backend:v1.0

          ports:
            - containerPort: 8000

          resources:
            requests:
              cpu: "250m"
              memory: "256Mi"

            limits:
              cpu: "1"
              memory: "512Mi"

這份設定最重要的是:

kind: Deployment

代表:

我要建立一個 Deployment

一百一十五、replicas 是什麼?

這一段:

replicas: 3

代表:

我要維持 3 個 FastAPI Pod

所以 Kubernetes 會建立:

Deployment
     │
     ├── Pod 1
     ├── Pod 2
     └── Pod 3

如果其中一個 Pod 掛掉:

Pod 1 ✓
Pod 2 X
Pod 3 ✓

Deployment 會重新補一個:

Pod 4

讓數量重新回到:

3

一百一十六、selector 和 labels 在幹嘛?

這兩段:

selector:
  matchLabels:
    app: fastapi-backend

以及:

template:
  metadata:
    labels:
      app: fastapi-backend

是在建立一個識別方式。

可以理解成:

這群 Pod 都貼上:

app=fastapi-backend

例如:

Pod 1
app=fastapi-backend

Pod 2
app=fastapi-backend

Pod 3
app=fastapi-backend

Deployment 就知道:

哪些 Pod 是我管理的?

答案就是:

app=fastapi-backend

這個 label 後面 Service 也會用到。


一百一十七、containerPort 是什麼?

FastAPI 在 Container 裡面執行:

0.0.0.0:8000

所以設定:

ports:
  - containerPort: 8000

可以理解成:

這個 Container
主要提供 8000 Port

但是要注意:

這不代表 Internet 已經可以直接存取:

8000

它只是描述 Container 裡面的 Port。

外部流量怎麼進來,後面還需要:

Service
Ingress

一百一十八、Resource Requests 與 Limits

前面 Auto Scaling 已經提過:

resources:
  requests:
    cpu: "250m"
    memory: "256Mi"

  limits:
    cpu: "1"
    memory: "512Mi"

可以先簡單理解成:

requests
→ 排程時至少需要多少資源

limits
→ 最多允許使用多少資源

例如:

250m CPU

代表:

0.25 CPU Core

而:

1

代表:

1 CPU Core

Scheduler 可以透過 requests 判斷:

這台 Node 還放不放得下這個 Pod?

而 HPA 在使用 CPU utilization 時,也會受到 CPU request 影響。


一百一十九、加入 Health Check

FastAPI 已經提供:

GET /health

所以 Deployment 可以加入:

readinessProbe:
  httpGet:
    path: /health
    port: 8000

  initialDelaySeconds: 5
  periodSeconds: 10

以及:

livenessProbe:
  httpGet:
    path: /health
    port: 8000

  initialDelaySeconds: 10
  periodSeconds: 20

完整:

containers:
  - name: fastapi
    image: myregistry.example.com/backend:v1.0

    ports:
      - containerPort: 8000

    readinessProbe:
      httpGet:
        path: /health
        port: 8000

      initialDelaySeconds: 5
      periodSeconds: 10

    livenessProbe:
      httpGet:
        path: /health
        port: 8000

      initialDelaySeconds: 10
      periodSeconds: 20

一百二十、Readiness Probe 是什麼?

Readiness Probe 解決的是:

這個 Pod 現在可以接 Request 了嗎?

例如 Pod 剛啟動:

Container Start
     │
     ▼
FastAPI Loading
     │
     ▼
Database Connection
     │
     ▼
Application Ready

如果程式還沒完全啟動:

Readiness = Failed

Kubernetes 就不會把流量送進來。

等:

GET /health
→ 200 OK

才會:

Readiness = Ready

然後 Service 才開始把 Request 分配給它。


一百二十一、Liveness Probe 是什麼?

Liveness Probe 回答的則是:

這個 Application 還活著嗎?

例如:

Process 還在

但 Application 已經卡死

此時:

GET /health

一直失敗。

Kubernetes 可以判斷:

這個 Container 不健康

然後重啟 Container。

所以可以簡單記:

Readiness
→ 可以接流量嗎?

Liveness
→ 還活著嗎?

一百二十二、Deployment 完整版本

現在 Deployment 可以寫成:

apiVersion: apps/v1
kind: Deployment

metadata:
  name: fastapi-backend

spec:
  replicas: 3

  selector:
    matchLabels:
      app: fastapi-backend

  template:
    metadata:
      labels:
        app: fastapi-backend

    spec:
      containers:
        - name: fastapi
          image: myregistry.example.com/backend:v1.0

          ports:
            - containerPort: 8000

          resources:
            requests:
              cpu: "250m"
              memory: "256Mi"

            limits:
              cpu: "1"
              memory: "512Mi"

          readinessProbe:
            httpGet:
              path: /health
              port: 8000
            initialDelaySeconds: 5
            periodSeconds: 10

          livenessProbe:
            httpGet:
              path: /health
              port: 8000
            initialDelaySeconds: 10
            periodSeconds: 20

部署:

kubectl apply -f deployment.yaml

查看:

kubectl get deployment

查看 Pod:

kubectl get pods

可能看到:

fastapi-backend-abc123   Running
fastapi-backend-def456   Running
fastapi-backend-ghi789   Running

一百二十三、但現在還不能穩定找到 Pod

目前有:

Pod 1
Pod 2
Pod 3

但是它們可能有自己的 IP:

Pod 1
10.244.1.10

Pod 2
10.244.1.11

Pod 3
10.244.2.5

如果 Pod 掛掉重新建立:

Pod 2 X

新的 Pod 可能變成:

Pod 4
10.244.3.20

所以 Application 不應該直接使用 Pod IP。

這就是:

Service

要解決的問題。


一百二十四、Service:為 Pod 提供固定入口

建立:

service.yaml

內容:

apiVersion: v1
kind: Service

metadata:
  name: fastapi-service

spec:
  selector:
    app: fastapi-backend

  ports:
    - port: 80
      targetPort: 8000

  type: ClusterIP

最重要的是:

selector:
  app: fastapi-backend

它會找到前面 Deployment 建立的:

app=fastapi-backend

那些 Pod。

所以:

               fastapi-service
                      │
        selector: app=fastapi-backend
                      │
           ┌──────────┼──────────┐
           ▼          ▼          ▼
        Pod 1      Pod 2      Pod 3

一百二十五、port 與 targetPort

這一段:

ports:
  - port: 80
    targetPort: 8000

意思是:

Service Port
80

↓

Pod Port
8000

所以 Cluster 裡面的其他服務可以呼叫:

http://fastapi-service

Service 再轉到:

FastAPI :8000

完整:

Client
  │
  │ :80
  ▼
Service
  │
  │ :8000
  ▼
FastAPI Pod

一百二十六、ClusterIP 是什麼?

設定:

type: ClusterIP

代表:

這個 Service
主要提供 Kubernetes Cluster 內部存取

例如 Frontend Pod:

Frontend Pod
     │
     ▼
http://fastapi-service
     │
     ▼
Backend Pods

但是 Internet 外部使用者:

Browser

目前還不能直接透過:

api.example.com

進入。

因此還需要:

Ingress

一百二十七、Ingress:讓外部 Request 進入 Cluster

建立:

ingress.yaml

例如:

apiVersion: networking.k8s.io/v1
kind: Ingress

metadata:
  name: fastapi-ingress

spec:
  ingressClassName: nginx

  rules:
    - host: api.example.com

      http:
        paths:
          - path: /
            pathType: Prefix

            backend:
              service:
                name: fastapi-service

                port:
                  number: 80

這代表:

api.example.com

的 Request:

Internet
   │
   ▼
Ingress
   │
   ▼
fastapi-service
   │
   ▼
FastAPI Pods

一百二十八、Ingress 很像前面學過的 Nginx

前面傳統部署:

server {
    server_name api.example.com;

    location / {
        proxy_pass http://backend;
    }
}

Kubernetes 的 Ingress 概念很接近:

api.example.com
      │
      ▼
fastapi-service

所以:

Ingress

並不是很陌生的新概念。

它其實就是在描述:

Domain / Path
應該送去哪一個 Service

例如:

example.com/
→ frontend-service

example.com/api/
→ backend-service

一百二十九、一個重要觀念:Ingress YAML 本身不會處理 Request

這裡要注意。

建立:

kind: Ingress

只是在告訴 Kubernetes:

我希望流量這樣 Routing

真正執行 HTTP Reverse Proxy 的,是:

Ingress Controller

例如常見:

NGINX Ingress Controller

因此:

Ingress
= Routing 規則

Ingress Controller
= 真正執行規則的程式

可以理解成:

Ingress YAML
     │
     ▼
描述設定
     │
     ▼
Ingress Controller
     │
     ▼
真正接 HTTP Request

一百三十、加入 HTTPS

Production 通常不會只使用:

http://api.example.com

而是:

https://api.example.com

Ingress 可以搭配 TLS Certificate。

例如概念上:

spec:
  tls:
    - hosts:
        - api.example.com

      secretName: api-tls

代表:

api.example.com

使用:

api-tls

這個 Kubernetes Secret 裡面的 TLS Certificate。

實務上也常搭配:

cert-manager

自動管理 Let's Encrypt Certificate。


一百三十一、HPA:自動調整 FastAPI Pod 數量

現在 Backend 已經有:

Deployment
Service
Ingress

接下來加入:

HPA

建立:

hpa.yaml

例如:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler

metadata:
  name: fastapi-hpa

spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: fastapi-backend

  minReplicas: 3

  maxReplicas: 10

  metrics:
    - type: Resource

      resource:
        name: cpu

        target:
          type: Utilization
          averageUtilization: 60

這段最重要的是:

scaleTargetRef:

代表:

HPA 要控制誰?

答案:

Deployment
fastapi-backend

一百三十二、minReplicas 與 maxReplicas

例如:

minReplicas: 3
maxReplicas: 10

意思是:

最低
3 Pods

最高
10 Pods

所以平常可能:

3 Pods

流量增加:

5 Pods

更多流量:

8 Pods

尖峰:

10 Pods

但不會超過:

10

流量下降後又可以慢慢回到:

3

一百三十三、averageUtilization: 60

這段:

averageUtilization: 60

可以簡單理解成:

希望 Pod 平均 CPU
維持大約 60%

例如:

Pod 1 = 90%
Pod 2 = 85%
Pod 3 = 95%

平均明顯高於:

60%

HPA 就可能提高 replicas。

例如:

3
↓
5

Deployment 再建立新的 Pod。


一百三十四、四份 YAML 的關係

現在已經有:

deployment.yaml
service.yaml
ingress.yaml
hpa.yaml

它們不是四個互不相關的設定。

而是:

Ingress
   │
   ▼
Service
   │
   ▼
Deployment
   │
   ▼
Pods

另外:

HPA
 │
 ▼
Deployment

所以整體:

                       Internet
                          │
                          ▼
                       Ingress
                          │
                          ▼
                       Service
                          │
                          ▼
                     Deployment
                          │
              ┌───────────┼───────────┐
              ▼           ▼           ▼
            Pod 1       Pod 2       Pod 3
              │           │           │
              └───────────┼───────────┘
                          │
                          ▼
                    Redis / Database


                       Metrics
                          │
                          ▼
                         HPA
                          │
                          ▼
                     Deployment
                          │
                          ▼
                    replicas 3~10

一百三十五、實際部署

假設四個檔案:

k8s/
├── deployment.yaml
├── service.yaml
├── ingress.yaml
└── hpa.yaml

可以分別:

kubectl apply -f deployment.yaml

kubectl apply -f service.yaml

kubectl apply -f ingress.yaml

kubectl apply -f hpa.yaml

或者:

kubectl apply -f k8s/

一次套用整個目錄。


一百三十六、查看部署狀態

查看 Deployment:

kubectl get deployment

查看 Pods:

kubectl get pods

查看 Service:

kubectl get service

查看 Ingress:

kubectl get ingress

查看 HPA:

kubectl get hpa

例如 HPA 可能看到:

NAME          TARGETS   MINPODS   MAXPODS   REPLICAS
fastapi-hpa   45%/60%   3         10        3

可以理解成:

目前 CPU
45%

目標
60%

目前 Pods
3

一百三十七、如果 Pod 出問題怎麼查?

先:

kubectl get pods

如果看到:

CrashLoopBackOff

可以查看:

kubectl logs <pod-name>

例如:

kubectl logs fastapi-backend-abc123

如果需要查看 Kubernetes 認為發生什麼:

kubectl describe pod <pod-name>

例如可以看到:

Image Pull Failed

Readiness Probe Failed

OOMKilled

Scheduling Failed

這些都是實務部署時非常常看的資訊。


一百三十八、環境變數怎麼放?

FastAPI 通常還需要:

DATABASE_HOST
REDIS_HOST
ENVIRONMENT
API_KEY
JWT_SECRET

Deployment 可以加入:

env:
  - name: DATABASE_HOST
    value: "mysql-service"

  - name: REDIS_HOST
    value: "redis-service"

但是像:

Password
API Key
JWT Secret

通常不應直接寫進 Deployment YAML。

Kubernetes 提供:

Secret

來管理敏感設定。

一般設定則可以使用:

ConfigMap

所以架構又可以變成:

ConfigMap
     │
     ▼

Deployment → Pod

     ▲
     │
   Secret

一百三十九、ConfigMap 與 Secret

可以簡單分成:

ConfigMap

ENVIRONMENT
LOG_LEVEL
DATABASE_HOST
REDIS_HOST

以及:

Secret

DATABASE_PASSWORD
JWT_SECRET
API_KEY

Pod 啟動時再把這些設定注入:

FastAPI Pod
│
├── Application Image
├── ConfigMap
└── Secret

這樣就可以繼續維持前面 Docker 的原則:

相同 Image

+

不同 Environment Configuration

例如:

backend:v1.0

Dev
Staging
Production

全部可以使用同一個 Image。

只換:

ConfigMap
Secret

一百四十、Database 與 Redis 怎麼連?

假設 Kubernetes 裡還有:

redis-service
mysql-service

FastAPI 可以直接透過 Service Name:

REDIS_HOST=redis-service

DATABASE_HOST=mysql-service

因此:

FastAPI Pod
    │
    ├────→ redis-service
    │           │
    │           ▼
    │         Redis
    │
    └────→ mysql-service
                │
                ▼
              MySQL

Backend 不需要知道:

Redis Pod IP
MySQL Pod IP

只需要知道:

Service Name

這也是 Kubernetes Service Discovery 的重要概念。


一百四十一、更新 FastAPI 版本

假設目前:

backend:v1.0

更新後 Build:

backend:v1.1

只要修改 Deployment:

image: myregistry.example.com/backend:v1.1

然後:

kubectl apply -f deployment.yaml

Deployment 就會進行 Rolling Update。

例如:

v1.0
v1.0
v1.0

逐漸:

v1.1
v1.0
v1.0

然後:

v1.1
v1.1
v1.0

最後:

v1.1
v1.1
v1.1

而 Readiness Probe 可以確保:

新的 v1.1 Pod
真正 Ready 之後
才開始接流量

這就能降低部署期間服務中斷的機會。


一百四十二、完整 FastAPI Kubernetes 架構

把目前所有東西串起來:

                           Internet
                              │
                              ▼
                     api.example.com
                              │
                              ▼
                           Ingress
                              │
                              ▼
                      fastapi-service
                              │
                 ┌────────────┼────────────┐
                 ▼            ▼            ▼
               Pod 1        Pod 2        Pod 3
              FastAPI      FastAPI      FastAPI
                 │            │            │
                 └──────┬─────┴─────┬──────┘
                        │           │
                        ▼           ▼
                      Redis       Database

Deployment 管理:

Pod 1
Pod 2
Pod 3

HPA 管理:

3 Pods
   ↕
10 Pods

Service 管理:

Request
→ 哪一個 Pod

Ingress 管理:

api.example.com
→ 哪一個 Service

ConfigMap / Secret 管理:

Application Configuration

Docker Image 管理:

Application
+
Runtime
+
Dependencies

所以一次完整的 Kubernetes FastAPI 部署,可以理解成:

Docker Image
      │
      ▼
Deployment
      │
      ▼
Pod
      │
      ▼
Service
      │
      ▼
Ingress
      │
      ▼
Internet

另外:

HPA
↓
Deployment

ConfigMap / Secret
↓
Pod

一百四十三、每個 YAML 到底負責什麼?

最後可以用一句話記住:

Deployment
→ 我的 FastAPI 要怎麼跑、跑幾份

Service
→ 這些 FastAPI Pod 要怎麼被穩定找到

Ingress
→ 外面的 HTTP / HTTPS Request 怎麼進來

HPA
→ FastAPI Pod 要根據流量增加還是減少

ConfigMap
→ 一般環境設定

Secret
→ 密碼、Token、Key 等敏感設定

到這裡,Kubernetes 已經不再只是:

很多陌生 YAML

而是每一份 YAML 都是在解決之前已經遇過的部署問題。

從最早:

SSH Server
→ 啟動 FastAPI

一路演進成:

Build Docker Image
        │
        ▼
Container Registry
        │
        ▼
Kubernetes Deployment
        │
        ▼
Pods
        │
        ▼
Service
        │
        ▼
Ingress
        │
        ▼
User

而整個系統還能透過 HPA 自動擴縮,透過 Health Check 自動判斷 Pod 狀態,並利用 Rolling Update 逐步完成版本更新。

這就是從傳統單機部署,走到 Container 與 Kubernetes 部署之後,整體部署方式最大的轉變。


上一篇
K8s的 auto scaling[Day8]
系列文
探討k8s部署方式9
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言