要想Pod好--健康檢查少不了
要想Kubernetes里每個服務(wù)的可用性更高,那么對Pod的健康檢查是少不了的。Pod生命周期和健康檢查是我們最常接觸的基礎(chǔ)知識,雖說是基礎(chǔ)吧,但如果理解不好,出現(xiàn)問題時很容易抓耳撓腮,揪頭發(fā)。
本文主要從以下六個方面介紹Pod的健康檢查:剛接觸K8S的糗事、Pod生命周期、重啟策略、健康檢查、如何選擇探針、實戰(zhàn),最后還會有知識點的總結(jié)和排查Pod問題的總結(jié)。
一、剛接觸K8S的糗事
回想2019年我剛開始接觸Kubernetes時,碰到Pod一直起不來的情況,就開始抓瞎。后來漸漸地掌握了一些排查方法之后,這種情況才得以緩解。
隨著時間推移,又碰到了問題。有一天在部署某個springboot微服務(wù)時,在開發(fā)測試環(huán)境部署了好多次,只有幾次能成功啟動,大部分的部署未能成功啟動。但是生產(chǎn)環(huán)境卻每次都能成功部署。當(dāng)時這個問題困擾了我很久。現(xiàn)在想來也是蠻有意思的。
相信很多你已經(jīng)猜出來答案了,對,跟我們今天要講的健康檢查有關(guān)。
二、Pod生命周期
談健康檢查之前,首先得一起回顧下Pod的生命周期 或者 說是Pod的狀態(tài)。
Pod 的生命周期,從 Pending 狀態(tài)開始, 如果Pod中至少有一個應(yīng)用容器正常啟動,則進入 Running狀態(tài),之后,如果Pod中的容器正常退出則進入 Succeeded狀態(tài),如果Pod中的容器非正常終止則進入 Failed 狀態(tài)。
- Pending狀態(tài):此時Pod已經(jīng)被K8S接受并且創(chuàng)建,但是Pod內(nèi)還沒有容器被創(chuàng)建,這個過程包括:等待Pod被調(diào)度的時間、下載鏡像的時間。
- Running狀態(tài):此時Pod已經(jīng)運行在某個節(jié)點上,Pod內(nèi)所有容器都已經(jīng)創(chuàng)建,并且有容器處于如下狀態(tài):運行狀態(tài)、正在啟動狀態(tài) 或 正在重啟狀態(tài)。
- Succeeded狀態(tài):此時Pod內(nèi)所有容器都成功執(zhí)行并且退出。
- Failed狀態(tài):此時Pod內(nèi)所有容器都已終止,但是有容器是非正常終止的。
- Unknown狀態(tài):無法獲取Pod狀態(tài),通常是因為Pod與所在主機通信失敗,也可能是別的原因。
三、重啟策略
Pod的重啟是由該Pod所處的Node節(jié)點上的kubelet 進行判斷和控制的。kubelet會根據(jù)重啟策略進行相應(yīng)操作。
Pod的重啟策略有3個:Always、OnFailure、Never,默認(rèn)是Always。
- Always:重啟策略是Always時,那么當(dāng)容器運行狀態(tài)是失效時,kubelet會自動重啟該容器,比如:存活探針檢測到應(yīng)用不健康了,就會自動重啟Pod。
- OnFailure:重啟策略是OnFailure時,那么當(dāng)容器是Failed狀態(tài)時,kubelet會自動重啟該容器。
- Never:不論容器運行狀態(tài)怎樣,kubelet都不會重啟該容器。
四、健康檢查
健康檢查功能可以保障應(yīng)用的可用性,以及控制何時可對外的訪問。
K8S有3種檢查探針:LivenessProbe存活探針、ReadinessProbe就緒探針、StartupProbe啟動探針。
- LivenessProbe存活探針判斷容器是否存活(Running狀態(tài)),如果存活探針檢測到容器不健康,則kubelet將kill掉該容器,并根據(jù)容器的重啟策略做相應(yīng)的處理。
- ReadinessProbe 就緒探針判斷容器是否可用(Ready狀態(tài)),達(dá)到Ready狀態(tài)的Pod才可以接收請求。kubelet 使用就緒探針檢測容器什么時候可以接受請求。
- StartupProbe啟動探針某些應(yīng)用啟動比較慢,例如某個大的單體應(yīng)用啟動時間長達(dá)3分鐘,此時如果只使用存活探針 或者 就緒探針,很可能應(yīng)用還沒起來,就被kill掉了。這種情況可以通過啟動探針來解決。如果配置了啟動探針,在存活探針和就緒探針成功之前不會重啟容器。說白了就是只要配置了啟動探針,那么在應(yīng)用沒成功啟動之前,存活探針和就緒探針就不生效。
以上3種探針,每種都有3種實現(xiàn)方式:
- ExecAction:在容器內(nèi)運行一個命令,如果該命令的返回碼為 0,則說明容器是健康的。
- TCPSocketAction:通過容器的 IP 地址和端口號進行TCP檢查,如果能夠建立TCP 連接,則說明容器是健康的。
- HTTPGetAction:通過容器的IP 地址、端口號以及路徑,發(fā)起HTTP請求,如果HTTP響應(yīng)的狀態(tài)碼大于等于200且小于400,則說明容器是健康的。
在部署Java微服務(wù)應(yīng)用時,我一般選用HTTPGetAction方式。
五、如何選擇探針
既然有3種探針,那么如何選擇呢?
- 如果你希望容器在檢測到失敗時,讓它被kill掉并且自動重啟,那就選擇存活態(tài)探針。
- 如果你希望在檢測成功時Pod才能接受請求,那就需要就緒態(tài)探針。如果某個應(yīng)用A 依賴 應(yīng)用B的啟動才能接受請求,那也需要就緒探針。
- 如果某個應(yīng)用啟動時間較長,那就需要加入啟動探針。
成年人的世界不做選擇題,3個字,全都要,比如:應(yīng)用場景是Spring微服務(wù)時,3種探針其實都會用上。
一個應(yīng)用啟動分3個階段:開始啟動 → 成功啟動(存活) → 可對外訪問。
那對應(yīng)的探針使用順序為:啟動探針 → 存活探針 → 就緒探針。如下圖:
如果只選擇存活探針,就很尷尬:
- 如果配置的存活檢測時間太短,那么碰到啟動慢的應(yīng)用,就徹底起不來了,因為應(yīng)用還沒起來就被kill掉了。
- 如果配置的存活檢測時間太長,那么應(yīng)用真到了出現(xiàn)問題的時候,又無法及時被重啟,從而影響了整體的可用性。
如果不配置就緒探針的話,也很尷尬:
- 比如有的場景下本身應(yīng)用起來了,但是依賴的應(yīng)用還沒起來,那么此時還無法對外提供訪問能力,此時就不能讓請求流量進來。
所以不做選擇題,全都要,需要在每個階段用上對應(yīng)的探針。
六、實戰(zhàn)
1.模擬不健康的應(yīng)用場景
(1) 編排yaml
比如:對Pod進行存活檢測,30S之后,如果不存活則kill掉,然后重啟。
apiVersion: v1
kind: Pod
metadata:
name: pod-lifecycle
namespace: demo
labels:
app: pod-lifecycle
spec:
containers:
- name: pod-lifecycle
image: busybox
args:
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -f /tmp/healthy; sleep 600
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
# 等待5秒執(zhí)行第一次探測
initialDelaySeconds: 5
# 探針連續(xù)失敗了 3 次之后,K8S認(rèn)為檢查已失敗,然后觸發(fā)重啟
failureThreshold: 3
# 每5秒執(zhí)行一次存活探測
periodSeconds: 5
可以看到Pod被重啟多次
(2) 排查異常
出現(xiàn)問題時也不用慌,可以通過kubectl get pods -n demo -o wide 和kubectl describe pod pod-lifecycle -n demo排查??梢郧逦目吹疆惓5脑颍捍婊顧z查失敗。
2.模擬啟動慢的應(yīng)用
(1) 編排yaml
比如:對Pod進行存活檢測,30S之后,如果不存活則kill掉,然后重啟。由于模擬了啟動比較耗時,所以在容器還未成功啟動,就直接被kill掉了,緊接著反復(fù)被kill掉。
apiVersion: v1
kind: Pod
metadata:
name: pod-lifecycle-2
namespace: demo
labels:
app: pod-lifecycle-2
spec:
containers:
- name: pod-lifecycle-2
image: busybox
args:
- /bin/sh
- -c
- sleep 20; touch /tmp/healthy; sleep 600
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
# 等待5秒執(zhí)行第一次探測
initialDelaySeconds: 5
# 探針連續(xù)失敗了 2 次之后,K8S認(rèn)為檢查已失敗,然后觸發(fā)重啟
failureThreshold: 2
# 每5秒執(zhí)行一次存活探測
periodSeconds: 5
執(zhí)行yaml之后,可以看到,Pod重復(fù)這樣的動作:健康檢查失敗被重啟。
(2) 引入startupProbe解決此問題
apiVersion: v1
kind: Pod
metadata:
name: pod-lifecycle-3
namespace: demo
labels:
app: pod-lifecycle-3
spec:
containers:
- name: pod-lifecycle-3
image: busybox
args:
- /bin/sh
- -c
- sleep 20; touch /tmp/healthy; sleep 600
startupProbe:
exec:
command:
- cat
- /tmp/healthy
# 等待5秒執(zhí)行第一次探測
initialDelaySeconds: 5
# 探針連續(xù)失敗了 10 次之后,K8S認(rèn)為檢查已失敗,然后觸發(fā)重啟
failureThreshold: 5
# 每5秒執(zhí)行一次存活探測
periodSeconds: 5
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
# 等待5秒執(zhí)行第一次探測
initialDelaySeconds: 5
# 探針連續(xù)失敗了 2 次之后,K8S認(rèn)為檢查已失敗,然后觸發(fā)重啟
failureThreshold: 2
# 每5秒執(zhí)行一次存活探測
periodSeconds: 5
七、總結(jié)
要想Kubernetes里每個服務(wù)的可用性更高,那么對Pod的健康檢查是少不了的。本文重點如下:
- Pod生命周期:Pending 、Running、 Succeeded或 Failed 、UnKnown。
- Pod重啟策略:Always、OnFailure、Never。
- 3種探針類型:啟動探針、存活探針、就緒探針。
- 如何選擇探針:一般情況下全都要。
- 排查Pod問題:搭配使用kubectl get pods -n demo -o wide 和kubectl describe pods webapp -n demo。
講到這里,文章開頭我碰到的問題,你肯定也知道答案了。由于應(yīng)用啟動時間較長,但是只配置了存活探針,沒有配置啟動探針。再加上存活探針配置的整體時間又太短了,每臺機器的性能又不同,所以導(dǎo)致有時候能啟動成功,有時候啟動失敗。