Kubernetes實踐之優雅終止
本文轉載自微信公眾號「云原生知識宇宙」,作者I am roc。轉載本文請聯系云原生知識宇宙公眾號。
概述
Pod 銷毀時,會停止容器內的進程,通常在停止的過程中我們需要執行一些善后邏輯,比如等待存量請求處理完以避免連接中斷,或通知相關依賴進行清理等,從而實現優雅終止目的。本文介紹在 Kubernetes 場景下,實現容器優雅終止的最佳實踐。
容器終止流程
我們先了解下容器在 Kubernetes 環境中的終止流程:
- Pod 被刪除,狀態置為 Terminating。
- kube-proxy 更新轉發規則,將 Pod 從 service 的 endpoint 列表中摘除掉,新的流量不再轉發到該 Pod。
- 如果 Pod 配置了 preStop Hook ,將會執行。
- kubelet 對 Pod 中各個 container 發送 SIGTERM 信號以通知容器進程開始優雅停止。
- 等待容器進程完全停止,如果在 terminationGracePeriodSeconds 內 (默認 30s) 還未完全停止,就發送 SIGKILL 信號強制殺死進程。
- 所有容器進程終止,清理 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 殺死,示例:
參考資料
處理 SIGTERM 代碼示例: https://imroc.cc/k8s/ref/code-example-of-handle-sigterm/
為什么我的容器收不到 SIGTERM 信號: https://imroc.cc/k8s/faq/why-cannot-receive-sigterm/
在 SHELL 中傳遞信號: https://imroc.cc/k8s/trick/propagating-signals-in-shell/