在Kubernetes
中, 所有的功能实现都以资源(Resource
)形式体现。简单来说,资源就是Kubernetes的API端点(endpoint
),用于存储某种类型的对象。用户通过对资源对象的CRUD
操作完成相应功能管理。Kubernetes 1.7之后添加了自定义资源(Custom Resource
)的扩展能力,允许使用者通过定义自己的资源类型来扩展Kubernetes API,大大增强扩展Kubernetes的灵活性。这些自定义资源和Pod
、Deployment
等原生资源对象的操作方法基本一样。
用户使用CRD(Custom Resource Definitions)
来定义自定义资源(Custom Resource
)。CRD
被创建后,Kubernetes APIServer
会为CRD
中指定的资源版本创建相应的RESTAPI端点。
CRD
可以通过YAML
文件来创建,YAML
文件中指定CRD
对象的规范,其中比较重要的字段有:
- metadata.name: 要创建的
CRD
名称 - spec.group:
CRD
对象所属的API组 - spec.version:
CRD
对象所属的API版本 - spec.names: 管理自定义资源所需要的名称描述,可以定义
plural
(复数名称),singular
(单数名称)和shortnames
(简称)等字段 - spec.names.kind: 指定自定义资源的类型名称
- spec.scope: 指明自定义资源有效范围,可以是整个集群有效(
clustered
)或命名空间内有效(namespaced
)
下面我们通过示例来说明创建CRD
及相应的自定义资源。
首先创建CRD YAML文件ac-crd.yaml
:
1 | apiVersion: apiextensions.k8s.io/v1beta1 |
使用kubectl
创建类型名称为AppConfig
的CRD
对象并查看结果, 我们的CRD
对象已经创建完成。
1 | [fg@kubernetes ~]$ kubectl create -f ac-crd.yaml |
此时所创建的CRD
所对应的API端点是: /apis/stable.flygoast.com/v1beta1/namespaces/*/appconfigs/
接着创建一个YAML
文件来创建AppConfig
类型的对象。我们没有限制自定义对象的字段,字段也可以包含任意的数据。ac1.yaml
代码如下:
1 | apiVersion: "stable.flygoast.com/v1beta1" |
使用kubectl
创建AppConfig
对象, 并查看相应对象信息:
1 | [fg@kubernetes ~]$ kubectl create -f ac1.yaml |
使用起来看上去非常简单。
Kubernetes的哲学是声明式API,使用者只关心资源的最终状态,而不关心执行哪些行为使资源对象达到最终状态,这些变化过程由Kubernetes来完成。现在的AppConfig对象操作只是完成数据对象的存储与获取,完整的声明式API实现还需要实现一个Custom Controller去保证当前资源状态与所期望状态的同步。
Controller需要监视Kubernetes的API对象,当相应的资源对象发生改变时,实现相应的逻辑完成资源对象的状态更新。Kubernetes对外提供了丰富的API资源,Controller可以基于RESTAPI实现,也可以使用客户端库来实现。
已经实现的客户端库可以参考:
https://kubernetes.io/docs/reference/using-api/client-libraries/
实现一个完善的Custom Controller较为复杂,可以参考文后所列的一些参考文章,本文以一个简要的Controller实现说明下基本逻辑。我们的Custom Controller简单的监视AppConfig对象,当对象发生变化时,输出相应的日志。
我们这里使用Python库来实现。
首先编写controller代码main.py
:
1 | import logging |
我们准备将该Controller运行在Kubernetes集群中,因而先打包成Docker镜像。
创建Dockerfile
:
1 | FROM python:latest |
BUILD镜像并上传:
1 | docker build -t flygoast/ac-controller . |
由于我们的controller将在POD
中运行并访问Kubernetes APIServer, 它需要访问Kubernetes API的权限。权限的分配过程如下:
- 创建专用
ServiceAccount
, 我们的POD将使用它运行 - 创建专用
Role
,指定所需要的API访问权限 - 创建
RoleBinding
, 将上述ServiceAccount
和Role
绑定
我们将上述的过程描述在rabc.yaml
文件中:
1 | apiVersion: v1 |
通过kubectl完成权限添加操作:
1 | [fg@kubernetes ~]$ kubectl apply -f rbac.yaml |
创建运行controller的Deployment
YAML文件ac-deployment.yaml
:
1 | apiVersion: apps/v1 |
完成controller部署:
1 | [fg@kubernetes ~]$ kubectl apply -f ac-deployment.yaml |
查看并找到相应pod:
1 | [fg@kubernetes ~]$ kubectl get pod |
然后,我们查看该POD的日志, 可以看到controller获取到了当前已存在的对象:
1 | [fg@kubernetes ~]$ kubectl logs -f ac-controller-7c996fddb6-66t9x |
打开另一终端,创建另一个AppConfig
对象的YAML
文件ac2.yaml
:
1 | apiVersion: "stable.flygoast.com/v1beta1" |
创建对象:
1 | [fg@kubernetes ~]$ kubectl apply -f ac2.yaml |
可以之前的终端上查看到新的日志输出:
1 | 2020-03-01 14:02:06,596 Object:ac2 content:"AppConfig 2" ADDED. |
新增对象的事件捕获到了。接着修改ac2.yaml
:
1 | apiVersion: "stable.flygoast.com/v1beta1" |
然后更新对象:
1 | [fg@kubernetes ~]$ kubectl apply -f ac2.yaml |
从之前的日志输出可以看到:
1 | 2020-03-01 14:10:16,319 Object:ac2 content:"AppConfig 2. Changed." MODIFIED. |
对象更新的事件也被捕获到。接下来,我们删除对象ac2
:
1 | [fg@kubernetes ~]$ kubectl delete ac ac2 |
从日志输出可以看到:
1 | 2020-03-01 14:11:05,511 Object:ac2 content:"AppConfig 2. Changed." DELETED. |
简单的controller实现可以基于相应对象的ADDED
、MODIFIED
、DELETED
事件调用回调函数,完成相应逻辑的处理。更为完善的Controller实现可以参考文后的链接。
参考链接:
- https://www.magalix.com/blog/extending-the-kubernetes-controller
- https://engineering.bitnami.com/articles/a-deep-dive-into-kubernetes-controllers.html
- https://engineering.bitnami.com/articles/kubewatch-an-example-of-kubernetes-custom-controller.html
- https://medium.com/programming-kubernetes/building-stuff-with-the-kubernetes-api-toc-84d751876650
- https://blog.meain.io/2019/accessing-kubernetes-api-from-pod/
- http://techgenix.com/crd-in-kubernetes/
- https://kubernetes.io/docs/concepts/extend-kubernetes/operator/
- https://flugel.it/building-kubernetes-operators-part-1-operator-pattern-and-concepts/
- https://stackoverflow.com/questions/47848258/kubernetes-controller-vs-kubernetes-operator
- https://octetz.com/docs/2019/2019-10-13-controllers-and-operators/