Kubernetes CRD(CustomResourceDefinition)概览

一、前言

Kubernetes平台对于分布式服务部署的很多重要的模块都有系统性的支持,借助如下一些平台资源可以满足大多数分布式系统部署和管理的需求:

但是在不同应用业务环境下,对于平台可能有一些特殊的需求,这些需求可以抽象为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集群关于该资源的状态并定义状态变化的处理逻辑


在实现自定义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。
--------------------- 
作者:cloudvtech 
来源:CSDN 
原文:https://blog.csdn.net/cloudvtech/article/details/80277960 
版权声明:本文为博主原创文章,转载请附上博文链接!

上一篇:kuberenetes CRD开发指南


下一篇:ERROR o.s.s.s.TaskUtils$LoggingErrorHandler - Unexpected error occurred in scheduled task.