1. 程式人生 > >如何基於kubernetes開發自定義的Controller

如何基於kubernetes開發自定義的Controller

繼上次分享Kubernetes原始碼編譯除錯之後,一直想寫些對scheduler,controller-manager,kubelete等元件的深入介紹,今天先介紹下Controller部分,在kubernetes內部提供了大量的controller,比如node controller,pod controller,endpoint controller等等。這些controller都是由controller-manager進行管理。每個Controller通過API Server提供的介面實時監控整個叢集的每個資源物件的當前狀態,當發生各種故障導致系統狀態發生變化時,會嘗試通過CRUD操作將系統狀態修復到“期望狀態”。

                                                   

在kubernetes中一切皆資源,Kubernetes 1.7之後,提供了CRD(CustomResourceDefinitions)的自定義資源二次開發能力來擴充套件kubernetesAPI,通過此擴充套件,可以向kubernetes API中增加新型別,會比修改kubernetes的原始碼或者是建立自定義的API server來的更加的簡潔和容易,並且不會隨著kuberntetes核心版本的升級,而出現需要程式碼重新merger的需要,以及相容性方面的問題。這一功能特性的提供大大提升了kubernetes的擴充套件能力。

                       

Kube - Controller的內部大致實現邏輯

主要使用到 Informer和workqueue兩個核心元件。Controller可以有一個或多個informer來跟蹤某一個resource。Informter跟API server保持通訊獲取資源的最新狀態並更新到本地的cache中,一旦跟蹤的資源有變化,informer就會呼叫callback。把關心的變更的Object放到workqueue裡面。然後woker執行真正的業務邏輯,計算和比較workerqueue裡items的當前狀態和期望狀態的差別,然後通過client-go向API server傳送請求,直到驅動這個叢集向用戶要求的狀態演化。

具體對client-go的使用,可參考《client-go的使用和原始碼分析》,在編寫自定義的Controller的時候,需要大量使用client-go元件。上圖中的藍色部分全部是client-go已經包含的部分,不需要重新開發可以直接使用。紅色的部分是自己的業務邏輯,需要自己開發。可見基於CRD開發一個自定義的資源管理API 來擴充套件kubernetes的底層能力還是非常簡潔的。比如在kubernetes中目前還是沒有辦法可以做到通過pod直接查詢到對應的service是誰。為了實現這個定製化的能力,為上層操作提供介面,就可以來擴充套件這個能力,寫一個pod-service-controller。這個controller中提供兩個informer,一個informer關注pod資源,一個informer關注service資源。把各自的變化情況通過回撥的方式放到workerqueue中。然後在worker中併發的去處理queue中的item。整理出pod和service的對映關係。這樣上層就可以直接通過該controller的RESTful API 查詢到他們之間的對映關係。而不需要在上層哐哐的寫一堆業務處理邏輯。

下面以sample-controller為例,來講解開發自定義Controller的關鍵步驟和注意點。

1.      根據CRD的模板定義出自己的資源管理物件。比如crd.yaml檔案

apiVersion:apiextensions.k8s.io/v1beta1

kind:CustomResourceDefinition

metadata:

  #名稱必須符合下面的格式:<plural>.<group>

  name: foos.samplecontroller.k8s.io

spec:

  # REST API使用的組名稱:/apis/<group>/<version>

  group: samplecontroller.k8s.io

  # REST API使用的版本號:/apis/<group>/<version>

  version: v1alpha1

  names:

    # CamelCased格式的單數型別。在清單檔案中使用

    kind: Foo

    # URL中使用的複數名稱:/apis/<group>/<version>/<plural>

    plural: foos

  # Namespaced或Cluster 

  scope: Namespaced

  validation:

    openAPIV3Schema:

      properties:

        spec:

          properties:

            replicas:

              type: integer

              minimum: 1

                      maximum:10

2.      Kubectl create  –f  crd.yaml  執行完成後就建立了Foo這個資源物件。沒有修改任何kubernetes核心程式碼,僅通過定義CustomResourceDefinition型別的yaml檔案就可以直接建立新的資源物件,非常的簡單和方便

一旦你建立CRD後,可以使用如下命令來驗證CRD是否建立成功

kubectl get crd-o 'custom-columns=NAME:{.metadata.name},ESTABLISHED:{.status.conditions[?(@.type=="Established")].status}'

執行後輸出如下:

NAME                                ESTABLISHED

foos.samplecontroller.k8s.io        True

3.      使用Foo這個新建立的型別,建立一個pod。

apiVersion: samplecontroller.k8s.io/v1alpha1

kind: Foo

metadata:

  name: example-foo

spec:

  deploymentName:example-foo

  replicas: 1

使用kubectl create –f example-foo.yaml

NAME                          READY     STATUS    RESTARTS  AGE

example-foo-86ccd54874-9ctfm        1/1       Running  0          3h

4.      Foo型別的Pod物件建立成功後,結合上對Foo型別資源的控制才會讓資源物件有意義。所以還需要開發一個自定義的Foo-Controller。

5.      定義一個Controller結構體

6.      構造出workqueue和Informer物件用於初始化Controller物件

7.      通過informer監控資源CRUD的操作的回撥函式

8.      啟動Controller

在run函式中在worker執行前,必須要等待狀態的同步完成。使用go啟動多個worker協程併發的從queue中一個個的獲取待處理的Item。其中的runwoker是包含真正的業務邏輯的函式。

9.       

以上便是一個完整的編寫自定義Controller的完整過程。比如推薦大家一個比較好的Controller學習參考實現就是kubewatch,事件探測器。

參考:

1. Client-go的使用和原始碼分析