iT邦幫忙

2026 iThome 鐵人賽

DAY 24
0
Kubernetes

不囉唆圖解 Kubernetes系列 第 24 篇

K8s 三種探針 Probe,存活檢查探針鈴 Liveness Probe 、就緒探針小旗子 Readiness Probe、啟動探針 Startup Probe

  • 分享至 

  • xImage
  •  

Day 24:K8s 三種探針 Probe,存活檢查探針鈴 Liveness Probe 、就緒探針小旗子 Readiness Probe、啟動探針 Startup Probe

K8s Probe

Pod 的狀態是 Running,但服務其實已經死了。
K8s 顯示一切正常,使用者卻在對你尖叫。

https://ithelp.ithome.com.tw/upload/images/20261007/20124462KCrTPkqmGU.png
泡泡(Pod)外掛著存活檢查鈴(Liveness Probe),頂上插著一面旗子(Readiness Probe),鈴響起就是一次檢查動作。

Running 不等於還能用

https://ithelp.ithome.com.tw/upload/images/20261007/20124462dVpWCnOmeS.png

Running 代表 Pod 至少有一個主要容器正在執行,不能單獨證明服務可以接收請求。

程序還在,不等於服務還能用。
連線池滿了、死結卡住了、記憶體漏光在做無效的 GC,程序都還活著,但它已經不會回應任何請求了。
K8s 看不出這種死法,除非我們教它怎麼看。

探針 Probe 是什麼?

Kubernetes 探針(Probe)是 管家松鼠 (kubelet) 對容器進行的健康診斷機制。三種探針各司其職:

存活檢查 Liveness Probe:你還活著嗎?沒回應就重啟

kubelet 定期替 Pod 做存活檢查 Liveness Probe,連續失敗達到設定門檻時,就會重啟裡面的小鳥(Container)。
它管的是「救不救」。

就緒探針 Readiness Probe:你準備好了嗎?沒好就不給流量

Readiness Probe 旗子升起來,Service 才把它加進 Endpoints;旗子降下來,柱子立刻把它從名單移除,通常不會重啟它。
它管的是「給不給客人」。

啟動探針 Startup Probe:你還在開機嗎?開機期間其他兩隻先別吵

有些服務啟動要花兩分鐘(載模型、跑 migration)。
Startup Probe 成功之前,liveness 和 readiness 不會開始執行;startup 自己失敗到門檻時仍可能造成重啟。
它管的是「什麼時候開始檢查」。

三者的分工用一句話講完:Liveness 決定生死,Readiness 決定流量,Startup 決定何時開始問。

最貴的設定錯誤

https://ithelp.ithome.com.tw/upload/images/20261007/20124462E9iZm10nqj.png
三顆泡泡 Pod 並排:第一顆的存活檢查 Liveness Probe 警示後重生,第二顆旗子 Readiness Probe 降下但 Pod 完好、旁邊的柱 Service 把它跳過,第三顆掛著一個沙漏。

最常見也最貴的錯誤是:Liveness 設太敏感,造成無限重啟。

服務只是暫時變慢(尖峰、GC、下游卡住),存活檢查連續失敗達到門檻,管家松鼠就會重啟它。
重啟要三十秒,這三十秒的流量壓到其他 Pod 上,其他 Pod也變慢,也被重啟,整組服務被自己的探針(Probe)殺光。
明明只是慢,結果變成全滅。

所以記住兩條保命原則。

第一,Liveness 要設得比 Readiness 寬鬆很多。
慢的時候先把旗子 Readiness Prob 降下來,暫停導入流量,給服務恢復時間。

第二,Liveness 只檢查「這個程序自己」。
千萬不要在 Liveness 裡去 ping 資料庫,資料庫抖一下,整組前端可能都被管家松鼠重啟,而重啟一百次也救不了資料庫。
那種檢查放 Readiness。

三種探針(Probe)的檢查方式都一樣有三種:httpGet(打一個網址看狀態碼)、tcpSocket(連得上就算活)、exec(在容器裡跑指令看回傳值)。
網頁服務用第一種就好。

動手 5 分鐘

前二十三天我們的前端泡泡(Pod)沒有任何健康檢查,K8s 只知道程序還在。今天幫它裝上探針(Probe),再故意弄壞給你看。

改 hello-deployment.yaml,在 ports 底下加這兩段:

          readinessProbe:
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 2
            periodSeconds: 3
            failureThreshold: 2
          livenessProbe:
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 10
            periodSeconds: 10
            failureThreshold: 5

注意兩組數字的差距。 Readiness 三秒問一次、錯兩次就降旗(六秒內反應);Liveness 十秒問一次、錯五次才動手(五十秒才重啟)。這就是「寬鬆很多」的實際樣子。

kubectl apply -f hello-deployment.yaml
kubectl rollout status deployment/hello
kubectl get pods

三顆 1/1。那個 1/1 的第一個數字,就是「旗子(Readiness Probe)升起來的容器數」 ── 你看了二十三天的欄位,今天才知道它的意思。

看探針(Probe)的設定生效了沒:

kubectl describe pod -l app=hello | grep -E "Liveness|Readiness"

會印出 http-get http://:80/ delay=10s timeout=1s period=10s #success=1 #failure=5 這種摘要。

現在弄壞一顆給你看。 這裡要另外開一組拋棄式的泡泡(Pod),原因很實際:你的 hello 泡泡(Pod)從 Day 19 起,首頁是 ConfigMap 唯讀掛載進去的,改不動。

建立 demo-probe.yaml,一根柱子(Service)加三顆帶探針(Probe)的泡泡(Pod):

apiVersion: v1
kind: Service
metadata:
  name: demo-probe
spec:
  selector:
    app: demo-probe
  ports:
    - port: 80
      targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-probe
spec:
  replicas: 3
  selector:
    matchLabels:
      app: demo-probe
  template:
    metadata:
      labels:
        app: demo-probe
    spec:
      containers:
        - name: web
          image: nginx:1.27-alpine
          ports:
            - containerPort: 80
          readinessProbe:
            httpGet: {path: /, port: 80}
            initialDelaySeconds: 2
            periodSeconds: 3
            failureThreshold: 2
          livenessProbe:
            httpGet: {path: /, port: 80}
            initialDelaySeconds: 10
            periodSeconds: 10
            failureThreshold: 5
kubectl apply -f demo-probe.yaml
kubectl rollout status deployment/demo-probe

把其中一顆的首頁搬走,讓它對 / 回 403:

POD=$(kubectl get pods -l app=demo-probe -o jsonpath='{.items[0].metadata.name}')
kubectl exec $POD -- mv /usr/share/nginx/html/index.html /tmp/

立刻盯著看:

kubectl get pods -l app=demo-probe -w

先看到的是 READY 從 1/1 變成 0/1,但 STATUS 還是 Running。 這就是小旗子(Readiness Probe)降下來了 ── 泡泡(Pod)活著,只是不接客。

同時去看柱子(Service)的名單(Endpoints),但要看對地方:

kubectl get endpointslices -l kubernetes.io/service-name=demo-probe

奇怪,ENDPOINTS 那欄還是三個位址,一個都沒少。

這裡有個很多人會誤會的細節:EndpointSlice 不會把未就緒的泡泡(Pod)從名單(Endpoints)上刪掉,它是掛一個 ready: false 的標記。 表格那一欄是不分就緒與否全部列出來的,所以你得往裡面看:

kubectl get endpointslices -l kubernetes.io/service-name=demo-probe \
  -o jsonpath='{range .items[*].endpoints[*]}{.addresses[0]}{"  ready="}{.conditions.ready}{"\n"}{end}'
10.244.0.39  ready=false
10.244.0.37  ready=true
10.244.0.38  ready=true

降旗的那顆變成 ready=false。 道路管理員(kube-proxy)只會把流量送給 ready=true 的,所以它確實已經不接客了 ── 只要還有健康的泡泡(Pod)在,使用者不會感覺到這件事(長連線、或三顆同時倒下時仍可能中斷)。

繼續等。五十秒後回到 -w 那個畫面:

demo-probe-xxx   1/1   Running   1 (5s ago)

RESTARTS 變成 1 ── 管家松鼠依照 Liveness Probe 的結果重啟了容器。重啟後容器從 image 重新長出來,index.html 回來了,READY 變回 1/1,那個位址也變回 ready=true。Ctrl+C 離開。

day-24-shot-01
https://ithelp.ithome.com.tw/upload/images/20261007/20124462B7CpgRwQFd.png
0/1 但 Running 是旗子(Readiness Probe)降下來;五十秒後 RESTARTS 變 1,代表管家松鼠依 Liveness Probe 重啟了容器。兩者差了整整一個數量級的時間。

看 kubelet 記錄的探針事件:

kubectl describe pod $POD | grep -E "Liveness probe failed|will be restarted"

Events 裡明明白白寫著 Liveness probe failed: HTTP probe failed with statuscode: 403,接著 Container web failed liveness probe, will be restarted。(403 不是 404 ── 目錄還在,只是沒有首頁可以給,Nginx 預設不列目錄。)

整個過程你什麼都沒做,服務也沒有中斷。 這就是今天要換到的東西。

驗收完把拋棄式那組清掉:

kubectl delete -f demo-probe.yaml

至於你真正的 hello 服務,把那兩段 readinessProbe / livenessProbe 加進 hello-deployment.yaml 的 ports 底下再 apply 就好,設定完全一樣。

小結

kubelet 先用 Startup Probe 等它開好機,平時用 Readiness Probe 管流量資格,最後用 Liveness Probe 判斷是否重啟。

參考資源


上一篇
Day 23:K8s 資料實戰,幫 hello service 接上一個資料庫
系列文
不囉唆圖解 Kubernetes 共 24 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言