天天看點

一圖讀懂k8s informer client-go

k8s client-go k8s informers實作了持續擷取叢集的所有資源對象、監聽叢集的資源對象變化功能,并在本地維護了全量資源對象的記憶體緩存,以減少對apiserver、對etcd的請求壓力。Informers在啟動的時候會首先在用戶端調用List接口來擷取全量的對象集合,然後通過Watch接口來擷取增量的對象,然後更新本地緩存。

概述

為什麼要有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 client-go

從圖中可以看出,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分析;

繼續閱讀