如何基于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)的自定义资源二次开发能力来扩展kubernetes API,通过此扩展,可以向kubernetes API中增加新类型,会比修改kubernetes的源代码或者是创建自定义的API server来的更加的简洁和容易,并且不会随着kuberntetes内核版本的升级,而出现需要代码重新merger的需要,以及兼容性方面的问题。这一功能特性的提供大大提升了kubernetes的扩展能力。

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

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,事件探测器。

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

K8S中文社区微信公众号

评论 2

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
  1. #2

    这只是一个watch事件的实现

    大容量人形自走式装饭机9个月前 (03-12)回复
  2. #1

    为什么不开源?  [/惊讶]

    MrNobody8个月前 (04-03)回复