Kubernetes集羣Node間歇性NotReady,查到最後竟然是這個原因

 

原文地址:https://mp.weixin.qq.com/s/aNt1uI1RWf73Y3Dd6gmnLg

image

被這個問題困擾一段時間了,先後進行了各種假設,然後又一一推翻,最後找到的原因讓人很意外。

這裏簡單記錄一下問題的排查過程,然後直接拋答案,如果想了解詳細的調查過程、調查過程中的所思所想和走過的彎路,點擊「閱讀原文」。

出現問題的集羣規模不大,總共 200 多個 Node,最早是從 Kubernetes 的事件中發現這個問題的,同事反饋時不時地監聽到 NodeNotReady 事件,但是登陸集羣查看時 Node 的狀態都是 Ready。

一開始忙其它的事情沒太在意,後來這種「幽靈式」的 NodeNotReady 越來越頻繁,並且持續的時間越來越長,不但觸發了報警系統,而且 Node 上的 Pod 也被漂移了,開始着手調查。

線索非常少,檢查 kube-controller-manager 的日誌,只看到一條 NodeNotReady 的事件日誌,在 kubelet 的日誌裏也翻不到有價值的信息。調整日誌級別後,只從 kube-controller-manager 的日誌裏得知一個事實:

Node 的狀態長時間沒有得到更新,超過 gracePeriod,被認定爲 NotReady。

關鍵是爲什麼會延遲?開始胡思亂想,是不是網絡不好,會不會 etcd 狀態不對,要麼是 docker 又 hang 了,還是 Go 的定時器有問題?亂想一氣,亂查一通,沒啥結論,還是老老實實地捋代碼吧。

Kubelet 默認上報頻率是 10s 一次,kube-controller-manager 的 gracePeriod 默認是 40s,調整這兩個參數只能延緩、掩蓋問題,不能解決問題,如果你遇到同樣的問題,不要在這些參數上耽誤功夫了。

Kubelet 的版本是 1.9.11,這個版本的 kubelet 在向 kbue-apiserver 提交狀態信息時,不會打印日誌。把添加了幾行日誌代碼的 kubelet 推送到問題集羣中,然後把 kube-apiserver 的日誌級別調整爲 2。這樣就有足夠的信息來界定問題區間了。

「幽靈式」的 NodeNotReady 非常頻繁,早晚不歇,一天好多次,這對調查過程非常有幫助!讓我們有足夠的案例用來分析,如果是個把月出現一次,那就抓瞎了。

很快從新的案例中收集到足夠的信息,把幾處日誌綜合起來看,確定 kubelet 到 kube-apiserver 的通信沒有問題,更新請求實時送達了 kube-apiserver,kube-apiserver 收到請求的時間與 kube-controller-manager 中記錄的時間也完全匹配。

問題在 kubelet 自身,上一次更新提交完成後,下一次更新很準時地在 10s 後啓動(證明 go 的定時器工作正常),但是這次更新卻莫名其妙地卡住了!

Github 上有一個 issue,當 kubelet 的 heartBeatClient 建立的連接被對方中斷後,kubelet 會 hang。看到這個 issue 時以爲找到了答案,出於謹慎,添加了更多的調試日誌,再次推送到問題集羣,試圖拿到更有力的證據。

結果很意外,本來滿心認爲是 kubelet 通過 heartBeatClient 獲取信息或者向 kube-apiserver 提交更新的時候發生了延遲。事實卻是,一個 if 判斷和兩行賦值語句,就耗費了足足了 26s !匪夷所思:

image

感覺無法理解,於是又開始瞎想了,難道是 Go 的 gc 有問題?賦值語句耗時如此之長,實在沒別的地方可想,翻了翻 Go 的內存模型,內存回收延遲都是毫秒級的,不可能是 gc 的問題。

突然想到之前遇到一個 kubelet 進程被長時間掛起,最後被 kernel 殺死的場景,會不會是 node 的系統有問題給 kubelet 進程按下了暫停鍵?

用 top 觀察 kubelet 進程的狀態,發現 wa 時間佔比有時候會突然升高到 20%,然後用 iostat 發現根分區磁盤和數據盤的 util 都是 100%,處於過飽和狀態,await 時間一度升到 20 多秒!

然後用 pidstat 找到了正在頻繁進行 IO 操作的 java 進程,通過向上找父進程的方法找到了它所在的容器。這個容器正在瘋狂地吐日誌,容器內進程不停地向標準輸出寫入日誌,日誌被收集到 container 目錄中。

但沒有非常直接地證據證明就是這個原因,把運行有這批容器的 node 找出來與發生過 「幽靈」Not Ready 的 node 對比,發現恰好是對應的,所有發生了「幽靈」Not Ready 的 node 上都運行有正在瘋狂吐日誌的容器,沒有運行該容器的 node 沒有發生 Not Ready。

聯繫相關方將寫入到標準輸出的日誌停止,node 磁盤設備的 util 從 100% 降低到接近於 0,「幽靈」Not Ready 也不出現了。

問題的根源其實還沒有徹底挖出來,在用 pidstat 查看 io 情況的時候,發現容器狂刷日誌時 dockerd 的寫操作也很高,是不是兩者相互作用才引發的問題?

最近業務回暖,調用量增加日誌隨之增多,再聯想到 docker ps 無響應的事情已經遇到了不止一次兩次了,這裏估計有戲,找機會再挖一挖。

另外聯繫業務方調整日誌輸出也不治本,只不過都是內部系統,規模也不大,比較好協調罷了,需要找一個業務無感知的限制方法。

調查這些問題真是耗時間,其它工作的進度又又又延後了......[圖片上傳失敗...(image-4176ba-1562938394780)]

[圖片上傳失敗...(image-342c2b-1562938394780)]

[圖片上傳失敗...(image-3ae320-1562938394779)]

這裏只是大概敘述一下,完整調查過程見「閱讀原文」。

上一篇:以Go的map是否併發安全爲例,介紹最權威的Go語言資料的使用方法

閱讀原文

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