「健康探針」模式談的是應用如何把自己的健康狀態傳達給 Kubernetes。要能被完全自動化,雲原生應用必須具備高度可觀測性,讓外界能推知它的狀態,好讓 Kubernetes 判斷應用是否已啟動、是否已準備好服務請求。這些觀測結果會影響 Pod 的生命週期管理,以及流量如何被導向應用。
問題#
Kubernetes 會定期檢查容器的行程狀態,偵測到問題就重啟它。但從實務經驗我們知道,單看行程狀態並不足以判斷應用是否健康——很多情況下應用已經卡死,行程卻還活著:
- Java 應用可能拋出
OutOfMemoryError,JVM 行程卻仍在執行。 - 應用可能因無窮迴圈、死結(deadlock)或某種輾轉(快取、堆積、行程 thrashing)而凍結。
要偵測這類狀況,Kubernetes 需要一套可靠的方式來檢查應用健康。重點不是去理解應用內部如何運作,而是取得一個指標,說明應用是否如預期運作、是否有能力服務使用者。
解法#
軟體業已經接受一個事實:不可能寫出零缺陷的程式碼;而在分散式應用中,失敗的機率只會更高。因此,處理失敗的重心已從「避免失敗」轉向「偵測故障並復原」。
偵測失敗並非可以一體適用於所有應用的簡單工作——每個應用對「失敗」的定義不同,而不同類型的失敗也需要不同的矯正動作:有些暫時性失敗只要給足時間就會自行恢復,有些則需要重啟應用。以下是 Kubernetes 用來偵測與矯正失敗的檢查。
行程健康檢查#
行程健康檢查是 kubelet 對容器行程持續執行的最簡單檢查:容器行程若沒在跑,就重啟它。因此即使沒有其他健康檢查,光靠這個通用檢查也能讓應用稍微更健壯一些。
如果你的應用有能力偵測任何形式的失敗並自行關閉,那麼行程健康檢查就夠了。但多數情況下這並不足夠,仍需要其他類型的健康檢查。
存活探針(Liveness Probes)#
若你的應用陷入死結,從行程健康檢查的角度看它仍然是健康的。為了偵測這類問題、以及依你應用商業邏輯定義的其他失敗類型,Kubernetes 提供存活探針——由 kubelet 代理定期執行的檢查,要求容器確認自己依然健康。
健康檢查從外部執行很重要,不能交給應用自己,因為某些失敗恰恰會讓應用的看門狗(watchdog)無法回報失敗。
就矯正動作而言,存活探針與行程健康檢查相同:偵測到失敗就重啟容器。但它在檢查手段上更有彈性:
- HTTP 探針:對容器 IP 位址發出 HTTP GET 請求,期待回應碼落在 200 到 399 之間。
- TCP Socket 探針:以能成功建立 TCP 連線為準。
- Exec 探針:在容器的核心命名空間中執行任意指令,期待成功的結束碼(0)。
apiVersion: v1
kind: Pod
metadata:
name: pod-with-liveness-check
spec:
containers:
- image: k8spatterns/random-generator:1.0
name: random-generator
env:
- name: DELAY_STARTUP
value: "20"
ports:
- containerPort: 8080
protocol: TCP
livenessProbe:
httpGet: # 對健康檢查端點發出 HTTP 探測
path: /actuator/health
port: 8080
initialDelaySeconds: 30 # 首次探測前先等 30 秒,給應用暖機時間依應用性質選擇最合適的方法;至於「什麼時候算健康」,由你的實作決定。
但要記住:健康檢查沒通過的後果就是容器被重啟。如果重啟容器根本無濟於事,那麼設一個註定失敗的健康檢查毫無好處——Kubernetes 只會不斷重啟你的容器,卻沒有解決底層問題。
就緒探針(Readiness Probes)#
存活檢查藉由殺掉不健康的容器、換上新容器來維持應用健康。但有時容器並非不健康,重啟也於事無補,最常見的例子是:
- 容器還在啟動中,尚未準備好處理任何請求;
- 容器過載、延遲上升,你希望它暫時擋掉額外負載、自我保護一陣子。
這類情境要用就緒探針。執行就緒檢查的方法與存活檢查相同(HTTP、TCP、Exec),但矯正動作不同:就緒探針失敗不會重啟容器,而是把容器從服務端點(service endpoint)移除,使它不再收到新流量。
就緒探針在容器準備好時發出訊號,讓它在被服務請求打中之前有時間暖機;由於就緒探針和存活檢查一樣會定期執行,它在後續階段也能用來為服務擋流量。
apiVersion: v1
kind: Pod
metadata:
name: pod-with-readiness-check
spec:
containers:
- image: k8spatterns/random-generator:1.0
name: random-generator
readinessProbe:
exec:
# 檢查應用「準備好服務請求時才建立」的檔案是否存在。
# 檔案不存在時 stat 回傳錯誤,就緒檢查即告失敗。
command: ["stat", "/var/run/random-generator-ready"]同樣地,「應用何時準備好開工、何時該被暫時放過」由你的健康檢查實作決定。
兩者的哲學差異:行程健康檢查與存活檢查的意圖是「重啟容器以從失敗中復原」,而就緒檢查則是為你的應用爭取時間,期待它自行恢復。
還要留意,當容器正在關閉時,即使它收到 SIGTERM 後就緒檢查仍然通過,Kubernetes 依然會設法阻止它接收新請求。
許多情況下,存活與就緒探針執行的是同樣的檢查。但就緒探針的存在讓容器有啟動的時間:只有通過就緒檢查,一次 Deployment 才被視為成功——例如在滾動更新中,舊版本的 Pod 要等到這一刻才會被終止。
存活與就緒探針是雲原生應用自動化的根本建構單元。Spring actuator、WildFly Swarm health check、Karaf health checks,或 Java 的 MicroProfile 規格等應用框架,都提供了健康探針的實作。
討論#
要能被完全自動化,雲原生應用必須具備高度可觀測性——提供一種手段,讓管理平台能讀取並詮釋應用健康,必要時採取矯正動作。健康檢查在部署、自我修復、擴縮等活動的自動化中扮演根本角色。除此之外,應用還有其他方式能揭露自身健康:
- 日誌(logging):最明顯也最古老的做法。容器最好把所有重要事件輸出到 system out 與 system error,並將日誌集中收集以供分析。日誌通常不用來觸發自動化動作,而是用來發出警示與後續調查;它更有用的面向是失敗的事後分析與偵測那些不易察覺的錯誤。
- 終止訊息:除了輸出到標準串流之外,也建議把容器退出的原因寫到
/dev/termination-log——這是容器在永遠消失之前留下遺言的地方。

圖 4-1:容器與執行期平台溝通的各種可觀測性選項
容器把應用當成黑盒子,提供了統一的封裝與執行方式。但任何想成為雲原生公民的容器,都必須提供 API 讓執行期環境觀測其健康並據以行動。這項支援是「以統一方式自動化容器更新與生命週期」的根本前提,而後者又進一步提升系統韌性與使用者體驗。
實務上,這意味著你的容器化應用至少必須提供各類健康檢查(存活與就緒)的 API。
表現更好的應用還應整合 OpenTracing 或 Prometheus 這類追蹤與指標收集函式庫,提供更多手段讓管理平台觀測應用狀態。把你的應用當成黑盒子,但要實作出所有必要的 API,幫助平台以最好的方式觀測與管理它。
下一個模式「受管生命週期」同樣是關於應用與 Kubernetes 管理層之間的溝通,但方向相反——談的是你的應用如何被告知重要的 Pod 生命週期事件。
更多資訊#
- Health Probe Example
- Configuring Liveness and Readiness Probes
- Setting Up Health Checks With Readiness and Liveness Probes
- Resource Quality of Service
- Graceful Shutdown with Node.js and Kubernetes
- Advanced Health-Check Patterns in Kubernetes