我們都知道可以使用k8s的Clientset來獲取所有的原生資源物件,那麼怎麼能持續的獲取叢集的所有資源物件,或監聽叢集的資源物件資料的變化呢?這裡不需要輪詢去不斷執行List操作,而是呼叫Watch介面,即可監聽資源物件的變化,當資源物件發生變化,使用者端即可通過Watch介面收到資源物件的變化。
Watch介面雖然可以直接使用,但一般情況下很少直接使用,因為往往由於叢集中的資源較多,我們需要自己在使用者端去維護一套快取,而這個維護成本比較大。
也是因為如此,client-go提供了自己的實現機制,Informers應運而生。
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主要包括以下幾個部分:
(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中;
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
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做處理;
Processor根據Controller的通知,即根據物件的變化事件型別(addNotification、updateNotification、deleteNotification),呼叫相應的ResourceEventHandler(addFunc、updateFunc、deleteFunc)來處理物件的變化。
Indexer中有informer維護的指定資源物件的相對於etcd資料的一份本地記憶體快取,可通過該快取獲取資源物件,以減少對apiserver、對etcd的請求壓力。
informer所維護的快取依賴於threadSafeMap結構體中的items屬性,其本質上是一個用map構建的鍵值對,資源物件都存在items這個map中,key為資源物件的namespace/name組成,value為資源物件本身,這些構成了informer的本地快取。
Indexer除了維護了一份本地記憶體快取外,還有一個很重要的功能,便是索引功能了。索引的目的就是為了快速查詢,比如我們需要查詢某個node節點上的所有pod、查詢某個名稱空間下的所有pod等,利用到索引,可以實現快速查詢。關於索引功能,則依賴於threadSafeMap結構體中的indexers與indices屬性。
使用者根據自身處理邏輯需要,註冊自定義的的ResourceEventHandler,當物件發生變化時,將觸發呼叫對應型別的ResourceEventHandler來做處理。
之前的文章也對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分析;