kubernetes-kubelet進程源碼分析(二)

kubelet關鍵代碼分析

在上篇博文,我們分析了kubelet進程的啓動流程,大致明白了kubelet的核心個哦你工作流程就是不斷從Pod Source中獲取與本節點相關的Pod,然後開始加工處理,所以我們先來分析Pod source部分代碼。前面我們提到,kubelet可是同時支持三類Pod source,爲了能夠將不同的Pod source匯聚到一起統一處理,谷歌特地設計了Podconfig這個對象,其代碼如下:

其中,source屬性包括了當前加載的所有Pod source類型,sourceLock是source的排他鎖,在新增Pod source的方法裏使用它來避免共享衝突。

在Pod發生變動時,例如Pod創建、刪除或更新,相關的Pod source就會產生對應的PodUpdate事件並推送到channel,爲了能夠統一處理來自多個source的channel,谷歌設計了config.mux這個聚合器,它負責監聽多路channel,當接收到channel發來的事件後,交給merger對象進行統一處理,merger對象最終把多路channel發來的時間合併寫入updates這個匯聚channel等待處理。

下面是config.mux的結構體定義,其屬性sources爲一個channel Map,key是對應的Pod source的類型:

我們繼續深入分析config.Mux的工作過程,前面提到,kubelet在啓動過程中在makePodSourceConfig方法裏創建了一個PodConfig對象,並且根據啓動參數來決定要加載哪些類型的Pod Source,在這個過程中調用了下述方法來創建一個對應的channel:

而channel具體的創建過程則在config.Mux裏,channel創建完成後被加入config.mux的sources並啓動一個協程開始監聽消息,代碼如下:

config.Mux的上述listen方法很簡單,就是監聽新創建的channel,一旦發現channel上有數據就交給merger進行處理:

 

我們先來看看Pod source是如何發送PodUpdate事件到自己所在的channel上的,在上篇博文,我們所見到的下面這段代碼創建了一個config file類型的Pod source:

在NewSourceFile方法裏啓動了一個協程,每隔指定時間(kc.FileCheckFrequency)就執行一次sourceFile的run方法,在run方法裏所調用的主體邏輯就是下面的函數:

看一下上面的代碼,我們就大致明白了config file類型的Pod source是如何工作的:它從指定的目錄中加載多了Pod定義文件並轉換爲Pod列表或者加載單個Pod定義文件並轉化爲單個Pod,然後生成對應的全量類型的PodUpdate事件並寫入channel中去。

接下來我們分析merger對象,Podconfig裏的merger對象其實是一個config.podStorage實例,它同時是PodConfig的pods屬性的一個引用。podStorage的源碼位於pkg/kubelet.config/config.go裏,其定義如下:

我們看到podstorage的關鍵屬性解釋如下。

1.pods:類型是Map,存放每個Pod source上拉過來的pod數據,是podstorage當前保存全量Pod的地方

2.updates:它就是PodConfig裏的updates屬性的一個引用

3.mode:表明podstorage的pod事件通知模式,有以下幾種:

1)PodConfigNotificationSnapshot:全量快照通知模式

2)PodConfigNotificationSnapshotAndUpdates:全量快照+更新Pod通知模式(代碼中創建podstorage實例時採用的模式)

3)PodConfigNotificationIncremental:增量通知模式

podStorage實現的merge接口的源碼如下:

在上述merge過程中,先調用內部函數merge,將Pod source的channel上發來的PodUpdate事件分解爲對應的新增、更新及刪除等三類PodUpdate事件,然後判斷是否有更新事件,如果有,則直接寫入彙總的channel裏(podstorage.updates),然後調用mergestate函數複製一份podstorage的當前全量Pod列表,以此產生一個全量的PodUpdate事件並寫入彙總的channel中,從而實現了多Pod Source channel的匯聚邏輯。

分析完merger過程以後,我們接下來看看是什麼對象,以及如何消費這個彙總的channel。在上篇博文提到,在kubelet進程啓動的過程中調用了startKubelet方法,此方法首先啓動一個協程,讓kubelet處理來自PodSource的Pod update消息,即下面這行代碼:

‘’

其中,PodConfig的updates()方法返回了前面我們所說的彙總channel變量的一個引用,下面是kubelet的Run(updates < -chan PodUpdate)方法的代碼:

上述代碼首先啓動了一個HTTP file server來遠程獲取本節點的系統日誌,接下來根據啓動參數的設置來決定是否在指定的docker容器中啓動kubelet進程,然後分別啓動Image manager(負責Image GC)、CAdvisor(Docker性能監控)、Container Manger(Container GC)、OOM Watcher(OOM監測)、Status Manager(負責同步本節點上Pod的狀態到API Server上)等組件,最後進入syncLoop方法中,無線循環調用下面的syncLoopIteration方法:

在上述代碼中,如果從Channel中拉取到了PodUpdate事件,則先調用podManager的UpdatePods方法來確定此PodUpdate的同步類型,並將結果放入podSyncTypes這個Map中,同時爲了提升處理效率,在代碼中增加了持續循環拉取PodUpdate數據直到channel爲空爲止的一段邏輯,在方法的最後,調用syncHandler接口來完成Pod同步的具體邏輯,從而實現了PodUpdate事件的高效批處理模式。

syncHandler在這裏就是kubelet實例,它的syncPods方法比較長,其主要邏輯如下:

1.將傳入的全量Pod,與Statusmanager中當前保存的Pod集合進行對比,刪除Statusmanager中當前已經不存在的Pod(孤兒Pod)。

2.調用kubelet的admitPods方法以過濾掉不適合本節點創建的Pod。此方法首先過濾掉狀態爲failed或者succeeded的pod,接着過濾掉不適合本節點的Pod,比如Host Port衝突、Node Label的約束不匹配及Node的可用資源不足等情況;最後檢查磁盤的使用情況,如果磁盤的可用空間不足,則過濾掉所有Pod。

3.對上述過濾後的Pod集合中的每一個Pod調用podWorkers的UpdatePod方法,而此方法內部創建了一個Pod的workUpdate事件併發布到該Pod對應的一個Work Channel上。

4.對於已經刪除或者不存在的Pod,通知podWorkers刪除相關聯的work channel。

5.對比Node當前運行中的Pod及目標Pod列表,殺掉多餘的Pod,並且調用docker runtime API,重新獲取當前運行中的Pod列表信息。

6.清理孤兒Pod所遺留的PV和磁盤目錄。

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