如何基于kubernetes开发自定义的Controller

 继上次分享Kubernetes源码编译调试之后,一直想写些对scheduler,controller-manager,kubelete等组件的深入介绍,今天先介绍下Controller部分,在kubernetes内部提供了大量的controller,比如node controller,pod controller,endpoint controller等等。这些controller都是由controller-manager进行管理。每个Controller通过API Server提供的接口实时监控整个集群的每个资源对象的当前状态,当发生各种故障导致系统状态发生变化时,会尝试通过CRUD操作将系统状态修复到“期望状态”。

                                                   如何基于kubernetes开发自定义的Controller

在kubernetes中一切皆资源,Kubernetes 1.7之后,提供了CRD(CustomResourceDefinitions)的自定义资源二次开发能力来扩展kubernetesAPI,通过此扩展,可以向kubernetes API中增加新类型,会比修改kubernetes的源代码或者是创建自定义的API server来的更加的简洁和容易,并且不会随着kuberntetes内核版本的升级,而出现需要代码重新merger的需要,以及兼容性方面的问题。这一功能特性的提供大大提升了kubernetes的扩展能力。

具体使用建议见:请参考Should I add a custom resource to myKubernetes Cluster?

 

                       如何基于kubernetes开发自定义的Controller

       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结构体

如何基于kubernetes开发自定义的Controller

6.      构造出workqueue和Informer对象用于初始化Controller对象

如何基于kubernetes开发自定义的Controller

7.      通过informer监控资源CRUD的操作的回调函数

如何基于kubernetes开发自定义的Controller

8.      启动Controller

如何基于kubernetes开发自定义的Controller

在run函数中在worker运行前,必须要等待状态的同步完成。使用go启动多个worker协程并发的从queue中一个个的获取待处理的Item。其中的runwoker是包含真正的业务逻辑的函数。

9.       

如何基于kubernetes开发自定义的Controller

以上便是一个完整的编写自定义Controller的完整过程。比如推荐大家一个比较好的Controller学习参考实现就是kubewatch,事件探测器。

https://github.com/bitnami-labs/kubewatch

如何基于kubernetes开发自定义的Controller

参考:

1. Client-go的使用和源码分析

http://www.huweihuang.com/article/source-analysis/client-go-source-analysis/

2. 使用 client-go 控制原生及拓展的 KubernetesAPI  

https://www.kubernetes.org.cn/1309.html