1. 程式人生 > 其它 >Kubernetes實戰(第二版)--第六章 管理Pod容器的生命週期

Kubernetes實戰(第二版)--第六章 管理Pod容器的生命週期

本章涵蓋了

  • 檢查pod狀態

  • 使用存活探針(liveness probes)保持容器健康

  • 使用生命週期鉤子在容器啟動和關閉時執行操作

  • 瞭解pod及其容器的完整生命週期

在閱讀了前一章之後,您應該能夠部署、檢查包含一個或多個容器的POD並與之通訊。在這一章,將深入瞭解POD及其容器操作。

請注意

可以在https://github.com/luksa/kubernetes-in-action-2nd-edition/tree/master/Chapter06上找到本章的程式碼。

6.1 理解Pod的狀態

當建立一個pod物件並且執行後,可以通過API讀取pod物件,來檢視pod發生了什麼。正如您在第4章學到的,pod物件清單,以及大多數其他型別的物件清單,包含一個提供物件狀態的部分。pod的狀態部分包含以下資訊:

  • pod的IP地址和所在工作節點
  • pod啟動時間
  • pod的服務質量(QoS)
  • pod處於什麼階段,
  • pod的條件
  • pod中容器的狀態

IP地址和起始時間不需要任何進一步的解釋,而且QoS類現在也不相關——您將在第19章學習它。然而,pod的階段和條件以及容器的狀態對於理解pod的生命週期非常重要。

6.1.1 理解pod階段

在pod生命週期的任何時刻,它都處於如下圖所示的五個階段之一。

圖6.1 Kubernetes pod的階段

下表解釋了每個階段的含義。

pod階段

描述

Pending

建立Pod物件之後,這是它的初始階段。在將Pod排程到一個節點並提取並啟動其容器的映象之前,一直處於這個階段。

Running

Pod中至少有一個容器在執行。

Succeeded

當Pod的所有容器成功完成,不打算無限期執行的pod被標記為Succeeded

Failed

如果沒有將pod配置為無限期執行,且至少有一個容器終止失敗,則pod將被標記為Failed。

Unknown

pod狀態是未知的,因為Kubelet已經停止與API伺服器的通訊。可能工作節點已失敗或已與網路斷開連線。

表6.1 Pod可以處於的階段列表

Pod的階段提供了一個關於Pod正在發生的事情的快速總結。讓我們再次展開kiada pod,檢查它的階段。通過將manifest檔案再次應用到你的叢集來建立pod,就像在上一章(你會在Chapter06/pod.kiada.yaml中找到它):

$kubectlapply-fpod.kiada.yaml

顯示Pod的階段

pod的階段是pod物件的狀態部分中的一個欄位。你可以通過顯示它的清單和選擇性地搜尋欄位來檢視它:

$ kubectl get po kiada -o yaml | grep phasephase:Running

提示

還記得jq工具嗎?可以使用它來列印phase欄位的值,如下所示 kubectl get po kiada -o json | jq .status.phas

可以使用kubectl describe命令檢視Pod的階段。Pod的狀態顯示在靠近輸出頂部的地方。

$ kubectl describe po kiadaName:         kiadaNamespace:    default...Status:       Running...

雖然kubectl get pods顯示的STATUS列也顯示了階段,但這隻對沒問題的pods是正確的:

$ kubectl get po kiadaNAME    READY   STATUS    RESTARTS   AGEkiada1/1Running040m

對於有問題的Pod,STATUS列顯示Pod有什麼問題。將在本章後面看到這一點。

6.1.2 理解Pod條件

Pod的階段並不能說明Pod的狀況。可以通過檢視pod的條件列表瞭解更多資訊,就像在第4章中對節點物件所做的那樣。Pod的狀態表明Pod是否達到了某種狀態,以及為什麼會這樣。

與階段相反,Pod同時有幾個條件。在撰寫本文時,已知四種條件型別。下表對它們進行了解釋。

Pod Condition

描述

PodScheduled

指示pod是否已被排程到某個節點。

Initialized

Pod的初始容器已經全部成功完成。

ContainersReady

Pod裡的所有容器都表明已經準備好了。這是整個Pod準備就緒的必要條件,但不是充分條件。

Ready

pod已經準備好為其客戶端提供服務。Pod內的容器和Pod內的readiness gates都已就緒。注意:這在第10章中有解釋。

表6.2 Pod條件一覽表

每個條件要麼滿足要麼不滿足。在下面的圖中可以看到,PodScheduled和Initialized條件開始時是未滿足的,但很快就會滿足,並且在Pod的整個生命週期中都是如此。相比之下,在Pod的生命週期中,Ready和ContainersReady條件可以改變很多次。

圖6.2Pod在其生命週期內條件的轉換

還記得在節點物件中可以找到的條件嗎?它們是MemoryPressure, DiskPressure, PIDPressure和Ready。如您所見,每個物件都有自己的條件型別集,但許多都包含通用的Ready條件,它通常指示物件是否一切正常。

檢查Pod的條件

要檢視Pod的條件,你可以使用kubectl describe,如下:

$ kubectl describe po kiada...Conditions:  Type              Status  Initialized       True  Ready             True  ContainersReady   True  PodScheduled      True...

kubectl describe命令只顯示每個條件是否為真。要找出為什麼一個條件是假的,您必須查詢狀態。pod清單中的條件欄位如下:

$ kubectl get po kiada -o json | jq .status.conditions[  {    "lastProbeTime": null,    "lastTransitionTime": "2020-02-02T11:42:59Z",    "status": "True",    "type": "Initialized"  },...

每個條件都有一個status欄位,指示條件是True、False還是Unknown。在kiada pod的情況下,所有條件的狀態都是True,這意味著它們都被滿足了。條件還可以包含一個reason欄位,該欄位為條件狀態的最後一次更改指定面向機器的原因,以及一個message欄位,該欄位詳細解釋該更改。lastTransitionTime欄位顯示更改發生的時間,而lastProbeTime表示最後一次檢查此條件的時間。6.1.3 瞭解容器狀態Pod的狀態中還包含每個容器的狀態。檢查狀態可以更好地瞭解每個容器的操作。該狀態包含幾個欄位。state欄位指示容器的當前狀態,而lastState欄位顯示前一個容器終止後的狀態。容器狀態還指示了容器的內部ID (containerID)、容器正在執行的映象和imageID、容器是否就緒以及重新啟動的頻率(restartCount)。瞭解容器狀態容器狀態中最重要的部分是state。容器可以處於如下圖所示的states之一。

圖6.3 容器的可能states下表對各個狀態進行了解釋。

Container State

描述

Waiting

容器正在等待啟動。reason和 message欄位表明容器為什麼處於這種狀態。

Running

容器已經建立,程序正在其中執行。startat欄位表示該容器啟動的時間。

Terminated

在容器中執行的程序已經終止。startat和finhedat欄位指示容器何時啟動,何時終止。主程序終止時使用的退出碼在exitCode欄位中。

Unknown

無法確定容器的狀態。

表6.3可能的容器states顯示Pod容器的狀態kubectl get pods顯示的pod列表只顯示了每個pod中的容器數量以及其中有多少已經就緒。要檢視單個容器的狀態,可以使用kubectl describe:

$ kubectl describe po kiada...Containers:  kiada:    Container ID:   docker://c64944a684d57faacfced0be1af44686...    Image:          luksa/kiada:0.1    Image ID:       docker-pullable://luksa/kiada@sha256:3f28...    Port:           8080/TCP    Host Port:      0/TCP    State:          Running//容器的當前狀態和啟動時間      Started:      Sun, 02 Feb 2020 12:43:03 +0100    Ready:          True//容器是否準備好提供其服務    Restart Count:  0//容器重新啟動了多少次    Environment:    <none>...

請關注清單中帶註釋的行,因為它們表明容器是否健康。kiada容器正在Running並Ready。它從未重新啟動過。

提示你也可以像這樣使用jq來顯示容器的狀態:kubectl get po kiada -o json | jq .status.containerStatuse

檢查init容器狀態在前一章中,瞭解了除了常規容器之外,pod還可以擁有在啟動時執行的init容器。與常規容器一樣,這些容器的狀態在pod物件清單的狀態部分中可用,但在initContainerStatuses欄位中。

檢查kiada-init pod的狀態

作為一個額外的練習,可以自己嘗試,建立上一章中的kiada-init pod,並檢查它的兩個常規容器和兩個初始化容器的階段、條件和狀態。使用kubectl describe命令和kubectl get po kiada-init -o json | jq .status命令查詢物件定義中的資訊

6.2 保持容器健康執行

在前一章中建立的pod執行時沒有任何問題。但如果其中一個容器掛掉了呢?如果pod裡的所有容器都掛掉了怎麼辦?如何保持這些pod的健康和其容器正常執行?這是本節的重點。6.2.1 理解容器自動重啟當一個pod被排程到一個節點時,該節點上的Kubelet啟動它的容器,並從那時起,只要pod物件存在,它們就會一直執行。如果容器中的主程序因任何原因終止,Kubelet將重啟容器。如果應用程式中出現錯誤導致崩潰,Kubernetes會自動重啟應用程式,因此,即使應用程式本身不做任何特殊操作,在Kubernetes中執行它也會自動賦予它自我修復的能力。讓我們看看它的實際應用。觀察容器故障在前一章中,您建立了kiada-ssl pod,它包含Node.js和Envoy容器。再次建立pod,並通過執行以下兩個命令啟用與pod的通訊:

$ kubectl apply -f pod.kiada-ssl.yaml$kubectlport-forwardkiada-ssl808084439901

現在終止Envoy容器,看看Kubernetes如何處理這個情況。在一個單獨的終端中執行以下命令,這樣你就可以看到當一個容器終止時pod的狀態是如何變化的:

$kubectlgetpods-w

你也可以使用下面的命令在另一個終端中觀察事件:

$kubectlgetevents-w

你可以通過傳送KILL訊號,模仿一個主程序崩潰的容器,但你不能在容器內部這樣做,因為Linux核心不讓你殺root程序(程序PID為1)。你將不得不SSH pod的主機節點,進而殺死程序。幸運的是,Envoy的管理介面允許您通過它的HTTP API停止程序。

要終止envoy容器,在瀏覽器中開啟URL http://localhost:9901,單擊quitquitquit按鈕或在另一個終端中執行curl命令:

$ curl -X POST http://localhost:9901/quitquitquitOK

要檢視容器及其所屬的pod發生了什麼,請檢查前面執行的kubectl get pods -w命令的輸出。這是它的輸出:

$ kubectl get po -wNAME           READY   STATUS     RESTARTS   AGEkiada-ssl      2/2     Running    0          1skiada-ssl      1/2     NotReady   0          9m33skiada-ssl2/2Running19m34s

該清單顯示pod的STATUS從Running變為NotReady,而READY列表明兩個容器中只有一個準備好了。緊接著,Kubernetes重啟了容器,pod的狀態返回到Running。“restart”列表示一個容器已經被重啟。

請注意如果其中一個容器失效,其他容器會繼續執行。

現在檢查前面執行的kubectl get events -w命令的輸出。下面是該命令及其輸出:

$ kubectl get ev -wLAST SEEN   TYPE      REASON      OBJECT           MESSAGE0s          Normal    Pulled      pod/kiada-ssl    Container image already                                                   present on machine0s          Normal    Created     pod/kiada-ssl    Created container envoy0s          Normal    Started     pod/kiada-ssl    Started container envoy

這些事件表明,新的envoy容器已經啟動。您應該能夠再次通過HTTPS訪問應用程式。請確認您的瀏覽器或curl。清單中的事件還暴露了Kubernetes如何重啟容器的一個重要細節。第二個事件表明已經重新建立了整個envoy容器。Kubernetes從不重新啟動容器,而是丟棄它並建立一個新的容器。不管怎樣,我們稱之為重新啟動容器。請注意當容器被重新建立時,程序寫入容器檔案系統的任何資料都會丟失。這種行為有時是不可取的。如下一章所述,要持久化資料,必須向pod新增一個儲存卷。請注意如果在pod中定義了init容器,並且重新啟動了其中一個pod的常規容器,則不會再次執行init容器。配置pod的重啟策略預設情況下,Kubernetes重啟容器,不管容器中的程序是以0退出還是非0退出——換句話說,不管容器成功完成還是失敗。可以通過在pod的規範中設定restartPolicy欄位來更改此行為。存在三種重啟策略。如下圖所示。

圖6.4 pod的restartPolicy決定是否重啟容器

三種重啟策略說明如下表所示。

重啟策略

說明

Always

無論容器中的程序終止時的退出碼是什麼,都將重新啟動容器。這是預設的重啟策略。

OnFailure

只有當程序以非零退出碼終止時,容器才會重新啟動,按照約定,這表示失敗。

Never

容器永遠不會重新啟動——即使它失敗了。

表6.4 Pod 重啟策略

請注意

令人驚訝的是,重新啟動策略是在pod級別配置的,並應用於它的所有容器。不能為每個容器單獨配置它。

理解容器重新啟動之前插入的時間延遲

如果你多次呼叫Envoy的/quitquitquit endpoint,你會注意到每次在容器終止後重啟它需要更長的時間。pod的狀態顯示為NotReady或CrashLoopBackOff。這是它的意思。

如下圖所示,當容器第一次終止時,它將立即重新啟動。然而,下一次Kubernetes會等待10秒再重新啟動它。每次終止後,這個延遲會加倍到20,40,80秒,然後是160秒。從那時起,延遲時間保持在五分鐘。這種兩次嘗試之間加倍的延遲稱為指數回退。

圖6.5 容器重啟之間的指數回退

在最壞的情況下,可以阻止一個容器啟動長達5分鐘時間。

請注意

當容器成功執行10分鐘時,延遲將重置為零。如果容器需要稍後重新啟動,則立即重新啟動。

在Pod清單中檢查容器狀態如下:

$ kubectl get po kiada-ssl -o json | jq .status.containerStatuses..."state": {  "waiting": {    "message": "back-off 40s restarting failed container=envoy ...","reason":"CrashLoopBackOff"

正如您在輸出中看到的,當容器等待重新啟動時,它的狀態是waiting,原因是CrashLoopBackOff。message欄位告訴您重新啟動容器所需的時間。請注意當你讓Envoy終止時,它會以零退出碼終止,也就是說它還沒有崩潰。因此,CrashLoopBackOff狀態可能會產生誤導。6.2.2使用活性探針檢查容器的執行狀況在前一節中,您瞭解到Kubernetes通過在應用程式的程序終止時重新啟動應用程式來保持應用程式的健康。但是應用程式也可能在沒有終止的情況下變得無響應。例如,一個有記憶體洩漏的Java應用程式最終開始發生OutOfMemoryErrors,但它的JVM程序繼續執行。理想情況下,Kubernetes應該檢測這種錯誤並重新啟動容器。應用程式可以自己捕獲這些錯誤並立即終止,但是如果應用程式因為陷入無限迴圈或死鎖而停止響應,該怎麼辦呢?如果應用程式無法檢測到這一點怎麼辦?為了確保在這種情況下重新啟動應用程式,可能需要從外部檢查其狀態。活性探針簡介Kubernetes可以通過定義活性探針來檢查應用程式是否仍然活動。可以為pod中的每個容器指定一個活性探針。Kubernetes定期執行探針,以詢問應用程式是否仍然執行良好。如果應用程式沒有響應、出現錯誤或響應為陰性,則認為容器不健康並終止。然後,如果重啟策略允許,則重新啟動容器。請注意活性探針只能在pod的常規容器中使用。它們不能在init容器中定義。活性探針的型別Kubernetes可以使用以下三種機制之一探針容器:

  • HTTP GET探針----將一個GET請求傳送到容器的IP地址(位於您指定的網口和路徑上)。如果探針接收到響應,而響應程式碼不表示錯誤(換句話說,如果HTTP響應程式碼是2xx或3xx),則認為探針成功。如果伺服器返回錯誤響應程式碼,或者沒有及時響應,則認為探針失敗。

  • TCP Socket探針----嘗試開啟到容器指定埠的TCP連線。如果連線成功建立,則認為探針成功。如果連線不能及時建立,則認為探針失敗。

  • Exec探針---在容器內執行命令,並檢查終止時使用的退出碼。如果退出碼為零,則探針成功。非零退出碼被認為是失敗。如果命令未能及時終止,也認為探針失敗。

請注意除了活性探針,容器還可以有啟動探針(在第6.2.6節中討論)和就緒探針(在第10章中解釋)。6.2.3 建立HTTP GET活性探針