iT邦幫忙

2026 iThome 鐵人賽

DAY 14
0

昨天把 Todo App 的三個服務都跑在 K8s 上了,今天要學 Probe,讓 K8s 知道 Pod 什麼時候真的健康。

K8s 提供三種 Probe 來監控 Pod 的健康狀態:

  • Liveness Probe → 服務死掉了,重啟 Pod
  • Readiness Probe → 服務還沒準備好,暫時移出流量池
  • Startup Probe → 服務啟動中,先暫緩 Liveness 和 Readiness 檢查

三種 Probe 都支援相同的探測方式(HTTP GET、TCP Socket、Exec),差別只在檢查失敗時的行為。

今天會先從最常用的 Liveness Probe 開始 !


Pod Running 不等於服務正常

K8s 看到 Pod 的 STATUS 是 Running,只代表 Container 程序還在跑,不代表服務本身是健康的。

實際上可能發生的情況:

  • FastAPI 啟動了,但應用程式進入了死鎖狀態,所有請求都沒有回應
  • 記憶體洩漏導致服務越來越慢,但程序沒有崩潰
  • 資料庫連線斷了,API 還在跑但所有需要資料庫的請求都會失敗

這些情況下 Pod 的 STATUS 還是 Running,K8s 不會主動重啟它,服務就這樣一直壞著。


Liveness Probe 是什麼

Liveness Probe 是 K8s 定期執行的健康檢查。檢查失敗超過設定的次數,K8s 就會重啟這個 Pod。

幾個重要參數:

livenessProbe:
  httpGet:
    path: /health
    port: 8000
  initialDelaySeconds: 15   # Pod 啟動後等幾秒才開始檢查
  periodSeconds: 10          # 每隔幾秒檢查一次
  timeoutSeconds: 5          # 每次檢查的超時時間
  failureThreshold: 3        # 連續失敗幾次才重啟
  successThreshold: 1        # 連續成功幾次算健康(Liveness 固定是 1)

initialDelaySeconds 很重要——應用程式啟動需要時間,如果一啟動就開始檢查,還沒準備好的服務會一直被 Probe 判定失敗,導致無限重啟。設成比啟動時間長一點的值就好。


三種 Probe 方式

三種 Probe 都支援以下探測方式,這裡以 Liveness Probe 為例示範寫法。

1. HTTP GET

最常用,對指定路徑發 HTTP GET,回應 2xx 或 3xx 算成功。

livenessProbe:
  httpGet:
    path: /health
    port: 8000
    httpHeaders:               # 選用,可以加自訂 header
      - name: Custom-Header
        value: Awesome

適合有 HTTP 介面的服務,例如 todo-api。

2. TCP Socket

嘗試建立 TCP 連線,連線成功算健康。

livenessProbe:
  tcpSocket:
    port: 3306

適合沒有 HTTP 介面的服務,例如 MySQL、Redis。

3. Exec

在 Container 裡面執行指令,exit code 是 0 算成功。

livenessProbe:
  exec:
    command:
      - cat
      - /tmp/healthy

最彈性,可以執行任意指令,但效能開銷比前兩種大。


實際操作

今天目標:幫 todo-api 加上 HTTP GET Liveness Probe、幫 MySQL 加上 Exec Liveness Probe,並透過故意設定錯誤路徑,觀察 K8s 偵測到失敗後自動重啟 Pod 的行為。

Step1: 幫 todo-api 加上 Liveness Probe

更新 /Todo-App/k8s/todo-api-deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: todo-api
spec:
  replicas: 2
  selector:
    matchLabels:
      app: todo-api
  template:
    metadata:
      labels:
        app: todo-api
        tier: backend
    spec:
      containers:
        - name: api
          image: yourname/todo-app-api:v1.0.0
          ports:
            - containerPort: 8000
          envFrom:
            - configMapRef:
                name: todo-api-config
            - secretRef:
                name: todo-api-secret
          livenessProbe:
            httpGet:
              path: /health
              port: 8000
            initialDelaySeconds: 15
            periodSeconds: 10
            timeoutSeconds: 5
            failureThreshold: 3
kubectl apply -f todo-api-deployment.yaml

Step2: 幫 MySQL 加上 Liveness Probe

MySQL 沒有 HTTP 介面,用 TCP Socket 檢查連線,或用 exec 執行 mysqladmin。

更新 /Todo-App/k8s/mysql-statefulset.yaml:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  serviceName: mysql-headless
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
        tier: database
    spec:
      containers:
        - name: mysql
          image: mysql:8.0
          ports:
            - containerPort: 3306
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-secret
                  key: MYSQL_ROOT_PASSWORD
            - name: MYSQL_DATABASE
              value: "tododb"
            - name: MYSQL_USER
              value: "user"
            - name: MYSQL_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-secret
                  key: MYSQL_PASSWORD
          livenessProbe:
            exec:
              command:
                - /bin/sh
                - -c
                - mysqladmin ping -h localhost -u root -p${MYSQL_ROOT_PASSWORD}
            initialDelaySeconds: 30
            periodSeconds: 10
            timeoutSeconds: 5
            failureThreshold: 3
          volumeMounts:
            - name: mysql-data
              mountPath: /var/lib/mysql
  volumeClaimTemplates:
    - metadata:
        name: mysql-data
      spec:
        accessModes:
          - ReadWriteOnce
        resources:
          requests:
            storage: 5Gi
kubectl apply -f mysql-statefulset.yaml

Step3: 確認 Probe 設定

#取得pod
kubectl get pod

kubectl describe pod <todo-api-pod-name>

在輸出裡找 Liveness 欄位:

https://ithelp.ithome.com.tw/upload/images/20260921/20183863Ltcigi4mnq.png

Step4: 觀察 Probe 失敗時的行為

故意讓 Probe 失敗,看 K8s 怎麼處理。可以暫時把 todo-api-deployment.yaml 的 path 改成一個不存在的路徑:

livenessProbe:
  httpGet:
    path: /nonexistent
    port: 8000
  initialDelaySeconds: 5
  periodSeconds: 5
  failureThreshold: 2

Apply 之後觀察 Pod 狀態:

kubectl apply -f todo-api-deployment.yaml
kubectl get pods -w

你會看到 RESTARTS 欄位開始增加,代表 K8s 在不斷重啟 Pod:

NAME                       READY   STATUS    RESTARTS   AGE
todo-api-xxx               1/1     Running   0          10s
todo-api-xxx               1/1     Running   1          30s   ← 重啟了
todo-api-xxx               1/1     Running   2          50s   ← 又重啟了

確認沒問題後記得把 path 改回 /health,並再次 apply:

kubectl apply -f todo-api-deployment.yaml

小結

今天學了 Liveness Probe:

  • Pod Running ≠ 服務健康,程序在跑不代表服務正常
  • Liveness Probe 定期檢查服務健康狀態,失敗就重啟 Pod
  • 三種方式:HTTP GET(最常用)、TCP Socket(適合資料庫)、Exec(最彈性)
  • initialDelaySeconds 要設得比服務啟動時間長,避免啟動期間就被重啟

明天學 Readiness Probe,它跟 Liveness Probe 很像,但決定的是「要不要把流量送給這個 Pod」,而不是「要不要重啟」!


上一篇
Day 13|PV 和 PVC
下一篇
Day 15|Readiness Probe
系列文
從零學 K8s|30 天核心概念 × 實作,新手也能真正掌握 Kubernetes 共 15 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言