一文學會 Kubernetes Pod 的生命週期管理(轉載)

收穫

瞭解 Pod 的狀態(Status)

瞭解 pod 階段(Phase)

瞭解 Pod conditions

  瞭解容器狀態(Status)

保持容器健康

  瞭解容器自動重啓

  使用探活(liveness)探針(Probe)檢查容器的健康狀況

  如果程序啓動緩慢,請使用 startup probe

  Liveness probe 一些建議

在容器啓動和關閉時執行自定義操作

  使用 poststart hook 在容器啓動時執行初始化操作

  使用 preStop hook 在容器終止之前運行自定義程序

瞭解 Pod 的生命週期

  初始化階段

  運行階段

  終止階段

總結

 

收穫
通過閱讀本文,我們可以瞭解:

檢查 Pod 的狀態

  • 使用活性探針保持容器健康
  • 使用生命週期掛鉤在容器啓動和關閉時執行操作
  • 瞭解 Pod 及其容器的完整生命週期

 

瞭解 Pod 的狀態(Status)
Pod 的狀態部分包含以下信息:

  • Pod 和託管它的工作節點的 IP 地址
  • Pod 何時啓動
  • Pod 的服務質量 (QoS) 等級
  • Pod 處於哪個階段,
  • 集羣的條件,以及其各個容器的狀態。

瞭解 pod 階段(Phase)

Pod Phase 描述
Pending 創建 Pod 對象後,這是其初始階段。 在 pod 被調度到節點並且拉取並啓動其容器的鏡像之前,它一直處於此階段。
Running 該 Pod 的至少一個容器正在運行。因此 Pod 包含多個容器,只要有一個正在運行,Pod 的狀態就是 Running
Succeeded 當所有容器成功完成時,不打算無限期運行的 Pod 會被標記爲“成功”。注意:Pod 的生命週期很多時候由工作負載(workload)來管理,因此會有不同的策略
Failed 當 pod 未配置爲無限期運行並且其至少一個容器未成功終止時,該 pod 會被標記爲“失敗”。
Unknown Pod 的狀態未知,因爲 Kubelet 已停止報告與 API 服務器的通信。 工作節點可能出現故障或與網絡斷開連接。

瞭解 Pod conditions

Pod 的條件(conditions)指示 Pod 是否已達到特定狀態以及原因。與階段相反,Pod 同時具有多個條件。

Pod Condition 描述
PodScheduled 指示pod是否已調度到節點
Initialized Pod 的初始化容器已全部成功完成。
ContainersReady Pod 中的所有容器都表明它們已準備就緒。 這是整個 Pod 準備就緒的必要但非充分條件。
Ready 該 Pod 已準備好爲其客戶提供服務。 Pod 中的容器和 Pod 的就緒門(Readiness Gates)都報告它們已準備就緒。

每個條件要麼滿足,要麼不滿足。 如下圖所示,PodScheduled 和 Initialized 條件一開始未滿足,但很快就滿足,並且在 Pod 的整個生命週期中保持這種狀態。 相比之下,Ready 和 ContainersReady 條件在 Pod 的生命週期內可能會發生多次變化。

瞭解容器狀態(Status)

容器狀態包含幾個字段。 state 字段指示容器的當前狀態,而 lastState 字段顯示前一個容器終止後的狀態。 容器狀態還指示容器的內部ID(containerID)、容器正在運行的鏡像和imageID、容器是否準備好以及重新啓動的頻率(restartCount)。

容器狀態最重要的部分是它的狀態。 容器可以處於下圖所示的狀態之一。

容器狀態 描述
Waiting 容器正在等待啓動。 原因(reason)和消息(message)字段指示容器處於此狀態的原因。
Running 容器已創建並且進程正在其中運行。 startedAt 字段指示該容器啓動的時間。
Terminated 容器中運行的進程已終止。 startedAt 和 finishedAt 字段指示容器何時啓動以及何時終止。 主進程終止的退出代碼位於 exitCode 字段中。
Unknown 無法確定容器的狀態。例如失聯了

 

保持容器健康

如果 Pod 中的一個容器死掉了怎麼辦? 如果 pod 中的所有容器都死掉了怎麼辦? 如何保持 Pod 健康及其容器運行?

瞭解容器自動重啓

當 pod 被調度到某個節點時,該節點上的 Kubelet 會啓動其容器,並從那時起,只要 pod 對象存在,它們就會保持運行。 如果容器中的主進程因任何原因終止,Kubelet 會重新啓動容器。 如果應用程序中的錯誤導致其崩潰,Kubernetes 會自動重新啓動它,因此即使應用程序本身沒有執行任何特殊操作,在 Kubernetes 中運行它也會自動賦予其自我修復的能力。

默認情況下,無論容器中的進程是否以零或非零退出代碼退出,Kubernetes 都會重新啓動容器 - 換句話說,無論容器成功完成還是失敗。 可以通過在 Pod 規範中設置 restartPolicy 字段來更改此行爲。

以下是三種重啓策略:

 

重啓策略 描述
Always 無論容器中的進程以什麼退出代碼終止,容器都會重新啓動。 這是默認的重啓策略。
OnFailure 僅當進程以非零退出代碼終止時,容器纔會重新啓動,按照慣例,這表示失敗。
Never 容器永遠不會重新啓動 - 即使它失敗了。

如下圖所示,容器第一次終止時,會立即重新啓動。 然而,下一次,Kubernetes 會等待十秒鐘,然後再次重新啓動。 然後,在每次後續終止後,此延遲會加倍到 20、40、80 秒,然後是 160 秒。 從此以後,延遲時間保持在五分鐘。 這種在嘗試之間加倍的延遲稱爲指數退避。

在最壞的情況下,容器可能會被阻止啓動長達五分鐘。

使用探活(liveness)探針(Probe)檢查容器的健康狀況

Liveness 探針

我們通過定義活性探針來檢查應用程序是否仍然“活着”(正常運行且響應請求)。 我們可以爲 pod 中的每個容器指定一個活性探針。 Kubernetes 定期運行探測器來詢問應用程序是否仍然存在且運行良好。 如果應用程序沒有響應、發生錯誤或響應是否定的,則容器被認爲不健康並被終止。 如果重啓策略允許,容器就會重新啓動。

Kubernetes 可以使用以下三種機制之一來探測容器:

  • HTTP GET 探針:通過指定的網絡端口和路徑上向容器的 IP 地址發送 GET 請求。 如果探針收到響應,並且響應代碼不代表錯誤(換句話說,如果 HTTP 響應代碼爲 2xx 或 3xx),則認爲探針成功。 如果服務器返回錯誤響應碼,或者沒有及時響應,則認爲探測失敗。
  • TCP 套接字探針:通過嘗試打開到容器指定端口的TCP 連接。 如果連接成功建立,則認爲探測成功。 如果不能及時建立連接,則認爲探測失敗。
  • Exec 探針:在容器內執行命令並檢查其終止的退出代碼。 如果退出代碼爲零,則探測成功。 非零退出代碼被視爲失敗。 如果命令未能及時終止,則探測也被視爲失敗。

Liveness 有幾個參數:

  • initialDelaySeconds 決定了Kubernetes在啓動容器後應該延遲多長時間執行第一個探測;
  • periodSeconds 字段指定兩次連續探測執行之間的時間量;
  • timeoutSeconds 字段指定在探測嘗試計數爲失敗之前等待響應的時間;
  • failureThreshold 字段指定探測器必須失敗多少次才能將容器視爲不健康並可能重新啓動。

對於沒有實現 HTTP 運行狀況檢查端點的應用程序,應使用 tcpSocket 或 exec liveness 探針。

exec 探針

exec 的命令在容器內執行,因此必須在容器的文件系統上可用。如果該命令返回退出代碼爲零,則容器被認爲是健康的。 如果它返回非零退出代碼或未能在 timeoutSeconds 字段中指定的時間內完成,則認爲探測失敗;如過失敗次數超過 failureThreshold 字段中的配置,則容器將立即終止(並根據重啓策略重啓或者停止),我們認爲單個探測失敗可以將容器視爲不健康。

另外 exec 會啓動一個臨時容器,因此建議儘量使用 HTTP 或者 TCP Probe,減少因爲健康檢查帶來的額外開銷。同時 exec 腳本實現也應該相對比較簡單,響應時間控制在 1s 以內,不適宜做業務測的複雜檢查。

如果程序啓動緩慢,請使用 startup probe

默認的 liveness probe 設置爲應用程序提供了 20 到 30 秒的時間來開始響應探針請求。 如果應用程序啓動時間較長,則會重新啓動並且必須重新啓動。 如果第二次啓動也需要同樣長的時間,則會再次重新啓動。 如果這種情況持續下去,容器將永遠不會達到 liveness probe 探測成功的狀態,並陷入無限重啓的循環。

爲了防止這種情況,可以增加 initialDelaySeconds、periodSeconds 或 failureThreshold 設置來解決較長的啓動時間,但這會對應用程序的正常運行產生負面影響。 periodSeconds * failureThreshold 的結果越高,如果應用程序變得不健康,重新啓動應用程序所需的時間就越長(不再靈敏)。

爲了處理應用程序的啓動和穩態運行之間的差異,Kubernetes 還提供了startup probe。

如上圖所示,與活性探針不同,啓動探針失敗是完全正常的。失敗僅表明應用程序尚未完全啓動。成功的啓動探針表明應用程序已成功啓動,Kubernetes 應該切換到活性探針。然後,通常會使用更短的時間執行活性探測,這樣可以更快地檢測無響應的應用程序。

通常 startup probe 和 liveness probe 配置爲使用相同的 HTTP endpoint,當然了也可以使用不同的端點。 我們還可以將啓動探針配置爲 exec 或 tcpSocket 探針。

Liveness probe 一些建議

我們應該爲所有 pod 定義 Liveness 探針。如果沒有它,Kubernetes 除了檢查應用程序進程是否已終止之外,無法知道您的應用程序是否仍然存活(進程在不代表運行健康)。

例如爲了提供更好的活性檢查,Web 應用程序通常會公開特定的運行狀況檢查端點,例如 /healthz。 調用此端點時,應用程序會對應用程序中運行的所有主要組件執行內部狀態檢查,以確保它們都沒有死亡或不再執行其應做的事情。

/healthz 不應該使用鑑權,否則鑑權系統一旦故障,所有 Pod 的 liveness 檢測都會失敗,從而導致重啓。

確保應用程序僅檢查其內部組件的操作,而不檢查受外部因素影響的情況。 例如,當前端服務的運行狀況檢查端點無法連接到後端服務時,它永遠不應該響應失敗。 如果後端服務出現故障,重啓前端也無法解決問題。 這樣的 liveness 探針重啓後會再次失敗,因此容器會反覆重啓,直到後端修復。 如果許多服務以這種方式相互依賴,那麼單個服務的故障可能會導致整個系統的級聯故障

Liveness 探針調用的處理程序應當儘量輕量化,也不應花費太長時間才能完成。 默認情況下,探測會頻繁地被執行,建議要在 1 秒鐘內完成執行

使用消耗大量 CPU 或內存的處理程序可能會嚴重影響容器的主進程。 探測處理程序調用消耗的 CPU 和內存會計入容器的資源配額,從而可能影響主程序的運行(CPU 節流或者容器被 OOM)。

探針的失敗閾值是可配置的。因此不要在探針處理程序中實現重試,而應保持簡單,可以使用 failureThreshold 來實現重試。

 

在容器啓動和關閉時執行自定義操作

如果我們需要在容器啓動和停止時執行自定義操作,可以使用如下鉤子程序:

  • PostStop hook,在容器啓動時執行
  • PreStop hook,在容器停止前執行

這些生命週期掛鉤是按容器指定的,而不是在 pod 級別指定的 init 容器。下圖可以幫助我們直觀地瞭解這些鉤子程序如何在容器的生命週期不同階段執行。

與 Liveness 探針一樣,lifecycle hook 可由如下方式實現:

  • 在容器內執行命令(exec)
  • 向容器中的應用程序發送 HTTP GET 請求

與 Liveness 探針相同,lifecycle hook 只能應用於常規容器,而不能應用於 init 容器。 與 liveness 探針不同,lifecycle hook 不支持 tcpSocket 方式。

使用 poststart hook 在容器啓動時執行初始化操作

創建容器後將立即調用 poststart hook。 我們可以使用 exec 類型的 hook 在主進程啓動時執行附加進程,也可以使用 httpGet 掛鉤向容器中運行的應用程序發送 HTTP 請求以執行某種類型的初始化或預熱過程 。

例如這裏的例子中,我們使用 postStart hook (exec 方式)執行軟件包的安裝。

儘管 postStart hook 與主容器進程異步運行,但它以兩種方式影響容器。

首先,容器保持在 Waiting 狀態,reason 是 ContainerCreating,直到 postStart hook 調用完成。 Pod 的階段爲 Pending。 如果此時運行 kubectl logs 命令,即使容器正在運行,它也會拒絕顯示日誌。 kubectl port-forward 命令也拒絕將端口轉發到 pod。

其次當 postStart hook 中使用的命令無法執行或返回非零退出代碼時,整個容器將被重新啓動。

使用 preStop hook 在容器終止之前運行自定義程序

相關內容:Grissom:容器技術回顧 - 什麼是優雅關閉以及如何實現

preStop hook 會在容器終止之前立即執行。 要終止進程,通常會向該進程發送 TERM 信號。 這告訴應用程序完成它正在做的事情並關閉。 容器也是一樣的方式。 每當容器需要停止或重新啓動時,都會向容器中的主進程發送 TERM 信號。 不過在此之前,Kubernetes 首先執行 preStop hook(如果定義了)。 在 preStop hook 完成之前不會發送 TERM 信號,除非進程由於調用 preStop hook 處理程序本身而終止。

preStop hook 可用於啓動容器的正常關閉或執行其他操作,而無需在應用程序中實現。 與 postStart hook 一樣,可以在容器內執行命令,也可以向在容器中運行的應用程序發送 HTTP 請求。

 

瞭解 Pod 的生命週期

Pod 生命週期包含三個階段:

  1. 初始化階段,Pod 的 init 容器運行。
  2. 運行階段,Pod 的常規容器在該階段運行。
  3. 終止階段,Pod 的容器被終止。

初始化階段

  1. 拉取鏡像
  2. 容器重啓,如果失敗,retry 時間會隨次數加倍
  3. 容器重啓後,根據鏡像拉取策略,可能會再次拉取鏡像
  4. 如果鏡像拉取失敗,會重試

如果 init 容器因錯誤而終止,並且 pod 的重啓策略設置爲 Always 或 OnFailure,則失敗的 init 容器將重新啓動。 如果該策略設置爲 Never,則後續的 init 容器和 pod 的常規容器永遠不會啓動。 Pod 的狀態無限期地顯示爲 Init:Error。我們必須刪除並重新創建 pod 對象才能重新啓動應用程序。

三種拉取鏡像的策略

運行階段

當所有 init 容器成功完成後,pod 的常規容器將全部並行創建。 理論上,每個容器的生命週期應該獨立於 Pod 中的其他容器,但事實並非如此。

容器的 postStart hook 會阻止後續容器的創建
Kubelet 不會同時啓動 pod 中的所有容器。 它按照 Pod 規範中定義的順序同步創建和啓動容器。 如果爲容器定義了 postStart hook,則它與主容器進程異步運行,但 postStart hook 處理程序的執行會阻止後續容器的創建和啓動(這個實現細節將來可能會改變).
相反,容器的終止是並行執行的。 長時間運行的 postStop hook 確實會阻止定義它的容器的關閉,但不會阻止其他容器的關閉。 容器的預停止鉤子全部同時調用。

  1. 鏡像從鏡像倉庫拉取成功
  2. 如果探活探針檢測失敗,會觸發容器重啓
  3. 如果容器不能優雅關閉,則在一定時間後會被強制關閉
  4. 容器重啓後,會根據鏡像拉取策略決定釋放重新拉取鏡像
  5. 當 Pod 被刪除發起後,Pod 進入終止過程,等所有容器停止或者刪除後,Pod 對象也被刪除.

終止階段

Pod 的容器會繼續運行,直到 Pod 對象被刪除,此時將啓動 pod 中所有容器的終止過程,並將其狀態更改爲"Terminating"。

  1. 當執行 kubectl delete pod 開始後,Pod 終止過程開始了;
  2. 容器 A 的 preStop hook 停止了容器內的進程,因此就不需要 Kubelet 來發送 TERM 信號;
  3. 容器 B 沒有定義 preStop hook,因此 Kubelet 直接發送了 TERM 信號;
  4. 容器 C 沒有及時退出,因此會被直接殺死;
  5. 所有容器結束後,Pod 也結束了,隨後會被殺死。

 

總結

  • Pod 的狀態包含有關 Pod 的階段(phase)、其條件(conditions)及其每個容器的狀態(status)信息。 我們可以通過運行 kubectl describe 命令或使用命令 kubectl get -o yaml 獲取完整的信息。
  • 根據 Pod 的重啓策略(restartPolicy),容器可以在終止後重新啓動。當然了容器永遠不會真正重新啓動:舊容器將被銷燬,並創建新容器。
  • 如果容器被重複終止,則在每次重新啓動之前都會插入指數增加的延遲。 第一次重新啓動沒有延遲,然後延遲 10 秒,在後續每次重新啓動之前延遲加倍。 最大延遲時間爲 5 分鐘,當容器正常運行至少兩次後,延遲時間會重置爲零。
  • 每次嘗試下載容器映像失敗後,也會讓啓動增加指數級的延遲。
  • 將 liveness probe 添加到容器可確保容器在停止響應時重新啓動。 liveness probe 通過 HTTP GET 請求、在容器中執行命令(exec)或打開 TCP 連接來檢查應用程序的狀態。
  • 如果應用程序需要很長時間才能啓動,則可以使用比 liveness probe 中更寬鬆的設置來定義 startup probe,以防止容器過早重新啓動。
  • 我們可以爲每個 Pod 的主容器定義 lifecycle hook。 postStart hook 在容器啓動時調用,而 preStop hook 在容器停止時調用。 Lifecycle hook 可以發送 HTTP GET 請求或在容器內執行命令。
  • 如果在容器中定義了 preStop hook,在容器停止時,則首先調用該鉤子程序。 然後 TERM 信號被髮送到容器中的主進程。 如果進程在終止序列開始後的 terminationGracePeriodSeconds 內沒有停止,則進程將被強制終止(kill -9)。
  • 當我們刪除 Pod 對象時,其所有容器將被同時終止。 Pod 的刪除 GracePeriodSeconds 是給予容器關閉的時間。 默認情況下,它設置爲 termination grace period,但可以使用 kubectl delete 命令覆蓋。
  • 如果關閉 pod 需要很長時間,則可能是其中運行的進程之一無法處理 TERM 信號。 添加 TERM 信號處理程序是比縮短終止或刪除寬限期更好的解決方案。

 

原文:《Kubernetes In Action 2nd》,預計將於明年出版,本人已經購買,目前的版本是 MEAP Edition,上述內容來自原文 《6. Manging the Pod lifecycleUnderstanding the pod lifecycleManging the Pod lifecycleManging the Pod lifecycle》。

 

原文鏈接

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章