1. 程式人生 > >記一次kubernetes驅逐踩坑

記一次kubernetes驅逐踩坑

最近在公司的線上伺服器上發現了一個現象: 將某個node的kubelet短暫的停掉之後,其上的pod馬上會被驅逐,這讓筆者大吃一驚,印象之中,停掉kubelet後,該node會變為NotReady狀態,隨後controller-manger會經過一段時間才開始驅逐其上的pod。還有個引數專門來控制這個時間:

--pod-eviction-timeout The grace period for deleting pods on failed nodes. (default 5m0s)`

該引數預設值為5min, 也就是說當node NotReady之後,最少也得五分鐘之後其上的pod才會被驅逐。但是現實情況明顯不符合預期啊,這樣就有點奇怪了。
鑑於該問題影響巨大,筆者果斷開啟了debug之旅。

首先我們從直接原因下手,需要了解一下當node NotReady之後,controller-manager是如何判斷並驅逐其上的pod的,這部分工作是由node lifecycel controller模組負責。

node lifecycle controller 主要負責對node生命週期進行檢查,判斷node是否存活,如果長時間檢測不到node心跳則驅逐其上的pod。在目前的版本(v1.16)中,預設開啟了TaintBasedEvictions, TaintNodesByCondition這兩個feature gate,則所有node生命週期管理都是通過condition + taint的方式進行管理。其主要邏輯由三部分組成:

  1. 不斷地檢查所有node狀態,設定對應的condition
  2. 不斷地根據node condition 設定對應的taint
  3. 不斷地根據taint驅逐node上面的pod

想要詳細瞭解node lifecycle controller工作機制的同學可以翻閱筆者上一篇文章: kubernetes中node心跳處理邏輯分析

檢視程式碼發現--pod-eviction-timeout並未起作用,原來在v1.13版本之前TaintBasedEvictions功能還未開啟,此時node not-ready之後,controller直接判斷not-ready時間是否超過了--pod-eviction-timeout

,超過就進行刪除。而v1.13,TaintBasedEvictions功能開啟之後就不會使用了該引數,轉而使用了上面描述的condition+taint方案。也就是說node NotReady之後,pod的驅逐時間完全由每個pod toleration中 tolerationSecond決定,而不是由controller-manager的引數--pod-eviction-timeout統一決定。這樣想想也合情合理,每個pod對於故障的容忍時間不同,tolerationSecond可以更加靈活地為每個pod指定不同的驅逐時間。

這樣說來,所有的pod都需要設定一個toleration才對,查閱相關資料後發現,社群已經有了一個DefaultTolerationSeconds admisson controller自動地幫助我們設定toleration,每次建立更新pod, 在請求傳送到apiserver之後會自動設定5min的預設toleration。

This admission controller sets the default forgiveness toleration for pods to tolerate the taints notready:NoExecute and unreachable:NoExecute for 5 minutes, if the pods don’t already have toleration for taints node.kubernetes.io/not-ready:NoExecute or node.alpha.kubernetes.io/unreachable:NoExecute.

文件上顯示該admission controller預設開啟,但是為什麼我司的環境上面沒有生效,仔細看了一下文件,是因為自己使用了一個deprecated的引數:--admission-control,使用這個引數的話必須顯式指定所有要開啟的admisson controller plugin列表。該引數在v1.10被廢棄,由兩個新的引數--enable-admission-plugins--disable-admission-plugins替換,這兩個引數如果不指定的話會有預設值,其中DefaultTolerationSeconds就屬於--enable-admission-plugins引數的預設值之一,也就是會預設開啟該plugin。又是一個升級導致的坑! 正確修改了該引數之後,新建立的pod就會預設帶上了toleration,DefaultTolerationSeconds adminssion controller plugin總算生效了。 

新建立的pod總算沒有問題了,但是對於叢集中已經存在的pod還沒有toleration該怎麼辦? 顯然社群對這種情況未做處理,DefaultTolerationSeconds是社群很早就開發的一個feature,但是TaintBasedEvictions是v1.13才預設開啟,估計社群在開發時前後沒有做好相容。翻了下DefaultTolerationSecondsadminssion plugin原始碼,發現該plugin對於pod create, update操作都會設定toleration, 所以一般情況下, 我們升級叢集的時候,總是能觸發pod發生update,該toleration會自動新增上,無需過多操作,大可不必擔心,心裡知道有這個事情並檢查一下就行。但是筆者的環境已經升級完成了,只能手動觸發pod update了,思來想去,想要觸發pod update又不能影響業務,給pod打label是一個比較好的方式,於是筆者寫了一個指令碼將叢集中所有的pod都打了一個無關緊要的label, 觸發uodate後就自動添加了toleration。

至此整個修復工作全部完成,回過頭來仔細想想,kubernetes版本間升級挑戰還是挺大的,相容性問題防不勝防,很可能兩個小版本間完全相容,沒任何問題,但是當一步步從低版本升級上來的時候問題就出現了,而且在相容性測試的時候難以覆蓋到每種情況,很可能需要很久問題才能暴露出來。對於這種情況,唯有掌握內部機理,熟讀原始碼才能快速診斷,修復