<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Kubernetes 中 Informer 的使用

          共 1301字,需瀏覽 3分鐘

           ·

          2020-08-04 07:30

          前面我們在使用 Clientset 的時候了解到我們可以使用 Clientset 來獲取所有的原生資源對象,那么如果我們想要去一直獲取集群的資源對象數(shù)據(jù)呢?豈不是需要用一個輪詢?nèi)ゲ粩鄨?zhí)行 List() 操作?這顯然是不合理的,實際上除了常用的 CRUD 操作之外,我們還可以進(jìn)行 Watch 操作,可以監(jiān)聽資源對象的增、刪、改、查操作,這樣我們就可以根據(jù)自己的業(yè)務(wù)邏輯去處理這些數(shù)據(jù)了。

          Watch 通過一個 event 接口監(jiān)聽對象的所有變化(添加、刪除、更新):

          // staging/src/k8s.io/apimachinery/pkg/watch/watch.go// Interface 可以被任何知道如何 watch 和通知變化的對象實現(xiàn)type Interface interface {  // Stops watching. Will close the channel returned by ResultChan(). Releases  // any resources used by the watch.  Stop()
          // Returns a chan which will receive all the events. If an error occurs // or Stop() is called, this channel will be closed, in which case the // watch should be completely cleaned up. ResultChan() <-chan Event}

          watch 接口的 ResultChan 方法會返回如下幾種事件:

          // staging/src/k8s.io/apimachinery/pkg/watch/watch.go// EventType 定義可能的事件類型type EventType string
          const ( Added EventType = "ADDED" Modified EventType = "MODIFIED" Deleted EventType = "DELETED" Bookmark EventType = "BOOKMARK" Error EventType = "ERROR"
          DefaultChanSize int32 = 100)
          // Event represents a single event to a watched resource.// +k8s:deepcopy-gen=truetype Event struct { Type EventType
          // Object is: // * If Type is Added or Modified: the new state of the object. // * If Type is Deleted: the state of the object immediately before deletion. // * If Type is Bookmark: the object (instance of a type being watched) where // only ResourceVersion field is set. On successful restart of watch from a // bookmark resourceVersion, client is guaranteed to not get repeat event // nor miss any events. // * If Type is Error: *api.Status is recommended; other types may make sense // depending on context. Object runtime.Object}

          這個接口雖然我們可以直接去使用,但是實際上并不建議這樣使用,因為往往由于集群中的資源較多,我們需要自己在客戶端去維護(hù)一套緩存,而這個維護(hù)成本也是非常大的,為此 client-go 也提供了自己的實現(xiàn)機制,那就是 Informers。Informers 是這個事件接口和帶索引查找功能的內(nèi)存緩存的組合,這樣也是目前最常用的用法。Informers 第一次被調(diào)用的時候會首先在客戶端調(diào)用 List 來獲取全量的對象集合,然后通過 Watch 來獲取增量的對象更新緩存。

          運行原理

          一個控制器每次需要獲取對象的時候都要訪問 APIServer,這會給系統(tǒng)帶來很高的負(fù)載,Informers 的內(nèi)存緩存就是來解決這個問題的,此外 Informers 還可以幾乎實時的監(jiān)控對象的變化,而不需要輪詢請求,這樣就可以保證客戶端的緩存數(shù)據(jù)和服務(wù)端的數(shù)據(jù)一致,就可以大大降低 APIServer 的壓力了。

          Informers

          如上圖展示了 Informer 的基本處理流程:

          • 以 events 事件的方式從 APIServer 獲取數(shù)據(jù)

          • 提供一個類似客戶端的 Lister 接口,從內(nèi)存緩存中 get 和 list 對象

          • 為添加、刪除、更新注冊事件處理程序

          此外 Informers 也有錯誤處理方式,當(dāng)長期運行的 watch 連接中斷時,它們會嘗試使用另一個 watch 請求來恢復(fù)連接,在不丟失任何事件的情況下恢復(fù)事件流。如果中斷的時間較長,而且 APIServer 丟失了事件(etcd 在新的 watch 請求成功之前從數(shù)據(jù)庫中清除了這些事件),那么 Informers 就會重新 List 全量數(shù)據(jù)。

          而且在重新 List 全量操作的時候還可以配置一個重新同步的周期參數(shù),用于協(xié)調(diào)內(nèi)存緩存數(shù)據(jù)和業(yè)務(wù)邏輯的數(shù)據(jù)一致性,每次過了該周期后,注冊的事件處理程序就將被所有的對象調(diào)用,通常這個周期參數(shù)以分為單位,比如10分鐘或者30分鐘。

          Informers 的這些高級特性以及超強的魯棒性,都足以讓我們不去直接使用客戶端的 Watch() 方法來處理自己的業(yè)務(wù)邏輯,而且在 Kubernetes 中也有很多地方都有使用到 Informers。但是在使用 Informers 的時候,通常每個 GroupVersionResource(GVR)只實例化一個 Informers,但是有時候我們在一個應(yīng)用中往往有使用多種資源對象的需求,這個時候為了方便共享 Informers,我們可以通過使用共享 Informer 工廠來實例化一個 Informer。

          共享 Informer 工廠允許我們在應(yīng)用中為同一個資源共享 Informer,也就是說不同的控制器循環(huán)可以使用相同的 watch 連接到后臺的 APIServer,例如,kube-controller-manager 中的控制器數(shù)據(jù)量就非常多,但是對于每個資源(比如 Pod),在這個進(jìn)程中只有一個 Informer。

          示例

          首先我們創(chuàng)建一個 Clientset 對象,然后使用 Clientset 來創(chuàng)建一個共享的 Informer 工廠,Informer 是通過 informer-gen 這個代碼生成器工具自動生成的,位于 k8s.io/client-go/informers 中。

          這里我們來創(chuàng)建一個用于獲取 Deployment 的共享 Informer,代碼如下所示:

          package main
          import ( "flag" "fmt" "path/filepath" "time"
          v1 "k8s.io/api/apps/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/util/homedir")
          func main() { var err error var config *rest.Config
          var kubeconfig *string
          if home := homedir.HomeDir(); home != "" { kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "[可選] kubeconfig 絕對路徑") } else { kubeconfig = flag.String("kubeconfig", "", "kubeconfig 絕對路徑") } // 初始化 rest.Config 對象 if config, err = rest.InClusterConfig(); err != nil { if config, err = clientcmd.BuildConfigFromFlags("", *kubeconfig); err != nil { panic(err.Error()) } } // 創(chuàng)建 Clientset 對象 clientset, err := kubernetes.NewForConfig(config) if err != nil { panic(err.Error()) }
          // 初始化 informer factory(為了測試方便這里設(shè)置每30s重新 List 一次) informerFactory := informers.NewSharedInformerFactory(clientset, time.Second*30) // 對 Deployment 監(jiān)聽 deployInformer := informerFactory.Apps().V1().Deployments() // 創(chuàng)建 Informer(相當(dāng)于注冊到工廠中去,這樣下面啟動的時候就會去 List & Watch 對應(yīng)的資源) informer := deployInformer.Informer() // 創(chuàng)建 Lister deployLister := deployInformer.Lister() // 注冊事件處理程序 informer.AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: onAdd, UpdateFunc: onUpdate, DeleteFunc: onDelete, })
          stopper := make(chan struct{}) defer close(stopper)
          // 啟動 informer,List & Watch informerFactory.Start(stopper) // 等待所有啟動的 Informer 的緩存被同步 informerFactory.WaitForCacheSync(stopper)
          // 從本地緩存中獲取 default 中的所有 deployment 列表 deployments, err := deployLister.Deployments("default").List(labels.Everything()) if err != nil { panic(err) } for idx, deploy := range deployments { fmt.Printf("%d -> %s\n", idx+1, deploy.Name) } <-stopper}
          func onAdd(obj interface{}) { deploy := obj.(*v1.Deployment) fmt.Println("add a deployment:", deploy.Name)}
          func onUpdate(old, new interface{}) { oldDeploy := old.(*v1.Deployment) newDeploy := new.(*v1.Deployment) fmt.Println("update deployment:", oldDeploy.Name, newDeploy.Name)}
          func onDelete(obj interface{}) { deploy := obj.(*v1.Deployment) fmt.Println("delete a deployment:", deploy.Name)}

          上面的代碼運行可以獲得 default 命名空間之下的所有 Deployment 信息以及整個集群的 Deployment 數(shù)據(jù):

          $ go run main.goadd a deployment: dingtalk-hookadd a deployment: spin-orcaadd a deployment: argocd-serveradd a deployment: istio-egressgatewayadd a deployment: vault-agent-injectoradd a deployment: rook-ceph-osd-0add a deployment: rook-ceph-osd-2add a deployment: code-server......1 -> nginx2 -> helloworld-v13 -> productpage-v14 -> details-v1......

          這是因為我們首先通過 Informer 注冊了事件處理程序,這樣當(dāng)我們啟動 Informer 的時候首先會將集群的全量 Deployment 數(shù)據(jù)同步到本地的緩存中,會觸發(fā) AddFunc 這個回調(diào)函數(shù),然后我們又在下面使用 Lister() 來獲取 default 命名空間下面的所有 Deployment 數(shù)據(jù),這個時候的數(shù)據(jù)是從本地的緩存中獲取的,所以就看到了上面的結(jié)果,由于我們還配置了每30s重新全量 List 一次,所以正常每30s我們也可以看到所有的 Deployment 數(shù)據(jù)出現(xiàn)在 UpdateFunc 回調(diào)函數(shù)下面,我們也可以嘗試去刪除一個 Deployment,同樣也會出現(xiàn)對應(yīng)的 DeleteFunc 下面的事件。

          Informers 是 client-go 中非常重要的概念,接下來我們將仔細(xì)分析 Informers 的實現(xiàn)原理,由于 Informers 實現(xiàn)非常復(fù)雜,我們將按照 Informers 的幾個核心知識點分別進(jìn)行講解。




          K8S進(jìn)階訓(xùn)練營,點擊下方圖片了解詳情

          瀏覽 51
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  四色激情五月婷婷 | 韩国三级中文字幕HD久久无码 | 成人毛片18女人毛片免费96 | 日本一区三级片 | 91操屄视频 |