一圖讀懂k8s informer client-go
概述
為什么要有k8s informer
我們都知道可以使用k8s的Clientset來獲取所有的原生資源對象,那么怎么能持續的獲取集群的所有資源對象,或監聽集群的資源對象數據的變化呢?這里不需要輪詢去不斷執行List操作,而是調用Watch接口,即可監聽資源對象的變化,當資源對象發生變化,客戶端即可通過Watch接口收到資源對象的變化。
Watch接口雖然可以直接使用,但一般情況下很少直接使用,因為往往由于集群中的資源較多,我們需要自己在客戶端去維護一套緩存,而這個維護成本比較大。
也是因為如此,client-go提供了自己的實現機制,Informers應運而生。
什么是k8s informer
informers實現了持續獲取集群的所有資源對象、監聽集群的資源對象變化功能,并在本地維護了全量資源對象的內存緩存,以減少對apiserver、對etcd的請求壓力。Informers在啟動的時候會首先在客戶端調用List接口來獲取全量的對象集合,然后通過Watch接口來獲取增量的對象,然后更新本地緩存。
此外informers也有很強的健壯性,當長期運行的watch連接中斷時,informers會嘗試拉起一個新的watch請求來恢復連接,在不丟失任何事件的情況下恢復事件流。另外,informers還可以配置一個重新同步的周期參數,每間隔該周期,informers就會重新List全量數據。
在informers的使用上,通常每個GroupVersionResource(GVR)只實例化一個informers,但有時候我們在一個應用中往往會在多個地方對同一種資源對象都有informer的需求,所以就有了共享informer,即SharedInformerFactory。所以可以通過使用SharedInformerFactory來實例化informers,這樣本地內存緩存就只有一份,通知機制也只有一套,大大提高了效率,減少了資源浪費。
一圖讀懂k8s informer
這里先給出一張k8s informer的詳細架構圖;
從圖中可以看出,k8s informer主要包括以下幾個部分:
1.Reflector
(1)Reflector從kube-apiserver中list資源對象列表,然后調用DeltaFIFO的Replace方法將object包裝成Sync/Deleted類型的Delta丟進DeltaFIFO中;
(2)Reflector從kube-apiserver中watch資源對象的變化,然后調用DeltaFIFO的Add/Update/Delete方法將object包裝成Added/Updated/Deleted類型的Delta丟到DeltaFIFO中;
2.DeltaFIFO
DeltaFIFO中存儲著一個map和一個queue;
(1)其中queue可以看成是一個先進先出隊列,一個object進入DeltaFIFO中,會判斷queue中是否已經存在該object key,不存在則添加到隊尾;
(2)map即map[object key]Deltas,是object key和Deltas的映射,Deltas是Delta的切片類型,Delta中存儲著DeltaType和object;另外,Deltas最末尾的兩個Deleted類型的Delta會被去重;
DeltaType有4種,分別是Added、Updated、Deleted、Sync
3.Controller
Controller從DeltaFIFO的queue中pop一個object key出來,并從DeltaFIFO的map中獲取其對應的 Deltas出來進行處理,遍歷Deltas,根據object的變化類型更新Indexer本地緩存,并通知Processor相關對象有變化事件發生:
(1)如果DeltaType是Deleted,則調用Indexer的Delete方法,將Indexer本地緩存中的object刪除,并構造deleteNotification struct,通知Processor做處理;
(2)如果DeltaType是Added/Updated/Sync,調用Indexer的Get方法從Indexer本地緩存中獲取該對象,存在則調用Indexer的Update方法來更新Indexer緩存中的該對象,隨后構造updateNotification struct,通知Processor做處理;如果Indexer中不存在該對象,則調用Indexer的Add方法將該對象存入本地緩存中,并構造addNotification struct,通知Processor做處理;
4.Processor
Processor根據Controller的通知,即根據對象的變化事件類型(addNotification、updateNotification、deleteNotification),調用相應的ResourceEventHandler(addFunc、updateFunc、deleteFunc)來處理對象的變化。
5.Indexer
Indexer中有informer維護的指定資源對象的相對于etcd數據的一份本地內存緩存,可通過該緩存獲取資源對象,以減少對apiserver、對etcd的請求壓力。
informer所維護的緩存依賴于threadSafeMap結構體中的items屬性,其本質上是一個用map構建的鍵值對,資源對象都存在items這個map中,key為資源對象的namespace/name組成,value為資源對象本身,這些構成了informer的本地緩存。
Indexer除了維護了一份本地內存緩存外,還有一個很重要的功能,便是索引功能了。索引的目的就是為了快速查找,比如我們需要查找某個node節點上的所有pod、查找某個命名空間下的所有pod等,利用到索引,可以實現快速查找。關于索引功能,則依賴于threadSafeMap結構體中的indexers與indices屬性。
6.ResourceEventHandler
用戶根據自身處理邏輯需要,注冊自定義的的ResourceEventHandler,當對象發生變化時,將觸發調用對應類型的ResourceEventHandler來做處理。
k8s informer詳細分析
之前的文章也對k8s informer進行了一系列的詳細分析,有興趣的可以看一下對k8s informer的詳細分析,這里給出k8s client-go/k8s informer分析系列的鏈接導航:
(1)informer概要分析;
(2)informer之初始化與啟動分析;
(3)informer之Reflector分析;
(4)informer之DeltaFIFO分析;
(5)informer之Controller&Processor分析;
(6)informer之Indexer分析;