client-go系列之1---client-go代碼結構講解


1. 寫在前面

個人主頁: https://gzh.readthedocs.io

關注容器技術、關注Kubernetes。問題或建議,請公眾號留言。

本系列內容都是基於這個版本的client-go進行講解,不同版本的略有差異。

[root@77DDE94FF07FCC1-wsl /ACode/client-go] git rev-parse HEAD
becbabb360023e1825a48b4db85f454e452ae249

下面簡單羅列一下本文中可能會用提到的一些常用縮寫,詳細的介紹請參考后面的文章(TODO)談起k8s中的GVR我們實際在講什么

說明

看一下k8s的資源模型:

apiVersion: extensions/v1beta1

kind: ReplicaSet

對於Resource我們可以將之類比於編程語言中的“包”的概念,主要用於區分不同的API,這樣可以有效避免Kinds重名的問題。通常會使用公司的域名等具有獨特明顯區分的字符串作為Resource的值,如:alibaba-inc.com

Version用於區分不同API的穩定程度及兼容性,如:v1beta1, v1

Kind即為API所對應的名字,如:Deployment, Service

2. 代碼結構

從這個包的名字可以很明顯的知道,client-go其實主要是提供了用戶與k8s交互時使用客戶端,方便大家編程。

個別目錄已過濾掉。

[root@77DDE94FF07FCC1-wsl /ACode/client-go] tree -d -L 1 -I "testing|examples|*_test*|Godeps|third_party|metadata|deprecated|restmapper"
.
├── discovery                   # 定義DsicoveryClient客戶端。作用是用於發現k8s所支持GVR(Group, Version, Resources)。
├── dynamic                     # 定義DynamicClient客戶端。可以用於訪問k8s Resources(如: Pod, Deploy...),也可以訪問用戶自定義資源(即: CRD)。
├── informers                   # k8s中各種Resources的Informer機制的實現。
├── kubernetes                  # 定義ClientSet客戶端。它只能用於訪問k8s Resources。每一種資源(如: Pod等)都可以看成是一個客端,而ClientSet是多個客戶端的集合,它對RestClient進行了封裝,引入了對Resources和Version的管理。通常來說ClientSet是client-gen來自動生成的。
├── listers                     # 提供對Resources的獲取功能。對於Get()和List()而言,listers提供給二者的數據都是從緩存中讀取的。
├── pkg                         
├── plugin                      # 提供第三方插件。如:GCP, OpenStack等。
├── rest                        # 定義RestClient,實現了Restful的API。同時會支持Protobuf和Json格式數據。
├── scale                       # 定義ScalClient。用於Deploy, RS, RC等的擴/縮容。
├── tools                       # 定義諸如SharedInformer、Reflector、DealtFIFO和Indexer等常用工具。實現client查詢和緩存機制,減少client與api-server請求次數,減少api-server的壓力。
├── transport
└── util                        # 提供諸如WorkQueue、Certificate等常用方法。

12 directories

3. 代碼使用簡單示例

在對每一部分進行講解前,先用一個圖來講解各部分之間的關系:

對於圖中的每一個帶有標號的部分,下面給出簡單的代碼使用展示, 如果暫時不明白下面的代碼可以先進行下一章節的學習。

3.1 獲取kubeconfig及context

這一部分對應於序號1---tools/clientcmd。

func main() {
        var kubeconfig *string

        // 默認會從~/.kube/config路徑下獲取配置文件
        if home := homeDir(); home != "" {
                kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional)absolute path to the kubeconfig file")
        } else {
                kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
        }

        flag.Parse()

        // 使用k8s.io/client-go/tools/clientcmd生成config的對象
        if config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig); err != nil {
                panic(err.Error())
        }
}

3.2 創建ClientSet

這一部分對應於序號2---ClientSet。

// 使用k8s.io/client-go/kubernetes生成一個ClientSet的客戶端,客戶端生成后,就可以使用這個客戶端與k8s API server進行交互了,如獲取資源列表、Create/Update/Delete資源等
clientset, err := kubenetes.NewForConfig(config)
if err != nil {
    panic(err.Error())
}

3.3 使用ClientSet獲取集群中的pods

這一部分對應於序號2/3/4---RestClient。

for {
    // 使用ClientSet客戶端獲取集群中所有的Pods。其中:ListOptions的結構如下:
    // type ListOptions struct {
    //      TypeMeta `json:",inline"`
    //      LabelSelector string `json:"labelSelector,omitempty"`
    //      FieldSelector string `json:"fieldSelector,omitempty"`    
    //}
    pods, err := clientset.CoreV1().Pods("").List(metav1.ListOptions{})
    if err != nil {
        panic(err.Error())
    }

    fmt.Printf("Number of pods are: %d\n", len(pods.Items))
}

3.4 使用ClientSet獲取指定的pod

這一部分對應於序號2/3/4---tools/clientcmd。

for {
    // 在這里我們從default這個namespace中獲取了名為my-pod的Pod對象
    pod, err := clientset.CoreV1().Pods("default").Get("my-pod", metav1.GetOptions{})
    if err != nil {
        painc(err.Error())
    }

    fmt.Printf("%v\n\n\n\n", pod.spec)
}

4. 各種Clients詳解

client-go中定義的比較重要的client有:

其中,RestClient是所有客戶端的基礎,后三者都是對RestClient的封裝。RestClient它通過kubeconfig與k8s-api-server進行交互。詳細結構如下圖:

ClientSets使用預生成的API對象, 這樣的好處是當本地的API對象與k8s-api-server進行交互時會變得比較方便,方便的同時,隨之也帶來了版本與類型強耦合的問題。

DynamicClient則使用unstructured.Unstructured表示來自API Server的所有對象值。Unstructured類型是一個嵌套的map[string]inferface{}值的集合來創建一個內部結構,這一點類似於RESTful API中的Json數據,這樣可以解決ClientSet中出現的強耦合的問題,換句話說,當客戶端的API發生變化時,DynamicClient無需重新編譯。DynamicClient使所有數據實現延時綁定,即只有到運行時才會實現綁定,這意味着程序運行之前,使用DynamicClient的程序將不會對對象進行Validation,這也是本client的一個缺點。

5. 其它組件

client-go中除了上面提到比較重要的客戶端外, 本庫還包含了各種機制(tools/cache)。

下圖比較直觀的展示了client-go與customer controller及client-go各組件之間的交互關系,是我們在開發自定義控制器時經常需要使用的機制,了解這個圖有助於我們更好的理解client-go及controller背后的實現邏輯。

如果您對client-go之前就比較了解,建議您移步sample-controller看一下控制器實現的具體代碼。

5.1 Reflector

refelector是定義在包緩存里面的Reflector結構體,可以用於監視指定資源類型(kind)的Kubernetes API。

實現這個功能的函數是ListAndWatch。監視的對象可以是一個內置的資源,也可以是一個自定義的資源(CRD)。

當reflector通過watch API接收到關於新資源實例存在的通知時,它會使用相應的listing API獲取新創建的對象,並將其放在watchHandler函數里面的DeltaFIFO隊列中。

5.2 Informer

它是定義在包緩存中的一個基礎控制器,它可以w使用函數processLoopDeltaFIFO隊列中取出對象。

這個基礎控制器的工作是保存對象以便以后檢索,並調用我們的控制器將對象傳遞給它。

5.3 Indexer

提供對對象的索引功能。它被定義在tools/cache包中的Indexer類型中。

一個典型的索引用例是基於對象標簽創建一個索引。Indexer可以基於幾個索引函數來維護索引。Indexer使用一個線程安全的數據存儲來存儲對象和它們的鍵值。

tools/cache內的Store類型中定義了一個名為MetaNamespaceKeyFunc的默認函數,該函數為該對象生成一個對象的鍵,作為<namespace>/<name>組合。

5.4 WorkQueue

這是在控制器代碼中創建的隊列,用於將對象的分發與處理解耦。編寫 Resource Event Handler 函數來提取所分發對象的鍵值並將其添加到工作隊列中。


歡迎關注我的微信公眾號:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM