本文由 CNCF + Alibaba 云原生技术公开课 整理而来
需求来源
在 Kubernetes 里面, API 编程范式也就是 Custom Resources Definition(CRD)
。CRD
,其实指的就是用户自定义资源。
为什么会有用户自定义资源问题呢?
随着 Kubernetes 使用的越来越多,用户自定义资源的需求也会越来越多。而 Kubernetes 提供的聚合各个子资源的功能,已经不能满足日益增长的广泛需求了。用户希望提供一种用户自定义的资源,把各个子资源全部聚合起来。但 Kubernetes 原生资源的扩展和使用比较复杂,因此诞生了用户自定义资源这么一个功能。
示例
CRD
的一个实例:
CRD
功能是在 Kubernetes 1.7 版本被引入的,用户可以根据自己的需求添加自定义的 Kubernetes 对象资源。值得注意的是,这里用户自己添加的 Kubernetes 对象资源都是 native 的、都是一等公民,和 Kubernetes 中自带的、原生的那些 Pod
、Deployment
是同样的对象资源。在 Kubernetes 的 API Server
看来,它们都是存在于 etcd
中的一等资源。
同时,自定义资源和原生内置的资源一样,都可以用 kubectl
来去创建、查看,也享有 RBAC、安全功能。用户可以开发自定义控制器来感知或者操作自定义资源的变化。
一个简单的 CRD
定义:
apiVersion: apiextension.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
文件解析:
apiVersion: apiextension.k8s.io/v1beta1 表示 CRD 当前所属的组是 apiextension.k8s.io,版本是 v1beta1
kind 表示 Kubernetes 资源类型是 CRD
metadata 表示 CRD 的元数据,元数据通常包含 name,name 建议遵循 顶级域名.xxx.APIGroup 这样的格式规范
spec 表示 CRD 的期望状态,group 表示 CRD 的 api 组;version 表示 CRD 的 api 版本;names 指定 CRD 的 kind 和 plural(昵称,用于简化长度);
scope 表示 CRD 是否被 namespace 管理。如 ClusterRoleBinding 是 Cluster 级别的;Pod、Deployment 可以被创建到不同的命名空间里,它们的 scope 就是 Namespaced 的
上面定义的 CRD
的一个实例:
apiVersion: samplecontroller.k8s.io/v1alpha1
kind: Foo
metadata:
name: example-foo
spec:
deploymentName: example-foo
replicas: 1
文件解析:
apiVersion: samplecontroller.k8s.io/v1alpha1 表示 Foo 当前所属的组是 samplecontroller.k8s.io,版本是 v1alpha1
kind 表示 Kubernetes 资源类型是 Foo
metadata 表示 Foo 的元数据,元数据通常包含 name、namespace 等
spec 表示 Foo 的期望状态,可以在 spec 中根据自己的需求定义一些内容,遵循 key:value 格式
- 带有校验的
CRD
:
一个包含校验的 CRD
定义:
apiVersion: apiextension.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
和不包含校验的 CRD 相比,多了 .spec.validation 字段,用于校验。
首先是一个 openAPIV3Schema 的定义,spec 中则定义了有哪些资源,以 replicas 为例,这里将 replicas 定义为一个 integer 的资源,最小值为 1,最大值是 10。
那么,当再次使用这个 CRD 的时候,如果给出的 replicas 不是 int 值,或者去写一个 -1 或大于 10 的值,这个 CRD 对象就不会被提交到 API Server,API Server 会直接报错,告诉不满足所定义的参数条件。
- 带有状态字段的
CRD
:
一个包含状态的 CRD
定义:
apiVersion: apiextension.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
subresources:
status: {
}
状态实际上是一个自定义资源的子资源,它的好处在于,对该字段的更新并不会触发 Deployment 或 Pod 的重新部署。
对于某些 Deployment 和 Pod,只要修改了某些 spec,它就会重新创建一个新的 Deployment 或者 Pod 出来。
但是状态资源并不会被重新创建,它只是用来回应当前 Pod 的整个状态。上面的 CRD 声明中它的子资源的状态非常简单,就是一个 key:value 的格式。在 "{}" 里写什么,都是自定义的。
架构设计
- 控制器概览:
只定义一个 CRD
其实没有什么作用,它只会被 API Server
简单地计入到 etcd
中。如何依据这个 CRD
定义的资源和 Schema
来做一些复杂的操作,则是由 Controller
来实现的。
Controller
其实是 Kubernetes 提供的一种可插拔式的方法来扩展或者控制声明式的 Kubernetes 资源。它是 Kubernetes 的大脑,负责大部分资源的控制操作。以 Deployment
为例,它就是通过 kube-Controller-Manager
来部署的。
比如说声明一个 Deployment
有 replicas
、有 2 个 Pod,那么 Kube-Controller-Manager
在观察 etcd
时接收到了该请求之后,就会去创建两个对应的 Pod
的副本,并且它会去实时地观察着这些 Pod
的状态,如果这些 Pod
发生变化了、回滚了、失败了、重启了等等,它都会去做一些对应的操作。
所以 Controller 才是控制整个 Kubernetes 资源最终表现出来的状态的大脑。用户声明完成 CRD
之后,也需要创建一个控制器来完成对应的目标。比如之前的 Foo,它希望去创建一个 Deployment
,replicas
为 1,这就需要创建一个控制器用于创建对应的 Deployment
才能真正实现 CRD
的功能。
- 控制器工作流程:
以 Kube-Controller-Manager
为例,左侧是一个 Informer
,它的机制就是通过去 watch
Kube-ApiServer
,而 Kube-ApiServer
会去监督所有 etcd
中资源的创建、更新与删除。Informer
主要有两个方法:ListFunc
和 WatchFunc
。
ListFunc
就是像 kuberctl get pods
这类操作,把当前所有的资源都列出来;WatchFunc
会和 Kube-ApiServer
建立一个长链接,一旦有一个新的对象提交上去之后,apiserver 就会反向推送回来,告诉 Informer 有一个新的对象创建或者更新等操作。
Informer
接收到了对象的需求之后,就会调用对应的函数(比如 AddFunc, UpdateFunc, DeleteFunc
),并将其按照 key 值的格式放到一个队列中去,key 值的命名规则就是 namespace/name
,name
就是对应的资源的名字。比如在 default
的 namespace
中创建一个 foo 类型的资源,那么它的 key 值就是 default/example-foo
。Controller
从队列中拿到一个对象之后,就会去做相应的操作。
首先,通过 Kube-ApiServer
来推送事件,比如 Added, Updated, Deleted
;然后进入到 Controller
的 ListAndWatch()
循环中;ListAndWatch
中有一个先入先出的队列,在操作的时候就将其 Pop()
出来;然后去找对应的 Handler,Handler 会将其交给对应的函数(比如 Add(), Update(), Delete()
)。
一个函数一般会有多个 Worker。多个 Worker 的意思是说比如同时有好几个对象进来,那么这个 Controller
可能会同时启动 5 个、10 个这样的 Worker 来并行地执行,每个 Worker 可以处理不同的对象实例。
工作完成之后,即把对应的对象创建出来之后,就把这个 key 丢掉,代表已经处理完成。如果处理过程中有什么问题,就直接报错,打印出一个事件来,再把这个 key 重新放回到队列中,下一个 Worker 就可以接收过来继续进行相同的处理。
- 总结:
CRD
是 Custom Resources Definition
的缩写,也就是用户自定义资源,用户可以使用这个功能扩展自己的 Kubernetes 原生资源信息;
CRD
和普通的 Kubernetes 资源一样,都可以受 RBAC
权限控制,并且支持 status
状态字段;
CRD-controller
也就是 CRD
控制器,能够实现用户自行编写,并且解析 CRD
并把它变成用户期望的状态。