Pod Running ≠ 服務正常
凌晨三點,你的 API 服務「Running」狀態,但客戶反映:首頁打不開。
\\\bash\
kubectl get pods
NAME READY STATUS RESTARTS AGE
my-api-xxx 1/1 Running 0 3h
\\
看起來一切正常。但你 curl 一下:
\\\bash\
curl http://my-api/health
# (沒回應,hang 住...)
\\
Pod 是活的,但程式卡死了。可能是:
- Java 應用 OOM 但沒有 crash,只是 GC 不停
- Node.js event loop 卡住(同步阻塞)
- 死鎖、無窮迴圈
- 連到外部資料庫的連線池耗盡
三種 Probe,各司其職
K8s 提供三種 Probe,每一種解決不同的問題:
| Probe | 問的問題 | 失敗怎麼辦 | 用在哪 |
|---|---|---|---|
| livenessProbe | 你還活著嗎? | 重啟容器 | 偵測死鎖、卡死 |
| readinessProbe | 你準備好接流量了嗎? | 從 Service 摘下 | 啟動中、暫時過載 |
| startupProbe | 你啟動完了嗎? | 重啟容器 | 啟動慢的應用(Java) |
- liveness = 廚師還有心跳嗎?沒有就換一個。
- readiness = 廚師準備好出菜沒?還沒好就先不送單進去。
- startup = 廚師還在熱鍋嗎?等他熱好再檢查其他的。
三種檢查方式
每種 Probe 都可以用以下三種方式來檢查:
| 方式 | 說明 | 適合 |
|---|---|---|
| httpGet | 打 HTTP,回 200-399 算過 | Web API |
| tcpSocket | 嘗試連線該 port | MySQL、Redis 等非 HTTP |
| exec | 在容器內執行指令,exit 0 算過 | 自訂檢查邏輯 |
YAML 寫法
\\\yaml\
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-probe-demo
spec:
template:
spec:
containers:
- name: api
image: my-api:1.0
ports:
- containerPort: 80
livenessProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 10
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /ready
port: 80
initialDelaySeconds: 5
periodSeconds: 5
startupProbe:
httpGet:
path: /healthz
port: 80
failureThreshold: 30
periodSeconds: 10
\\
關鍵參數
| 參數 | 意義 |
|---|---|
\initialDelaySeconds\ | 啟動後等多久才開始檢查 |
\periodSeconds\ | 多久檢查一次 |
\timeoutSeconds\ | 等多久算超時 |
\failureThreshold\ | 連續失敗幾次才算掛 |
\successThreshold\ | 連續成功幾次算恢復(readiness 用) |
Liveness 失敗會怎樣?
實作一次給你看:
\\\bash
# 進去 Pod,把 /healthz 端點故意搞壞
POD=$(kubectl get pods -l app=api-probe-demo -o jsonpath='{.items[0].metadata.name}')
kubectl exec $POD -- rm /app/healthz
# 觀察 Pod 狀態
kubectl get pods -l app=api-probe-demo -w
\\\
你會看到:
\\\\
NAME READY STATUS RESTARTS
api-probe-demo 1/1 Running 0
api-probe-demo 0/1 Running 1 ← 重啟了!
api-probe-demo 1/1 Running 1 ← 恢復
\\
RESTARTS 變成 1——K8s 偵測到 liveness 失敗,自動重啟容器。
Readiness 失敗會怎樣?
關鍵差別:不重啟,但摘掉流量。
\\\bash
# 看 Service 的 Endpoints
kubectl get endpoints my-api-svc
# NAME ENDPOINTS AGE
# my-api-svc 10.42.0.5:80,10.42.0.6:80 5m
# 把 10.42.0.5 的 readiness 弄壞
# 等 5 秒後再看:
kubectl get endpoints my-api-svc
# NAME ENDPOINTS AGE
# my-api-svc 10.42.0.6:80 5m ← 10.42.0.5 不見了!
\\\
這就是 readiness 的精髓——程式還活著,但暫時不健康(如資料庫連線斷了),K8s 就先不要把流量送過來,等它恢復再說。
為什麼要 startupProbe?
Java / Spring Boot 啟動可能要 30-60 秒。如果只設 livenessProbe:
\\\yaml\
livenessProbe:
httpGet:
path: /healthz
initialDelaySeconds: 5 # ← 5 秒後就開始檢查
failureThreshold: 3 # ← 連續失敗 3 次就重啟
\\
結果:
解法是 startupProbe:
\\\yaml\
startupProbe:
httpGet:
path: /healthz
failureThreshold: 30 # 失敗 30 次才放棄
periodSeconds: 10 # 每 10 秒一次 → 最多容忍 5 分鐘啟動
\\
startupProbe 沒過之前,liveness 和 readiness 都不會跑。等啟動好了,才換成正常的 liveness 監控。
三個 Probe 該怎麼配?
最佳實踐:
\\\yaml
# 啟動慢 → startupProbe 寬鬆
startupProbe:
failureThreshold: 30
periodSeconds: 10
# 活著就好 → liveness 簡單一點
livenessProbe:
failureThreshold: 3
periodSeconds: 10
# 流量精細控制 → readiness 嚴格
readinessProbe:
failureThreshold: 1
periodSeconds: 5
\\\
Liveness 不要設太嚴格——容易誤殺正在處理請求的 Pod。Readiness 要敏感——出問題趕快摘流量,恢復後再回來。
重點整理
- Pod Running ≠ 程式正常,要有 Probe 才知道
- Liveness 失敗 → 重啟,Readiness 失敗 → 摘流量,Startup 給慢啟動緩衝
- 三種檢查方式:httpGet / tcpSocket / exec
- Java 應用幾乎都需要 startupProbe,避免啟動迴圈
- Liveness 寬鬆、Readiness 敏感,避免誤殺
下一步
健康檢查有了,但程式還是會吃光記憶體 / CPU。一個 Pod 失控吃掉 16GB 記憶體,會把整個 Node 拖垮,其他 Pod 全部跟著掛。
下一篇:Resource Limits 與 OOMKilled — 怎麼防止單一 Pod 拖垮整個 Node
📅 下一篇:Resource Limits 與 OOMKilled:怎麼防止單一 Pod 拖垮整個 Node
requests / limits 兩個欄位設好,Pod 互不打架——順便看 K8s QoS 三層級。
📚 完整系列總覽:K8s 系列教學首頁(共 40 課,按學習路徑順序排)