Custom Resource Define 简称 CRD,是 Kubernetes(v1.7+)为提高可扩展性,让开发者去自定义资源的一种方式。CRD 资源可以动态注册到集群中,注册完毕后,用户可以通过 kubectl 来创建访问这个自定义的资源对象,类似于操作 Pod 一样。不过需要注意的是 CRD 仅仅是资源的定义而已,需要一个 Controller 去监听 CRD 的各种事件来添加自定义的业务逻辑。

定义

如果说只是对 CRD 资源本身进行 CRUD 操作的话,不需要 Controller 也是可以实现的,相当于就是只有数据存入了 etcd 中,而没有对这个数据的相关操作而已。比如我们可以定义一个如下所示的 CRD 资源清单文件:(crd-demo.yaml)

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
# name 必须匹配下面的spec字段:<plural>.<group>
name: crontabs.stable.example.com
spec:
# group 名用于 REST API 中的定义:/apis/<group>/<version>
group: stable.example.com
# 列出自定义资源的所有 API 版本
versions:
- name: v1beta1 # 版本名称,比如 v1、v2beta1 等等
served: true # 是否开启通过 REST APIs 访问 `/apis/<group>/<version>/...`
storage: true # 必须将一个且只有一个版本标记为存储版本
schema: # 定义自定义对象的声明规范
openAPIV3Schema:
description: Define CronTab YAML Spec
type: object
properties:
spec:
type: object
properties:
cronSpec:
type: string
image:
type: string
replicas:
type: integer
# 定义作用范围:Namespaced(命名空间级别)或者 Cluster(整个集群)
scope: Namespaced
names:
# kind 是 sigular 的一个驼峰形式定义,在资源清单中会使用
kind: CronTab
# plural 名字用于 REST API 中的定义:/apis/<group>/<version>/<plural>
plural: crontabs
# singular 名称用于 CLI 操作或显示的一个别名
singular: crontab
# shortNames 相当于缩写形式
shortNames:
- ct

需要注意的是 v1.16 版本以后已经 GA 了,使用的是 v1 版本,之前都是 v1beta1,定义规范有部分变化,所以要注意版本变化。

这个地方的定义和我们定义普通的资源对象比较类似,我们说我们可以随意定义一个自定义的资源对象,但是在创建资源的时候,肯定不是任由我们随意去编写 YAML 文件的,当我们把上面的 CRD 文件提交给 Kubernetes 之后,Kubernetes 会对我们提交的声明文件进行校验,从定义可以看出 CRD 是基于 OpenAPI v3 schem 进行规范的。当然这种校验只是对于字段的类型进行校验,比较初级,如果想要更加复杂的校验,这个时候就需要通过 Kubernetes 的 admission webhook 来实现了。关于校验的更多用法,可以前往官方文档查看。

同样现在我们可以直接使用 kubectl 来创建这个 CRD 资源清单:

$ kubectl apply -f crd-demo.yaml
customresourcedefinition.apiextensions.k8s.io/crontabs.stable.example.com created

这个时候我们可以查看到集群中已经有我们定义的这个 CRD 资源对象了:

$ kubectl get crd |grep example
crontabs.stable.example.com 2019-12-19T02:37:54Z

这个时候一个新的 namespace 级别的 RESTful API 就会被创建:

/apis/stable/example.com/v1beta1/namespaces/*/crontabs/...

然后我们就可以使用这个 API 端点来创建和管理自定义的对象,这些对象的类型就是上面创建的 CRD 对象规范中的 CronTab。

现在在 Kubernetes 集群中我们就多了一种新的资源叫做 crontabs.stable.example.com,我们就可以使用它来定义一个 CronTab 资源对象了,这个自定义资源对象里面可以包含的字段我们在定义的时候通过 schema 进行了规范,比如现在我们来创建一个如下所示的资源清单:(crd-crontab-demo.yaml)

apiVersion: "stable.example.com/v1beta1"
kind: CronTab
metadata:
name: my-new-cron-object
spec:
cronSpec: "* * * * */5"
image: my-awesome-cron-image

我们可以直接创建这个对象:

$ kubectl apply -f crd-crontab-demo.yaml
crontab.stable.example.com/my-new-cron-object created

然后我们就可以用 kubectl 来管理我们这里创建 CronTab 对象了,比如:

$ kubectl get ct  # 简写
NAME AGE
my-new-cron-object 42s
$ kubectl get crontab
NAME AGE
my-new-cron-object 88s

在使用 kubectl 的时候,资源名称是不区分大小写的,我们可以使用 CRD 中定义的单数或者复数形式以及任何简写。

我们也可以查看创建的这个对象的原始 YAML 数据:

$ kubectl get ct -o yaml
apiVersion: v1
items:
- apiVersion: stable.example.com/v1beta1
kind: CronTab
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"stable.example.com/v1beta1","kind":"CronTab","metadata":{"annotations":{},"name":"my-new-cron-object","namespace":"default"},"spec":{"cronSpec":"* * * * */5","image":"my-awesome-cron-image"}}
creationTimestamp: "2019-12-19T02:52:55Z"
generation: 1
name: my-new-cron-object
namespace: default
resourceVersion: "12342275"
selfLink: /apis/stable.example.com/v1beta1/namespaces/default/crontabs/my-new-cron-object
uid: dace308d-5f54-4232-9c7b-841adf6bab62
spec:
cronSpec: '* * * * */5'
image: my-awesome-cron-image
kind: List
metadata:
resourceVersion: ""
selfLink: ""

我们可以看到它包含了上面我们定义的 cronSpec 和 image 字段。

Controller

就如上面我们说的,现在我们自定义的资源创建完成了,但是也只是单纯的把资源清单数据存入到了 etcd 中而已,并没有什么其他用处,因为我们没有定义一个对应的 Controller 来处理他。

官方提供了一个自定义 Controller 的示例:https://github.com/kubernetes/sample-controller,实现了:

  • 如何注册资源 Foo
  • 如何创建、删除和查询 Foo 对象
  • 如何监听 Foo 资源对象的变化情况

要想了解 Controller 的实现原理和方式,我们就需要了解下 client-go 这个库的实现,Kubernetes 部分代码也是基于这个库实现的,也包含了开发自定义控制器时可以使用的各种机制,这些机制在 client-go 源码的 tools/cache 目录下面有定义。

下图显示了 client-go 中的各个组件是如何公众的以及与我们要编写的自定义控制器代码的交互入口:

client-go 组件:

  • Reflector:通过 Kubernetes API 监控 Kubernetes 的资源类型 采用 List/Watch 机制, 可以 Watch 任何资源包括 CRD 添加 object 对象到 FIFO 队列,然后 Informer 会从队列里面取数据
  • Informer:controller 机制的基础,循环处理 object 对象 从 Reflector 取出数据,然后将数据给到 Indexer 去缓存,提供对象事件的 handler 接口,只要给 Informer 添加 ResourceEventHandler 实例的回调函数,去实现 OnAdd(obj interface{})、 OnUpdate(oldObj, newObj interface{}) 和 OnDelete(obj interface{}) 这三个方法,就可以处理好资源的创建、更新和删除操作了。
  • Indexer:提供 object 对象的索引,是线程安全的,缓存对象信息

controller 组件: * Informer reference: controller 需要创建合适的 Informer 才能通过 Informer reference 操作资源对象 * Indexer reference: controller 创建 Indexer reference 然后去利用索引做相关处理 * Resource Event Handlers:Informer 会回调这些 handlers * Work queue: Resource Event Handlers 被回调后将 key 写到工作队列,这里的 key 相当于事件通知,后面根据取出事件后,做后续的处理 * Process Item:从工作队列中取出 key 后进行后续处理,具体处理可以通过 Indexer reference controller 可以直接创建上述两个引用对象去处理,也可以采用工厂模式,官方都有相关示例

client-go/tool/cache/ 和自定义 Controller 的控制流(图片来源):

如上图所示主要有两个部分,一个是发生在 SharedIndexInformer 中,另外一个是在自定义控制器中。

  • Reflector 通过 Kubernetes APIServer 执行对象(比如 Pod)的 ListAndWatch 查询,记录和对象相关的三种事件类型Added、Updated、Deleted,然后将它们传递到 DeltaFIFO 中去。
  • DeltaFIFO 接收到事件和 watch 事件对应的对象,然后将他们转换为 Delta 对象,这些 Delta 对象被附加到队列中去等待处理,对于已经删除的,会检查线程安全的 store 中是否已经存在该文件,从而可以避免在不存在某些内容时排队执行删除操作。
  • Cache 控制器(不要和自定义控制器混淆)调用 Pop() 方法从 DeltaFIFO 队列中出队列,Delta 对象将传递到 SharedIndexInformer 的 HandleDelta() 方法中以进行进一步处理。
  • 根据 Delta 对象的操作(事件)类型,首先在 HandleDeltas 方法中通过 indexer 的方法将对对象保存到线程安全的 Store 中,然后,通过 SharedIndexInformer 中的 sharedProcessor 的 distribution() 方法将这些对象发送到事件 handlers,这些事件处理器由自定义控制器通过 SharedInformer 的方法比如 AddEventHandlerWithResyncPeriod() 进行注册。
  • 已注册的事件处理器通过添加或更新时间的 MetaNamespaceKeyFunc() 或删除事件的 DeletionHandingMetaNamespaceKeyFunc() 将对象转换为格式为 namespace/name 或只是 name 的 key,然后将这个 key 添加到自定义控制器的 workqueue 中,workqueues 的实现可以在 util/workqueue 中找到。
  • 自定义的控制器通过调用定义的 handlers 处理器从 workqueue 中 pop 一个 key 出来进行处理,handlers 将调用 indexer 的 GetByKey() 从线程安全的 store 中获取对象,我们的业务逻辑就是在这个 handlers 里面实现。

client-go 中也有自定义 Controller 的样例代码,位于:k8s.io/client-go/examples/workqueue/main.go。

Kubernetes Operator: CRD的更多相关文章

  1. Kubernetes Operator基础入门

    本文转自Rancher Labs 你是否曾经想过SRE团队是如何有效地成功管理复杂的应用?在Kubernetes生态系统中,Kubernetes Operator可以给你答案.在本文中,我们将研究Op ...

  2. 当我们聊kubernetes operator时,我们在聊些什么

    不聊什么 在开始聊operator前,先说说这篇文章里我们不聊什么.我们这里不聊operator的具体实现,不聊operator的由来历史,不聊operator的hello world.如果想了解这些 ...

  3. Kubernetes Operator: Operator

    Operator 就可以看成是 CRD 和 Controller 的一种组合特例,Operator 是一种思想,它结合了特定领域知识并通过 CRD 机制扩展了 Kubernetes API 资源,使用 ...

  4. 谈谈 Kubernetes Operator

    简介 你可能听过Kubernetes中Operator的概念,Operator可以帮助我们扩展Kubernetes功能,包括管理任何有状态应用程序.我们看到了它被用于有状态基础设施应用程序的许多可能性 ...

  5. 在Kubernetes上运行有状态应用:从StatefulSet到Operator

    一开始Kubernetes只是被设计用来运行无状态应用,直到在1.5版本中才添加了StatefulSet控制器用于支持有状态应用,但它直到1.9版本才正式可用.本文将介绍有状态和无状态应用,一个通过K ...

  6. 亲历者说:Kubernetes API 与 Operator,不为人知的开发者战争

    如果我问你,如何把一个 etcd 集群部署在 Google Cloud 或者阿里云上,你一定会不假思索的给出答案:当然是用 etcd Operator! 实际上,几乎在一夜之间,Kubernetes ...

  7. Spark in action on Kubernetes - Spark Operator的原理解析

    前言 在上篇文章中,向大家介绍了如何使用Spark Operator在kubernetes集群上面提交一个计算作业.今天我们会继续使用上篇文章中搭建的Playground进行调试与解析,帮助大家更深入 ...

  8. 第24 章 : Kubernetes API 编程利器:Operator 和 Operator Framework

    Kubernetes API 编程利器:Operator 和 Operator Framework 本节课程主要分享以下三方面的内容: operator 概述 operator framework 实 ...

  9. 12 . Kubernetes之Statefulset 和 Operator

    Statefulset简介 k8s权威指南这样介绍的 "在Kubernetes系统中,Pod的管理对象RC.Deployment.DaemonSet和Job都面向无状态的服务.但现实中有很多 ...

随机推荐

  1. DBSync新增对MongoDB、ES的支持

    数据库同步工具DBSync近日进行了升级,最新版本为V1.9,新增了对MongoDB.Elasticseach(ES)的支持,具体情况:1.支持同型库之间的同步,如:MongoDB至MongoDB,E ...

  2. JsonPath:针对json的强大的规则解析与参数查找工具

    项目特点 GitHub项目地址:https://github.com/json-path/JsonPath 主要功能: 将Json字符串转为Java Map对象(这个不算什么,FastJson之类的工 ...

  3. VxWorks环境搭建与学习

    搭建环境所需的所有资源: https://pan.baidu.com/s/1sUF2I_DBHs-86IUJ4Ykn2Q 提取码: t7sj 实时系统vxWorks - Shell命令 https:/ ...

  4. Python究竟属不属于嵌入式语言?

    写在前面: 几十年来,大家普遍的认为C与C++才是标准的嵌入式语言,那么现在大火的Python算是一种嵌入式语言吗? 在给出我的答案之前我们要先明确几个问题? 什么是Python? 编程语言的定义? ...

  5. 苹果手机和Windows之间互传文件

    参考链接:https://jingyan.baidu.com/article/a378c960c46804f229283064.html 实现原理:就是使用Samba服务,windows共享一个文件夹 ...

  6. for_in循环练习题_100到999之间的水仙花数

    水仙花数 153 == 3**3 + 5**3 + 1**3 点击查看笔者代码 for i in range(100, 1000): a = i % 10 b = i // 100 c = (i // ...

  7. 从零开始在centos搭建博客(二)

    本篇为备份篇. 因为装的东西不多,所以需要备份的只有mysql和wordpress的文件夹. 备份mysql mysql备份命令 使用mysqldump命令,格式如下: # 这是格式 mysqldum ...

  8. angular变更检测相关文章

    你需要了解的关于Angular 变更检测的一切 If you think `ngDoCheck` means your component is being checked - read this a ...

  9. 剑指offer——day-1

    今天开始记录一下剑指offer的题目训练,提升一下自己的编程能力吧 题目一: 用两个栈实现一个队列.队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列 ...

  10. Linux—系统基础一

    Linux系统基础(一) Linux的基本原则: 由目的单一的小程序组成,组合小程序完成复杂任务: 一切皆文件: 配置文件保存为纯文本格式. 1.shell 1.1 shell简介 Shell俗称壳( ...