目录

Kubernetes自定义资源和api-server-aggregation

概述

在 Kubernetes 里,Resource 也叫资源,实际上是 Kubernetes API 里的一个 Endpoint 类型,比如内置的 Pod,也是一种资源类型。Custom Resources 自定义资源是在 Kubernetes 的默认配置下,对资源类型的扩展。

实际上,自定义资源类型只是让用户可以创建和查询到资源对象,当结合 Custom Controller 自定义控制器,才能像 Kubernetes 里其他对象一样去做 CRUD 的操作。通过自定义控制器,可以控制自定义资源的生命周期。通过这种贯穿于 Kubernetes 里的控制循环的思想,可以让集群里的资源对象按照预定义的状态,不断地去调整,CRD仅仅是资源的定义,而 Controller 可以去监听CRD的CRUD事件来添加自定义业务逻辑。

在官网里提示的,有两种方式来让用户去扩展 Kubernetes 的资源类型。

  1. CustomResourceDefinitions: 复用Kubernetes的API Server,CRDs+CRD Controller
  2. API server aggregation: 需要编写API Server,提供更细粒度的控制

这两种方式,其实挺不好理解的。简单理解的话,就是后者需要写更多的代码,去创建自己的 API Server,这里说的 API Server 跟 Master 上的 API Server 是类似的概念,但不完全相同,这里的 API Server 更想表达的意思是为了实现自定义资源来扩展 Kubernetes 的功能或者说工作负荷,用户需要按照 API Server 的模式来去创建自己的 API Server,然后通过 aggregate 这一层组件来实现聚合,也就是说,用户为了增加自定义资源,并且希望注册到 Core API Server 中,是需要写更多的代码的,但是他比前者带来一个更显著的好处就是,他的灵活性是更高的,毕竟你写的是类似 Core API Server 的东西,关于资源的存储、认证这些都可以做的更加灵活,甚至复用一些已经存在的组件。

目前社区上看到很多的 Operator 多数是基于 CustomResourceDefinitions 来做的,例如 Spark Operator,Tf Operator 等等。

Operator 的模式,是由 coreos 提出的,结合自定义资源和自定义控制器的常见套路。定义一个自定义资源并不难,甚至非常简单,也就几行 yaml,或者代码里一个 struct 结构体就完事了,真正比价麻烦的是写一个自定义控制器来控制这个自定义资源对象。

示例

一个 CRD 的 yaml 文件示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  # name must match the spec fields below, and be in the form: <plural>.<group>
  name: crontabs.stable.example.com
spec:
  # group name to use for REST API: /apis/<group>/<version>
  group: stable.example.com
  # list of versions supported by this CustomResourceDefinition
  version: v1beta1
  # either Namespaced or Cluster
  scope: Namespaced
  names:
    # plural name to be used in the URL: /apis/<group>/<version>/<plural>
    plural: crontabs
    # singular name to be used as an alias on the CLI and for display
    singular: crontab
    # kind is normally the CamelCased singular type. Your resource manifests use this.
    kind: CronTab
    # shortNames allow shorter string to match your resource on the CLI
    shortNames:
    - ct

通过 kubectl create -f crd.yaml 可以创建一个 CRD。kubectl get CustomResourceDefinitions 可以获取创建的所有CRD。

/kubernetes%E8%87%AA%E5%AE%9A%E4%B9%89%E8%B5%84%E6%BA%90%E5%92%8Capi-server-aggregation/img.png

然后可以通过 kubectl create -f my-crontab.yaml 可以创建一个CRD的实例:

1
2
3
4
5
6
7
apiVersion: "stable.example.com/v1beta1"
kind: CronTab
metadata:
  name: my-new-cron-object
spec:
  cronSpec: "* * * * */5"
  image: my-awesome-cron-image
/kubernetes%E8%87%AA%E5%AE%9A%E4%B9%89%E8%B5%84%E6%BA%90%E5%92%8Capi-server-aggregation/img_1.png

实现一个Controller

如何去实现一个 Controller 呢?可以使用 Go 来实现,并且不论是参考资料还是开源支持都非常好,推荐有 Go 语言基础的优先考虑用 client-go 来作为 Kubernetes 的客户端,用 KubeBuilder 来生成骨架代码。一个官方的 Controller 示例项目是sample-controller。

Controller 的逻辑其实是很简单的,监听CRD实例(以及关联的资源)的CRUD事件,然后执行相应的业务逻辑

这里需要解释一下为什么创建 Pod 的数量需要减去 existedReadyPodNumber。经观察发现,如果现存有 CRD 实例,然后启动Controller,会立即收到CRD的added事件,即使 Pod 都是健康的,这会导致创建出双倍的 Pod。所以这里需要判断一下现存的Pod 数量,如果够了,就不去创建了。

但是这又会引入一个问题,假如我将 MyDeployment 删掉了,此时会级联删除关联的 Pod,在没来得及删掉之前,又去创建一个新的 MyDeployment,这时候会发现现存的 Pod 数量并非为0,所以新建的 Pod 数量就不能达到replicas。解决办法是去判断一下Pod的状态,如果是NotReady,那么就不算是正常的Pod。

但这种解决思路还是有问题,在删掉MyDeployment之后不会立即将Pod状态置为NotReady,需要一定时间,在这段时间内如果创建MyDeployment,那么还是有可能会出现少创建Pod的情况。

创建一个Pod的实现,将这段代码放到了MyDeployment中,以表示从属关系(代码也好写一些)。

如果Pod是从属于某个MyDeployment,那么我们应该将OwnerReference传入;

Pod的name必须以MyDeployment的name为前缀,后面加上唯一ID;

Pod的spec必须与MyDeployment的spec中的pod template一致;

Pod的labels中包含MyDeployment的label,并且要加上一个pod-template哈希值,以在更新资源时判断pod template是否改变,如果没有变化,则不触发modified事件。

Sample Controller

下面介绍一下 Sample Controller 的实现和 Demo。

/kubernetes%E8%87%AA%E5%AE%9A%E4%B9%89%E8%B5%84%E6%BA%90%E5%92%8Capi-server-aggregation/img_2.png

参考资料

  1. custom-resources
  2. 官方文档
  3. CRD Yaml的Schema
  4. Kubernetes1.9管中窥豹-CRD概念、使用场景及实例
  5. kubebuilder
警告
本文最后更新于 2017年2月1日,文中内容可能已过时,请谨慎参考。