Kubernetes client-go實戰應用

lotus_ruan發表於2021-09-09

[TOC]

Kubernetes client-go實戰應用

實戰應用

    • 官方的一個簡單的Controller示例,可以處理任何CRD資源

    • The main.go and controller.go contains the sample code to watch for the CRD and do some task accordingly.

    • 採用code-generator 的方式

    • 一個簡單的處理任何CRD資源的示例

client-go手動操作CRD

以前建立和管理CRD的client庫位於:,但是現在client-go已經支援CRD

1,定義types型別

比如定義一個目錄api/types/v1alpha1,裡面是關於types的一些定義檔案;

metav1.ObjectMeta 型別會包含典型的任意的Kubernetes資源的metadata屬性,如name, namespace, labels, annotations。

2,定義DeepCopy深複製方法

被Kubernetes API定義的所有資源物件、型別都需要實現k8s.io/apimachinery/pkg/runtime.Object這個介面定義,這個介面包含兩個方法GetObjectKind() 和 DeepCopyObject():

  • GetObjectKind():

    • 內建的metav1.TypeMeta結構已經實現

  • DeepCopyObject():

    • 需要我們自己去實現這個方法

    • 目的是生成物件,C++中有這樣的用法,名為:深複製,深複製意味著會重新生成物件並複製物件中的所有欄位、地址等資料;淺複製僅僅是物件的引用,並沒有生成新的物件。

    • 需要手動去寫各種欄位域的賦值

3,透過scheme註冊types型別

Scheme定義了序列化和反序列化API物件的方法,用於將group、版本和型別資訊轉換為Go模式和從Go模式轉換為Go模式的型別登錄檔,以及不同版本的Go模式之間的對映。

當和API Server通訊的時候能夠處理新的types型別的話就需要先讓client能夠知道有這個新的types型別存在。

AddToScheme 會利用到反射,因此新定義的types型別的結構體的命名必須要和自定義的Kind的命名(如VirtualService)保持一致,否則會找不到對應的kind,

var (
    SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
    AddToScheme   = SchemeBuilder.AddToScheme
)
  • AddToScheme: runtime.SchemeBuilder的一個外部方法,當Kubernetes註冊新定義的types型別後,就可以在任何地方呼叫這個方法

4,啟動一個HTTP client

當我們定義好一個新的types型別並且新增了一個register註冊方法到全域性的scheme編譯器後,我們就可以建立一個http client去載入我們的自定義資源了。現在就要用到client-go/rest這個RESTClient去實現了。一般而言,為了更為安全的方式使用API,優雅的姿勢是打包這些操作到clientset中,透過rest包中的RESTClientFor方法進行相關的封裝,然後再實現一些普適的interface介面,包含Get、Create、 Delete, Update 、List、Watch等通用介面方法,這個可以參考

5,構建Informer

一般優雅的姿勢去操作Kubernetes的資源並實時做出響應的方案是採用client-go的informer,它的工作模式是:初始時使用List()去載入資源的所有相關例項,然後使用Watch()進行訂閱更新;使用初始物件List列表和從watch訂閱更新到的資料會構建一個本地快取,該快取可以快速訪問任何自定義資源而無需每次都訪問API Server。像Pod、Deployment等資源物件都是採用這種方式。

k8s.io/client-go/tools/cache包中提供了一個Informer方法cache.NewInformer,這個cache.NewInformer返回兩個引數Store 和Controller:

  • Controller: 控制 List() 和 Watch() 的呼叫並填充Store

  • Store: 返回從API Server獲取的資源的最新狀態

    • 可以透過這個Store去訪問我們的自定義資源CRDs

code-generator自動程式碼生成操作CRD

程式碼生成相比於前面的手動生成的優勢在於不用手動去寫一些基礎的deepcopy,client,informer,lister這些方法

code-generation 也是基於client-go,因為client-go 需要實現runtime.Object interface的CustomResources型別 ,這樣就要實現諸如DeepCopy深複製的一系列方法,code-generation 就是實現了比如深複製的程式碼生成器,關於,如下:

  • deepcopy-gen: 給自定義type型別T建立一個DeepCopy方法func (t* T) DeepCopy() *T

  • client-gen: 給自定義資源的APIGroups建立clientsets

  • informer-gen:透過informers給自定義資源建立一個基礎介面方法去操作自定義資源

  • lister-gen: 為GET和LIST請求建立一個listers監聽器

informer和lister相當於是controllers,抽象一層controllers去進行操作資源.

實際專案中,需要引入這個工程,然後透過一些指令碼生成。一個簡單的示例是,這個同時也是這篇博文
code-generation-customresources中的示例

也是程式碼生成的,並且有關於自定義資源CRD的一些操作。

1,定義好相關目錄和檔案

  • 建立好工程目錄

    • pkg/apis/{Groupame}/{Version},如pkg/apis/networking.istio.io/v1

  • 在version目錄下建立檔案

    • doc.go

    • types.go : 資源物件的定義

    • regsiter.go:Scheme和register type

  • 檔案裡面的註釋,格式,都有要求

    • 將code-generator專案引入的vendor下

  • 建立自動生成指令碼

    • 呼叫vendor/k8s.io/code-generator/generate-groups.sh生成

注意點:types不能有interface{} ,否則自動生成的時候會生成出錯,因此其實也不建議透過程式碼自動生成,還是手動去編寫會更好。

type VirtualServiceSpec struct {
    Hosts    []string    `json:"hosts"`
    Gateways []string    `json:"gateways"`
    Http     interface{} `json:"http"`
}

報錯如下:
/v1/zz_generated.deepcopy.go" (107:37: expected ';', found '{' (and 5 more errors)).

2,生成程式碼

然後透過如下命令

generate-groups.sh <generators> <output-package> <apis-package> <groups-versions> ...


vendor/k8s.io/code-generator/generate-groups.sh all github.com/openshift-evangelists/crd-code-generation/pkg/client github.com/openshift-evangelists/crd-code-generation/pkg/apis example.com:v1 ...


可以同時指定多個輸出目錄
vendor/k8s.io/code-generator/generate-groups.sh all 
  base-code-example/istio/crd-code-generation-service/pkg/client base-code-example/istio/crd-code-generation-service/pkg/apis 
  "example.com:v1 networking.istio.io:v1" 
  --go-header-file ${SCRIPT_ROOT}/hack/custom-boilerplate.go.txt

可以生成client的所有方法

3,自動生成的優劣

優勢:

  • 減少很大一部分的通用實現,如deepcopy,client,informer,lister這些方法,這個相對來說是通用的,各個CRD都要實現一套

  • 避免自己編寫出一些不完善或者錯誤的實現

劣勢:

  • 非常嚴格的定義,包括註釋,都要按照要求去實現

  • type定義的struct,暫時不支援interface型別的轉換,相對來說不方便,需要嚴格知道spec的所有域和欄位的準確型別

istio中client-go的處理

試想一下,istio中的資源、物件都是Kubernetes的CRDs,那麼必然,istio中肯定有處理好Kubernetes CRD的方式,我們知道目前都是採用client-go,那麼istio中必然會有大量的client-go的引用和使用,透過原始碼可以發現確實如此並且都是採用RESTClient,clientset是包含RESTClient的。

在原始碼路徑/Users/meitu/Documents/work_Meitu/goDev/Applications/src/istio.io/istio/pilot/pkg/config/kube/crd中就是相關CRD的處理,然後/Users/meitu/Documents/work_Meitu/goDev/Applications/src/istio.io/istio/pilot/pkg/config/kube/crd/types.go是istio中所有一些常用資源的定義如DestinationRule、Gateway、VirtualService等。這樣的話,我們自己要透過client-go去實現後端服務,去開發的話,就可以參考istio原始碼中的一些定義和基本方法,然後結合client-go的一般性處理去實現後端服務程式。



作者:吳德寶AllenWu
連結:


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/1762/viewspace-2820575/,如需轉載,請註明出處,否則將追究法律責任。

相關文章