kubernetes 最佳實踐: 優雅終止

概述

Pod 銷燬時,會停止容器內的進程,通常在停止的過程中我們需要執行一些善後邏輯,比如等待存量請求處理完以避免連接中斷,或通知相關依賴進行清理等,從而實現優雅終止目的。本文介紹在 Kubernetes 場景下,實現容器優雅終止的最佳實踐。

容器終止流程

我們先了解下容器在 Kubernetes 環境中的終止流程:

  1. Pod 被刪除,狀態置爲 Terminating

  2. kube-proxy 更新轉發規則,將 Pod 從 service 的 endpoint 列表中摘除掉,新的流量不再轉發到該 Pod。

  3. 如果 Pod 配置了 preStop Hook ,將會執行。

  4. kubelet 對 Pod 中各個 container 發送 SIGTERM 信號以通知容器進程開始優雅停止。

  5. 等待容器進程完全停止,如果在 terminationGracePeriodSeconds 內 (默認 30s) 還未完全停止,就發送 SIGKILL 信號強制殺死進程。

  6. 所有容器進程終止,清理 Pod 資源。

業務代碼處理 SIGTERM 信號

要實現優雅終止,務必在業務代碼裏面處理下 SIGTERM 信號,參考 處理 SIGTERM 代碼示例 ) 。

別讓 shell 導致收不到 SIGTERM 信號

如果容器啓動入口使用了腳本 (如 CMD ["/start.sh"]),業務進程就成了 shell 的子進程,在 Pod 停止時業務進程可能收不到 SIGTERM 信號,因爲 shell 不會自動傳遞信號給子進程。更詳細解釋請參考 爲什麼我的容器收不到 SIGTERM 信號 ?

如果解決?請參考 在 SHELL 中傳遞信號

合理使用 preStop Hook

若你的業務代碼中沒有處理 SIGTERM 信號,或者你無法控制使用的第三方庫或系統來增加優雅終止的邏輯,也可以嘗試爲 Pod 配置下 preStop,在這裏面實現優雅終止的邏輯,示例:

        lifecycle:
          preStop:
            exec:
              command:
              - /clean.sh

參考 Kubernetes API 文檔

在某些極端情況下,Pod 被刪除的一小段時間內,仍然可能有新連接被轉發過來,因爲 kubelet 與 kube-proxy 同時 watch 到 pod 被刪除,kubelet 有可能在 kube-proxy 同步完規則前就已經停止容器了,這時可能導致一些新的連接被轉發到正在刪除的 Pod,而通常情況下,當應用受到 SIGTERM 後都不再接受新連接,只保持存量連接繼續處理,所以就可能導致 Pod 刪除的瞬間部分請求失敗。

這種情況下,我們也可以利用 preStop 先 sleep 一小下,等待 kube-proxy 完成規則同步再開始停止容器內進程:

        lifecycle:
          preStop:
            exec:
              command:
              - sleep
              - 5s

調整優雅時長

如果需要的優雅終止時間比較長 (preStop + 業務進程停止可能超過 30s),可根據實際情況自定義 terminationGracePeriodSeconds,避免過早的被 SIGKILL 殺死,示例:

參考資料

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/v8Oz1Kn0vGf3q8Dc3VIojQ