天天看點

kubernetes系列之十四:Kubernetes CRD(CustomResourceDefinition)概覽

一、前言

Kubernetes平台對于分布式服務部署的很多重要的子產品都有系統性的支援,借助如下一些平台資源可以滿足大多數分布式系統部署和管理的需求:

kubernetes系列之十四:Kubernetes CRD(CustomResourceDefinition)概覽

但是在不同應用業務環境下,對于平台可能有一些特殊的需求,這些需求可以抽象為Kubernetes的擴充資源,而Kubernetes的CRD(CustomResourceDefinition)為這樣的需求提供了輕量級的機制,保證新的資源的快速注冊和使用。在更老的版本中,TPR(ThirdPartyResource)是與CRD類似的概念,但是在1.9以上的版本中被棄用,而CRD則進入的beta狀态。

轉載自https://blog.csdn.net/cloudvtech

二、CRD的基本概念

CRD的工作步驟如下:

使用者向Kubernetes API服務注冊一個帶特定schema的資源,并定義相關API

  • 注冊一系列該資源的執行個體
  • 在Kubernetes的其它資源對象中引用這個新注冊資源的對象執行個體
  • 使用者自定義的controller例程需要對這個引用進行釋義和實施,讓新的資源對象達到預期的狀态

從基本原理上來講,CRD定義的Kubernetes擴充資源:

  • 借助Kubernetes RBAC和authentication機制來保證該擴充資源的security、access control、authentication和multitenancy。
  • 将擴充資源的資料存儲到Kubernetes的etcd叢集
  • 借助Kubernetes提供的controller模式開發架構,實作新的controller,并借助APIServer監聽etcd叢集關于該資源的狀态并定義狀态變化的處理邏輯
kubernetes系列之十四:Kubernetes CRD(CustomResourceDefinition)概覽

在實作自定義controller的時候,藍色的部分是client-go中經為使用者提供的架構和邏輯,可以直接使用,紅色的部分就是使用者需要實作的關于該擴充資源的業務邏輯。informer會借助APIServer跟蹤該擴充資源定義的變化,一旦被觸發就會調用回調函數,并把變更的具體内容放到Workqueue中,自定義controller裡面的worker會擷取Workqueue裡面内容,并進行相應的業務處理。

轉載自https://blog.csdn.net/cloudvtech

三、CRD的配置和使用

借助kubernetes的例子可以了解一下CRD配置的方式

3.1 crd的定義

crd.yaml

apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: foos.samplecontroller.k8s.io
spec:
  group: samplecontroller.k8s.io
  version: v1alpha1
  names:
    kind: Foo
    plural: foos
  scope: Namespaced
           

運作如下指令建立一個種類為Foo的CRD

kubectl create -f crd.yaml
           

3.2 定義這個種類的CRD的驗證schema

crd-validation.yaml

apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: foos.samplecontroller.k8s.io
spec:
  group: samplecontroller.k8s.io
  version: v1alpha1
  names:
    kind: Foo
    plural: foos
  scope: Namespaced
  validation:
    openAPIV3Schema:
      properties:
        spec:
          properties:
            replicas:
              type: integer
              minimum: 1
              maximum: 10
           
kubectl create -f crd-validation.yaml
           

3.3 定義一個Foo CRD的資源執行個體

example-foo.yaml

apiVersion: samplecontroller.k8s.io/v1alpha1
kind: Foo
metadata:
  name: example-foo
spec:
  deploymentName: example-foo
  replicas: 1
           
kubectl create -f example-foo.yaml
           

轉載自https://blog.csdn.net/cloudvtech

四、CRD contorller的實作

這個例子主要的作用時定義一個Foo CRD,這個CRD的資源執行個體可以定義一個replica的值,後端controller讀取這個值,并且建立包含這個數目replica POD的nginx deployment。兩個主要的實作controller的檔案是:

https://github.com/kubernetes/sample-controller/blob/master/main.go

https://github.com/kubernetes/sample-controller/blob/master/controller.go

4.1 初始化CDR controller

在main.go裡面使用了如下package:

kubeinformers "k8s.io/client-go/informers"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
           

執行個體化kubeinformer、informer等對象,運作CRD controller:

...
	kubeInformerFactory := kubeinformers.NewSharedInformerFactory(kubeClient, time.Second*30)
	exampleInformerFactory := informers.NewSharedInformerFactory(exampleClient, time.Second*30)
	controller := NewController(kubeClient, exampleClient, kubeInformerFactory, exampleInformerFactory)
	...
	go kubeInformerFactory.Start(stopCh)
	go exampleInformerFactory.Start(stopCh)
	...
	controller.Run(2, stopCh)		
           

4.2 擷取informer并注冊處理函數

在controller初始化的時候擷取Deployment和Foo兩個資源的informer并注冊處理函數:

// obtain references to shared index informers for the Deployment and Foo
	// types.
	deploymentInformer := kubeInformerFactory.Apps().V1().Deployments()
	fooInformer := sampleInformerFactory.Samplecontroller().V1alpha1().Foos()
	...
	// Set up an event handler for when Foo resources change
	fooInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc: controller.enqueueFoo,
		UpdateFunc: func(old, new interface{}) {
			controller.enqueueFoo(new)
		},
	})
	...
	deploymentInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc: controller.handleObject,
		UpdateFunc: func(old, new interface{}) {
			newDepl := new.(*appsv1.Deployment)
			oldDepl := old.(*appsv1.Deployment)
			if newDepl.ResourceVersion == oldDepl.ResourceVersion {
				// Periodic resync will send update events for all known Deployments.
				// Two different versions of the same Deployment will always have different RVs.
				return
			}
			controller.handleObject(new)
		},
		DeleteFunc: controller.handleObject,
	})	
           

Foo的informer的處理函數借助enqueueFoo将狀态變動事件加入到隊列,deployment的Informer借助handleObject函數處理狀态變動事件,可以看到最後也是将必要的事件放入隊列。這裡參考了controller模式的開發指導,借助這個開發模式架構,CDR的controller可以擷取任何感興趣的Kubernetes資源的事件進行相關處理;在這個例子中,CDR controller感興趣的隻有兩個資源的事件,一個是Foo這個資源的執行個體的加入事件,另外一個是deployment的變化事件(應為這裡的Foo CRD的作用實際上是建立一個Foo資源執行個體所定義的replica的nginx deployment),處理這個事件的handleObject要對deployment進行過濾,隻将Foo資源執行個體對應的nginx部署過濾出來,并将對應的事件加入到隊列。

4.3 隊列處理

是一個隊列驅動的loop:

// processNextWorkItem will read a single work item off the workqueue and
// attempt to process it, by calling the syncHandler.
func (c *Controller) processNextWorkItem() bool {
...
		// Run the syncHandler, passing it the namespace/name string of the
		// Foo resource to be synced.
		if err := c.syncHandler(key); err != nil {
			return fmt.Errorf("error syncing '%s': %s", key, err.Error())
		}
...
           

每個事件object都要經由syncHandler進行處理,主要包括建立deployment、更新deployment等行為:

...
deployment, err = c.kubeclientset.AppsV1().Deployments(foo.Namespace).Create(newDeployment(foo))
...
deployment, err = c.kubeclientset.AppsV1().Deployments(foo.Namespace).Update(newDeployment(foo))
...
err = c.updateFooStatus(foo, deployment)
...
           

每次的deployment事件操作都會先産生一個新的deployment配置:

// newDeployment creates a new Deployment for a Foo resource. It also sets
// the appropriate OwnerReferences on the resource so handleObject can discover
// the Foo resource that 'owns' it.
func newDeployment(foo *samplev1alpha1.Foo) *appsv1.Deployment {
	labels := map[string]string{
		"app":        "nginx",
		"controller": foo.Name,
	}
	return &appsv1.Deployment{
		ObjectMeta: metav1.ObjectMeta{
			Name:      foo.Spec.DeploymentName,
			Namespace: foo.Namespace,
			OwnerReferences: []metav1.OwnerReference{
				*metav1.NewControllerRef(foo, schema.GroupVersionKind{
					Group:   samplev1alpha1.SchemeGroupVersion.Group,
					Version: samplev1alpha1.SchemeGroupVersion.Version,
					Kind:    "Foo",
				}),
			},
		},
		Spec: appsv1.DeploymentSpec{
			Replicas: foo.Spec.Replicas,
			Selector: &metav1.LabelSelector{
				MatchLabels: labels,
			},
			Template: corev1.PodTemplateSpec{
				ObjectMeta: metav1.ObjectMeta{
					Labels: labels,
				},
				Spec: corev1.PodSpec{
					Containers: []corev1.Container{
						{
							Name:  "nginx",
							Image: "nginx:latest",
						},
					},
				},
			},
		},
	}
}
           

4.4 總結

是以這個example-controller可以根據Foo資源執行個體定義的replica,建立或者更新由controller管理的一個nginx deployment的replica。

轉載自https://blog.csdn.net/cloudvtech

繼續閱讀