- 目的
- CRD資源定義
- 代碼生成
- Controller編寫
目的
Custom Resource
是擴充Kubernetes的一種方式(另外一種就是通過聚合層API
apiserver-aggregation
),而controller對指定的resource進行監聽和執行對應的動作(watch,diff,action)。Operator與Controller差別
- 所有的Operator都是用了Controller模式,但并不是所有Controller都是Operator。隻有當它滿足: controller模式 + API擴充 + 專注于某個App/中間件時,才是一個Operator。
- Operator就是使用CRD實作的定制化的Controller. 它與内置K8S Controller遵循同樣的運作模式(比如 watch, diff, action)
- Operator是特定領域的Controller實作
讨論兩者差別:https://github.com/kubeflow/tf-operator/issues/300
是以先學習如何建構出一些自定義的Controller肯定是之後實作Operator的基礎。
實作一個自定義的Controller由兩部分組成:CRD和Controller邏輯代碼
這裡以sample-controller的代碼為例,同時我們自己寫的Controller也可以參考這個代碼結構。
CRD資源定義
以sample-controler中的為例,我們需要建立的一個
Foo
如下example-foo.yaml:
建立該
Foo
自定義資源後,期望建立出一個名稱為
example-foo
,副本數為
1
的deployment。
它的CRD定義如下:
更多關于crd定義規則可以參考官方文檔:
https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/
當把這個crd資源apply到叢集中後,我們可以通過
kubectl get apiservice v1alpha1.samplecontroller.k8s.io -o yaml
指令看到注冊這個
apiservice
代碼編寫
隻需要将我們
Foo
resource相關的struct,其餘的類似自定義資源的
informers
,
listers
,
clientset
以及
deepcopy
的代碼都可以通過工具code-generator自動生成。
以及編寫我們自定義Controller的業務邏輯代碼就好了
struct資源定義
type.go
其中類似
+k8s:
的注釋是代碼生成來識别的。
register.go 中将
Foo
,
FooList
注冊進入
scheme
中。
代碼生成
代碼生成能幫我處理大部分重複代碼,主要通過
https://github.com/kubernetes/code-generator
這個包進行解析tag并生成。
全局tag
必須在目标包的doc.go檔案中聲明,典型路徑是 pkg/apis///doc.go。
内容示例:
局部tag
- +genclient: 為這個 package 建立 client。
- +genclient:noStatus: 當建立 client 時,不存儲 status。
- +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object: 為結構體生成 deepcopy 的代碼,實作了 runtime.Object 的 Interface。
代碼生成:
通過./hack/update-codegen.sh方法可以生成,client以及deepcopy代碼。
包含:
_前提:code-generator 已經在vendor中,執行
go mod vendor
_
update-codegen.sh内容如下
完整使用說明:
Usage: generate-groups.sh ... the generators comma separated to run (deepcopy,defaulter,client,lister,informer) or "all". the output package name (e.g. github.com/example/project/pkg/generated). the external types dir (e.g. github.com/example/api or github.com/example/project/pkg/apis). the groups and their versions in the format "groupA:v1,v2 groupB:v1 groupC:v2", relative
to .
... arbitrary flags passed to all generator binaries.
Examples:
generate-groups.sh all github.com/example/project/pkg/client github.com/example/project/pkg/apis "foo:v1 bar:v1alpha1,v1beta1"
generate-groups.sh deepcopy,client github.com/example/project/pkg/client github.com/example/project/pkg/apis "foo:v1 bar:v1alpha1,v1beta1"
interface{}處理
場景:如果我們需要一個通用的類型的object,如下:
我們在spec裡面定義了一個fields字段,類型是object(即key ,value的形式),value的值可能是int也可能是string或者bool
在type定義的時候我是這麼寫的,定義為map[string]interface{}
當在代碼生成的時候,發現會報錯:
遇到這種問題,需要自己實作深拷貝,例如這種:
Controller編寫
在編寫Controller之前需要了解client-go中的informer機制:

- 黃色的部分是controller相關的架構,包括workqueue。
- 藍色部分是client-go的相關内容,包括informer, reflector(其實就是informer的封裝), indexer。
- 從流程上看,reflector從apiserver中通過list&watch機制接收事件變化,進入Delta FIFO隊列中,由informer進行處理。
- informer會将delta FIFO隊列中的事件交給indexer元件,indexer元件會将事件持久化存儲在本地的緩存中。
- 之後,由于使用者事先将為informer注冊各種事件的回調函數,這些回調函數将針對不同的元件做不同的處理。
- 例如在controller中,将把object放入workqueue中,之後由controller的業務邏輯中進行處理。處理的時候将從緩存中擷取object的引用。即各元件對資源的處理僅限于本地緩存中,直到update資源的時候才與apiserver互動。
簡單來講通過list/watch機器提供了本地緩存避免每次去請求apiserver。
并且提供了Event Handler方法,在将資料儲存進入cache時,通過調用自定義handler方法,增加自定義處理。
是以Controller 的代碼結構,就是如下:
- 在NewController中通過fooInformer添加
,執行AddEventHandler
enqueueFoo
- enqueueFoo 中将擷取的變更放入隊列
- 啟動一個或多個work從隊列中取資料,最終通過syncHandler進行業務判斷
- 在sample-controller中同時還監聽了deployment,在
判斷是否歸屬handleObject
Kind ,同時執行Foo
入隊。enqueueFoo
具體代碼判斷參考controller.go
在main.go方法中調用如下:
就是調用通過coge-gen生成代碼建立informer并啟動,建立client。
思考:為什麼要通過隊列來控制資料的變化?
我覺得用隊列一方面是解耦,因為往往一個Controller裡面可能要通過informer監聽各類資源對象,通過隊列借助了各個informer的依賴。另一友善可以通過不同類型的隊列比如限速隊列,延遲隊列達到不同的并發控制。
總結
- 先定義crd,再實作Controller邏輯
- 可以通過code-generator生成informer,client,listers代碼
- 注意針對interface{}類型需要自己實作deepCopy方法
- 實作一個Operator,內建更多更複雜的Controller的話,我們一般使用Operator架構,比如kubebuilder
參考
- https://juejin.im/post/5de097bbf265da05d849ab53
- https://blog.fatedier.com/2019/04/02/k8s-custom-controller/
- https://blog.gmem.cc/crd
推薦閱讀
- Linux、K8S、Go等是如何設計排程系統的?排程系統設計精要
喜歡本文的朋友,歡迎關注“Go語言中文網”:
Go語言中文網啟用微信學習交流群,歡迎加微信:274768166,投稿亦歡迎