天天看點

Kubernetes client-go實戰應用

Kubernetes client-go實戰應用

github上client-go官方項目工程

sample-controller示例

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

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

采用code-generator 的方式

kubernetes-crd-example 一個簡單的處理任何CRD資源的示例

以前建立和管理CRD的client庫位于:github.com/kubernetes/…

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

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

被Kubernetes API定義的所有資源對象、類型都需要實作k8s.io/apimachinery/pkg/runtime.Object這個接口定義,這個接口包含兩個方法GetObjectKind() 和 DeepCopyObject():

GetObjectKind():内置的metav1.TypeMeta結構已經實作

DeepCopyObject():

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

目的是生成deep copy 對象,C++中有這樣的用法,名為:深拷貝,深拷貝意味着會重新生成對象并拷貝對象中的所有字段、位址等資料;淺拷貝僅僅是對象的引用,并沒有生成新的對象。

需要手動去寫各種字段域的指派

Scheme定義了序列化和反序列化API對象的方法,用于将group、版本和類型資訊轉換為Go模式和從Go模式轉換為Go模式的類型系統資料庫,以及不同版本的Go模式之間的映射。

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

AddToScheme 會利用到反射,是以新定義的types類型的結構體的命名必須要和自定義的Kind的命名(如<code>VirtualService</code>)保持一緻,否則會找不到對應的kind,

AddToScheme: runtime.SchemeBuilder的一個外部方法,當Kubernetes注冊新定義的types類型後,就可以在任何地方調用這個方法

當我們定義好一個新的types類型并且添加了一個register注冊方法到全局的scheme編譯器後,我們就可以建立一個http client去加載我們的自定義資源了。現在就要用到<code>client-go/rest</code>這個RESTClient去實作了。一般而言,為了更為安全的方式使用API,優雅的姿勢是打包這些操作到clientset中,通過rest包中的<code>RESTClientFor</code>方法進行相關的封裝,然後再實作一些普适的interface接口,包含Get、Create、 Delete, Update 、List、Watch等通用接口方法,這個可以參考Pod client set的實作

一般優雅的姿勢去操作Kubernetes的資源并實時做出響應的方案是采用client-go的informer,它的工作模式是:初始時使用List()去加載資源的所有相關執行個體,然後使用Watch()進行訂閱更新;使用初始對象List清單和從watch訂閱更新到的資料會建構一個本地緩存,該緩存可以快速通路任何自定義資源而無需每次都通路API  Server。像Pod、Deployment等資源對象都是采用這種方式。

在<code>k8s.io/client-go/tools/cache</code>包中提供了一個Informer方法<code>cache.NewInformer</code>,這個<code>cache.NewInformer</code>傳回兩個參數Store 和Controller:

Controller: 控制 List() 和 Watch() 的調用并填充Store

Store: 傳回從API Server擷取的資源的最新狀态可以通過這個Store去通路我們的自定義資源CRDs

代碼生成相比于前面的手動生成的優勢在于不用手動去寫一些基礎的<code>deepcopy,client,informer,lister</code>這些方法

code-generation 也是基于client-go,因為client-go 需要實作runtime.Object  interface的CustomResources類型 ,這樣就要實作諸如DeepCopy深拷貝的一系列方法,code-generation  就是實作了比如深拷貝的代碼生成器,關于k8s.io/code-genera…,如下:

deepcopy-gen: 給自定義type類型T建立一個DeepCopy方法<code>func (t* T) DeepCopy() *T</code>

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

informer-gen:通過informers給自定義資源建立一個基礎接口方法去操作自定義資源

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

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

實際項目中,需要引入k8s.io/code-genera…這個工程,然後通過一些腳本生成。一個簡單的示例是github上的crd-code-generation項目,這個同時也是這篇博文 code-generation-customresources中的示例

官方的sample-controller項目也是代碼生成的,并且有關于自定義資源CRD的一些操作。

建立好工程目錄

<code>pkg/apis/{Groupame}/{Version}</code>,如<code>pkg/apis/networking.istio.io/v1</code>

在version目錄下建立檔案

doc.go

types.go : 資源對象的定義

regsiter.go:Scheme和register type

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

vendor

将code-generator項目引入的vendor下

建立自動生成腳本

調用<code>vendor/k8s.io/code-generator/generate-groups.sh</code>生成

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

然後通過如下指令

可以生成client的所有方法

優勢:

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

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

劣勢:

非常嚴格的定義,包括注釋,都要按照要求去實作

type定義的struct,暫時不支援interface類型的轉換,相對來說不友善,需要嚴格知道spec的所有域和字段的準确類型

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

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

Accessing Kubernetes CRDs from the client-go package

Kubernetes Deep Dive: Code Generation for CustomResources

k8s.io/code-genera…

官方的sample-controller項目

github上的crd-code-generation項目