PLEG is not healthy?幕后黑手居然是它!
本文轉(zhuǎn)載自微信公眾號「運(yùn)維開發(fā)故事」,作者沒有文案的夏老師。轉(zhuǎn)載本文請聯(lián)系運(yùn)維開發(fā)故事公眾號。
問題描述
環(huán)境 :ubuntu18.04,自建集群k8s 1.18 ,容器運(yùn)行時(shí)docker。
現(xiàn)象:某個(gè)Node頻繁NotReady,kubectl describe該Node,出現(xiàn)“PLEG is not healthy: pleg was last seen active 3m46.752815514s ago; threshold is 3m0s”錯(cuò)誤,頻率在5-10分鐘就會(huì)出現(xiàn)一次。
我們首先要明白PLEG是什么?
PLEG 全稱叫 Pod Lifecycle Event Generator,即 Pod 生命周期事件生成器。實(shí)際上它只是 Kubelet 中的一個(gè)模塊,主要職責(zé)就是通過每個(gè)匹配的 Pod 級別事件來調(diào)整容器運(yùn)行時(shí)的狀態(tài),并將調(diào)整的結(jié)果寫入緩存,使 Pod 的緩存保持最新狀態(tài)。先來聊聊 PLEG 的出現(xiàn)背景。在 Kubernetes 中,每個(gè)節(jié)點(diǎn)上都運(yùn)行著一個(gè)守護(hù)進(jìn)程 Kubelet 來管理節(jié)點(diǎn)上的容器,調(diào)整容器的實(shí)際狀態(tài)以匹配 spec 中定義的狀態(tài)。具體來說,Kubelet 需要對兩個(gè)地方的更改做出及時(shí)的回應(yīng):
- Pod spec 中定義的狀態(tài)
- 容器運(yùn)行時(shí)的狀態(tài)
對于 Pod,Kubelet 會(huì)從多個(gè)數(shù)據(jù)來源 watch Pod spec 中的變化。對于容器,Kubelet 會(huì)定期(例如,10s)輪詢?nèi)萜鬟\(yùn)行時(shí),以獲取所有容器的最新狀態(tài)。隨著 Pod 和容器數(shù)量的增加,輪詢會(huì)產(chǎn)生不可忽略的開銷,并且會(huì)由于 Kubelet 的并行操作而加劇這種開銷(為每個(gè) Pod 分配一個(gè) goruntine,用來獲取容器的狀態(tài))。輪詢帶來的周期性大量并發(fā)請求會(huì)導(dǎo)致較高的 CPU 使用率峰值(即使 Pod 的定義和容器的狀態(tài)沒有發(fā)生改變),降低性能。最后容器運(yùn)行時(shí)可能不堪重負(fù),從而降低系統(tǒng)的可靠性,限制 Kubelet 的可擴(kuò)展性。為了降低 Pod 的管理開銷,提升 Kubelet 的性能和可擴(kuò)展性,引入了 PLEG,改進(jìn)了之前的工作方式:
- 減少空閑期間的不必要工作(例如 Pod 的定義和容器的狀態(tài)沒有發(fā)生更改)。
- 減少獲取容器狀態(tài)的并發(fā)請求數(shù)量。
所以我們看這一切都離不開kubelet與pod的容器運(yùn)行時(shí)。
一方面,kubelet扮演的是集群控制器的角色,它定期從API Server獲取Pod等相關(guān)資源的信息,并依照這些信息,控制運(yùn)行在節(jié)點(diǎn)上Pod的執(zhí)行;
另外一方面,kubelet作為節(jié)點(diǎn)狀況的監(jiān)視器,它獲取節(jié)點(diǎn)信息,并以集群客戶端的角色,把這些狀況同步到API Server。在這個(gè)問題中,kubelet扮演的是第二種角色。Kubelet會(huì)使用上圖中的NodeStatus機(jī)制,定期檢查集群節(jié)點(diǎn)狀況,并把節(jié)點(diǎn)狀況同步到API Server。而NodeStatus判斷節(jié)點(diǎn)就緒狀況的一個(gè)主要依據(jù),就是PLEG。
PLEG是Pod Lifecycle Events Generator的縮寫,基本上它的執(zhí)行邏輯,是定期檢查節(jié)點(diǎn)上Pod運(yùn)行情況,如果發(fā)現(xiàn)感興趣的變化,PLEG就會(huì)把這種變化包裝成Event發(fā)送給Kubelet的主同步機(jī)制syncLoop去處理。但是,在PLEG的Pod檢查機(jī)制不能定期執(zhí)行的時(shí)候,NodeStatus機(jī)制就會(huì)認(rèn)為,這個(gè)節(jié)點(diǎn)的狀況是不對的,從而把這種狀況同步到API Server。
整體的工作流程如下圖所示,虛線部分是 PLEG 的工作內(nèi)容。
以node notready 這個(gè)場景為例,來講解PLEG:
Kubelet中的NodeStatus機(jī)制會(huì)定期檢查集群節(jié)點(diǎn)狀況,并把節(jié)點(diǎn)狀況同步到API Server。而NodeStatus判斷節(jié)點(diǎn)就緒狀況的一個(gè)主要依據(jù),就是PLEG。
PLEG定期檢查節(jié)點(diǎn)上Pod運(yùn)行情況,并且會(huì)把pod 的變化包裝成Event發(fā)送給Kubelet的主同步機(jī)制syncLoop去處理。但是,在PLEG的Pod檢查機(jī)制不能定期執(zhí)行的時(shí)候,NodeStatus機(jī)制就會(huì)認(rèn)為這個(gè)節(jié)點(diǎn)的狀況是不對的,從而把這種狀況同步到API Server,我們就會(huì)看到 not ready 。
PLEG有兩個(gè)關(guān)鍵的時(shí)間參數(shù),一個(gè)是檢查的執(zhí)行間隔,另外一個(gè)是檢查的超時(shí)時(shí)間。以默認(rèn)情況為準(zhǔn),PLEG檢查會(huì)間隔一秒,換句話說,每一次檢查過程執(zhí)行之后,PLEG會(huì)等待一秒鐘,然后進(jìn)行下一次檢查;而每一次檢查的超時(shí)時(shí)間是三分鐘,如果一次PLEG檢查操作不能在三分鐘內(nèi)完成,那么這個(gè)狀況,會(huì)被NodeStatus機(jī)制當(dāng)做集群節(jié)點(diǎn)NotReady的憑據(jù),同步給API Server。
PLEG Start就是啟動(dòng)一個(gè)協(xié)程,每個(gè)relistPeriod(1s)就調(diào)用一次relist,根據(jù)最新的PodStatus生成PodLiftCycleEvent。relist是PLEG的核心,它從container runtime中查詢屬于kubelet管理containers/sandboxes的信息,并與自身維護(hù)的 pods cache 信息進(jìn)行對比,生成對應(yīng)的 PodLifecycleEvent,然后輸出到 eventChannel 中,通過 eventChannel 發(fā)送到 kubelet syncLoop 進(jìn)行消費(fèi),然后由 kubelet syncPod 來觸發(fā) pod 同步處理過程,最終達(dá)到用戶的期望狀態(tài)。
PLEG is not healthy的原因
這個(gè)報(bào)錯(cuò)清楚地告訴我們,容器 runtime 是不正常的,且 PLEG 是不健康的。這里容器 runtime 指的就是 docker daemon 。Kubelet 通過操作 docker daemon 來控制容器的生命周期。而這里的 PLEG,指的是 pod lifecycle event generator。PLEG 是 kubelet 用來檢查 runtime 的健康檢查機(jī)制。這件事情本來可以由 kubelet 使用 polling 的方式來做。但是 polling 有其高成本的缺陷,所以 PLEG 應(yīng)用而生。PLEG 嘗試以一種“中斷”的形式,來實(shí)現(xiàn)對容器 runtime 的健康檢查,雖然實(shí)際上,它同時(shí)用了 polling 和”中斷”這樣折中的方案。
從 Docker 1.11 版本開始,Docker 容器運(yùn)行就不是簡單通過 Docker Daemon 來啟動(dòng)了,而是通過集成 containerd、runc 等多個(gè)組件來完成的。雖然 Docker Daemon 守護(hù)進(jìn)程模塊在不停的重構(gòu),但是基本功能和定位沒有太大的變化,一直都是 CS 架構(gòu),守護(hù)進(jìn)程負(fù)責(zé)和 Docker Client 端交互,并管理 Docker 鏡像和容器?,F(xiàn)在的架構(gòu)中組件 containerd 就會(huì)負(fù)責(zé)集群節(jié)點(diǎn)上容器的生命周期管理,并向上為 Docker Daemon 提供 gRPC 接口。
PLEG在每次迭代檢查中會(huì)調(diào)用runc的 relist() 函數(shù)干的事情,是定期重新列出節(jié)點(diǎn)上的所有容器,并與上一次的容器列表進(jìn)行對比,以此來判斷容器狀態(tài)的變換。相當(dāng)于docker ps來獲取所有容器,在通過docker Inspect來獲取這些容器的詳細(xì)信息。在有問題的節(jié)點(diǎn)上,通過 docker ps命令會(huì)沒有響應(yīng),這說明上邊的報(bào)錯(cuò)是準(zhǔn)確的。
經(jīng)常出現(xiàn)的場景
出現(xiàn) pleg not healthy,一般有以下幾種可能:
- 容器運(yùn)行時(shí)無響應(yīng)或響應(yīng)超時(shí),如 docker進(jìn)程響應(yīng)超時(shí)(比較常見)
- 該節(jié)點(diǎn)上容器數(shù)量過多,導(dǎo)致 relist 的過程無法在 3 分鐘內(nèi)完成
- relist 出現(xiàn)了死鎖,該 bug 已在 Kubernetes 1.14 中修復(fù)。
- 網(wǎng)絡(luò)
排查處理過程描述
1.我們在問題節(jié)點(diǎn)上執(zhí)行top,發(fā)現(xiàn)有進(jìn)程名為scope的進(jìn)程cpu占用率一直是100%。通過翻閱資料得知 systemd.scope:范圍(scope)單元并不通過單元文件進(jìn)行配置, 而是僅能以編程的方式通過 systemd D-Bus 接口創(chuàng)建。范圍單元的名稱都以 ".scope" 作為后綴。與服務(wù)(service)單元不同,范圍單元用于管理 一組外部創(chuàng)建的進(jìn)程, 它自身并不派生(fork)任何進(jìn)程。范圍(scope)單元的主要目的在于以分組的方式管理系統(tǒng)服務(wù)的工作進(jìn)程。2.在繼續(xù)執(zhí)行在有問題的節(jié)點(diǎn)上,通過 docker ps命令會(huì)沒有響應(yīng)。說明容器 runtime也是有問題的。那容器 runtime與systemd有不有關(guān)系呢?3.我們通過查閱到阿里的一篇文章,阿里巴巴 Kubernetes 集群問題排查思路和方法。找到了關(guān)系,有興趣的可以根據(jù)文末提供的鏈接去細(xì)致了解。以下是在該文章中截取的部分內(nèi)容。
什么是D-Bus呢?
通過阿里巴巴 Kubernetes 集群問題排查思路和方法[1]中如下描述:在 Linux 上,dbus 是一種進(jìn)程間進(jìn)行消息通信的機(jī)制。
RunC 請求 D-Bus
容器 runtime 的 runC 命令,是 libcontainer 的一個(gè)簡單的封裝。這個(gè)工具可以用來管理單個(gè)容器,比如容器創(chuàng)建和容器刪除。在上節(jié)的最后,我們發(fā)現(xiàn) runC 不能完成創(chuàng)建容器的任務(wù)。我們可以把對應(yīng)的進(jìn)程殺掉,然后在命令行用同樣的命令啟動(dòng)容器,同時(shí)用 strace 追蹤整個(gè)過程。
分析發(fā)現(xiàn),runC 停在了向帶有 org.free 字段的 dbus socket 寫數(shù)據(jù)的地方。
解決問題
最后可以斷定是systemd的問題,我們用 systemctl daemon-reexec 來重啟 systemd,問題消失了。所以更加確定是systemd的問題。
具體原因大家可以參考:https://www.infoq.cn/article/t_ZQeWjJLGWGT8BmmiU4這篇文章。
根本上解決問題是:將systemd升級到 v242-rc2,升級后需要重啟操作系統(tǒng)。(https://github.com/lnykryn/systemd-rhel/pull/322)
總結(jié)
PLEG is not healthy的問題居然是因?yàn)閟ystemd導(dǎo)致的。最后通過將systemd升級到 v242-rc2,升級后需要重啟操作系統(tǒng)。(https://github.com/lnykryn/systemd-rhel/pull/322) 參考資料
- Kubelet: Pod Lifecycle Event Generator (PLEG)
- Kubelet: Runtime Pod Cache
- relist() in kubernetes/pkg/kubelet/pleg/generic.go
- Past bug about CNI — PLEG is not healthy error, node marked NotReady
- https://www.infoq.cn/article/t_ZQeWjJLGWGT8BmmiU4
- https://cloud.tencent.com/developer/article/1550038