Kubernetes集群实践(十一)编写一个自定义资源CRD

本文主要介绍如何在Kubernetes编写自定义资源CRD。

关键词:k8s

前言

Kubernetes的最大亮点之一必定是它的声明式API设计,所谓的声明式就是告诉Kubernetes你要什么,而不是告诉它怎么做命令。我们日常使用Kubernetes做编排工作的时候,经常会接触Deployment、Service、Pod等资源对象,我们可以很灵活地创建其定义配置,然后执行kubectl apply命令,Kubernetes总能为我们创建相关资源对象并完成资源的注册,进而执行资源所负责的功能。

CRD,称之为自定义资源定义,本质上,它的表现形式是一段声明,用于定义用户定义的资源对象罢了。单单通过它还不能产生任何收益,因为开发者还要针对CRD定义提供关联的CRD对象CRD控制器(CRD Controller)。CRD控制器通常可以通过Golang进行开发,只需要遵循Kubernetes的控制器开发规范,并基于client-go进行调用,并实现Informer、ResourceEventHandler、Workqueue等组件逻辑即可。听起来感觉很复杂的样子,不过其实真正开发的时候,并不困难,因为大部分繁琐的代码逻辑都能通过Kubernetes的code generator代码生成出来。关于如何进行CRD控制器的开发,下面我们会通过一个例子慢慢地深入,希望通过实践来理解CRD的原理。

声明式API

命令式API

首先,声明式API是相对于命令式API而言的。

假设我们想创建一个deployment,可以创建一个下面的yaml文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80

然后在master执行

1
kubectl create -f nginx.yaml

然后这两个Pod就跑起来了。

如果想修改上的资源清单,需要先修改资源文件,然后再执行

1
kubectl replace -f nginx.yaml

以上基于yaml文件操作的方式,被称作命令式API。同样属于命令式API的还有Docker Swarm的创建方式:

1
docker service create --name nginx --replicas 2  nginx

声明式API

kubectl apply 的操作属于声明式API。

kubectl replace的操作,可以看作是使用新的YAML文件的API对象,替换原有的API对象;kubectl set image和 kubelctl edit也是对原有对象的修改,这些都属于命令式API。

kube-apiserver在响应命令式请求时,只能一次处理一个写请求,否则可能会出现冲突。

而对于apply的声明式请求,可以一次处理多个写操作,并且具备merge能力。

所谓声明式API,就是告诉kubernetes你要什么,而不是告诉他怎么做命令。在日常做容器编排时,经常会操作Deployment、Service、Pod等资源对象,我们可以很灵活的创建其自定义配置,然后执行kubectl apply 命令,kubernetes可以创建相关资源对象并完成资源的注册,进而执行资源所负责的功能。

有了自定义API对象,开发者就不需要逐一进行Deployment、Service 、ConfigMap等步骤,而是创建一些可以表述整个应用程序或者软件服务的对象。除此之外,我们还可以在创建高阶对象的基础上创建底层对象。例如:我们想要创建一个backup资源,在定义yaml的同时,可以希望通过spec的定义进行日常的备份操作声明,当提交给k8s集群时,相关的Deployment、Service资源会被自动创建,很大程度上让业务扩展性加大。

CRD

CRD,称之为自定义资源定义,本质上,它的表现形式是一段声明,用于定义用户定义的资源对象罢了。单单通过它还不能产生任何收益,因为开发者还要针对CRD定义提供关联的CRD对象CRD控制器(CRD Controller)。CRD控制器通常可以通过Golang进行开发,只需要遵循Kubernetes的控制器开发规范,并基于client-go进行调用,并实现Informer、ResourceEventHandler、Workqueue等组件逻辑即可。听起来感觉很复杂的样子,不过其实真正开发的时候,并不困难,因为大部分繁琐的代码逻辑都能通过Kubernetes的code generator代码生成出来。关于如何进行CRD控制器的开发,下面我们会通过一个例子慢慢地深入,希望通过实践来理解CRD的原理。

API

整个kubernetes里所有API对象都可以用下面的树形结构表示出来:

可以看出,API对象的组织方式,是层层递进的。

如果现在想要创建一个CronJob对象,那么YAML文件的开始部分需要这样写:

1
2
apiversion: batch/v2alpha1
kind: CronJob

对于上文,Crontab是这个API对象的资源类型,batch是他的组,v2alpha1是版本;

当提交了这个yaml文件一行,kubernetes就会把这个YAML文件描述内容,转换成一个CronJob对象

apiserver会遵循以下的流程:

  1. 匹配API对象的组。

    通常核心对象如Pod、Node不需要group,直接在/api这个层级下进行匹配;

    而对于CronJob等非核心API对象来说,Kubernetes必须在 /apis这个层级下找到对应的Group,根据batch,找到/apis/batch。

    API是根据对象功能为依据的。如Job和CronJob就属于batch这个Group。

  2. 匹配API对象的版本号

    对于CronJob这个API对象来说,Kubernetes在batch这个Group下,匹配的版本号就是v2alpha1

    K8s中同一种API对象可以由多个版本,这正是K8s进行API版本化管理的手段