Kubernetes 声明式API

对于声明式API来说,只需向系统提交一个定义好的API对象来声明资源对象的"期望状态"。然后由系统去确保资源对象从"当前状态"迁移到"期望状态"。这里的API对象是一种"意向表达(Record of Intent)“。创建API对象的本质上是在告知Kubernetes系统,期望资源对象看起来应该是什么样子,这就是Kubernetes系统所谓的期望状态(Desired State)。更多API对象的介绍可以参考笔者Kubernetes API对象一文或者参考官网Kubernetes API Overview一文。
声明式API践行了"将简单留给用户,将复杂流程系统"的设计理念。对于用户来说,只需掌握API对象的使用,无需关心内部的实现细节。系统会使用控制器模式将资源对象调谐到API对象指定的"期望状态”。声明式API是Kubernetes最核心的设计理念,正因为实现了对声明式API的支持,才可在基于Kubernetes构建的上层平台使用一致的编程范式和交互编程界面,才使得今天整个云原生生态中诞生了如此多的Kubernetes插件能力和扩展。

API对象

在Kubernetes中,一个API对象的完整资源路径是由Group(API组)、Version(API版本)和Resource(API资源类型)3个部分组成的。通过这样的结构,整个Kubernetes里的所有API对象就可以用如下的树形结构表示出来:

请添加图片描述

如上图中,Resource类型为Job的API对象,其分组是batch,Version是v1。可以看到对于Kubernetes的核心API对象,如Pod、Service、ConfigMap、Node、Namespace等,其所在Group为空。所以,对于这些API对象,Kubernetes会直接放置在/api这个层级下。

查找API对象

在Kubernetes中需要查找API对象时,首先匹配API对象的Group。对于核心API对象,Kubernetes会在/api这个层级下匹配。对于非核心对象,如Job、CronJob、Deployment、StatefulSet等,Kubernetes就会在/apis这个层级查找它对应的Group。以Job、CronJob为例,其Group为batch,则对应层级是/apis/batch。
然后,Kubernetes会进一步匹配到API对象的Version。在Kubernetes中,同一种API对象可以有多个版本,这正是Kubernetes进行API版本化管理的重要手段。
最后,Kubernetes会匹配API对象的Resource。同一个Group、同一个Version,可以拥有多种不同的资源类型。如Group为batch,Version为v1下的资源类型有Job、CronJob。这样,就可以匹配到需要查找的API对象。以Job为例,对应的层级是/apis/batch/v1/jobs。
一个Kubernetes集群,可以通过"kubectl api-resources"命令查看本集群所支持的全体API类型。

创建API对象

熟悉了API对象的查找过程后,接下来介绍Kubernetes创建API对象的过程。这里以创建一个/apis/batch/v2alpha1层级下的CronJob对象为例,创建过程对应流程如下图所示:

请添加图片描述

(1) 通过kubectl命令或API Server提供的接口发起创建API对象的POST请求后,编写的API对象的YAML信息会被提交给API Server。而API Server的第一个功能,就是过滤这个请求,并完成一些前置性的工作,比如授权、超时处理、审计等。
(2) 然后,请求会进入MUX和Routes流程。MUX和Routes是API Server完成URL和Handler绑定的场所。而API Server的Handler要做的事情,就是查找到对应的API对象的类型定义,这里是CronJob对象。
(3) 接着,API Server会根据这个API对象的类型定义,使用用户提交的YAML文件里的字段,创建一个API对象,这里是CronJob对象。在这个过程中,API Server会进行一个Convert工作,即:把用户提交的YAML文件,转换成一个叫作Super Version的对象,它正是该API资源类型所有版本的字段全集。这样用户提交的不同版本的YAML文件,就都可以用这个Super Version对象来进行处理了。
(4) 执行完上一步后,API Server会先后进行Admission()和Validation()操作。Admission负责对API对象做进一步的验证或添加默认参数。如在某些情况下,为了适用于应用系统的配置,Admission逻辑可能会改变目标对象。此外,Admission逻辑也会改变请求操作的一部分相关资源。而Validation,则负责验证这个API对象里的各个字段是否合法。这个被验证过的API对象,都保存在了API Server里一个叫作Registry的数据结构中。也就是说,只要一个API对象的定义能在Registry里查到,它就是一个有效的Kubernetes API对象。
(5) 最后,API Server会把验证过的API对象转换成用户最初提交的版本,进行序列化操作,并调用存储服务(默认是etcd)保存起来。

自定义API对象

除了Kubernetes定义的API对象,Kubernetes也支持自定义API对象,这对扩展Kubernetes的功能有着重要的意义。为支持自定义API对象,Kubernetes在1.7版本之后,引入了CRD(Custom Resource Definition,自定义资源定义)插件机制。这样用户就可在Kubernetes中添加一个跟Pod、Node类似的、新的API资源类型,即:自定义API资源。这就如面向对象编程,如果想创建一个对象,应先定义一个类。在Kubernetes中,可以使用资源类型为CustomResourceDefinition的资源来声明自定义API资源。
在编写资源类型为CustomResourceDefinition的YAML文件前,先介绍下Kubernetes对CustomResourceDefinition的完整Schema约束。

apiVersion: apiextensions.k8s.io/v1               # CustomResourceDefinition所在分组及版本号
kind: CustomResourceDefinition                    # 指定资源类型为CustomResourceDefinition
metadata:                                         # 遵循一般地ObjectMeta定义
  annotations: object                                 # 记录一些注释信息,如轻量级上线工具的元数据信息、负责人员的电话或呼机号码等。
  creationTimestamp: time                             # 记录元数据的创建时间
  deletionGracePeriodSeconds: int                     # 优雅删除等待时间
  deletionTimestamp: time                             # 记录元数据的删除时间
  finalizers:                                         # 告诉 Kubernetes等到特定的条件被满足后,再完全删除被标记为删除的资源
  - string
  - string
  generation: int                                     # 记录期望状态的版本的序列号
  lables: object                                      # 标签用于指定对用户有意义且相关的对象的标识属性,但不直接对核心系统有语义含义
  name: string                                        # 标识元数据的名称
  namespace: string                                   # 指定作用的namespace
  ownerReferences:                                    # 指定属主(Owner)
  - object
  - object
  uid: string                                         # 标识元数据,在整个集群中具有唯一性
spec:                                             # 描述期望状态及一些基础信息
  group: string                                       # 指定自定义资源所在分组
  scope: Namespaced | Cluster                         # 指定资源作用于Cluster还是Namespace
  versions:                                           # 记录当前自定义资源支持的版本信息
    - name: string                                       # 版本名称,如v1、v2beta1等
      deprecated: boolean                                # 指定版本是否已经废弃,默认是false
      served: boolean                                    # 指定版本是否可以作为REST API使用
      storage: boolean                                   # 表示在将自定义资源保存到存储时应使用此版本。必须只有一个版本storage=true。同一时间段,只能使用一个版本
      schema:                                            # schema用来声明对自定义资源的字段规范
        openAPIV3Schema:                                     # OpenAPI v3 schema,定义遵循JSON Schema
          type: string                                          # 指定字段的类型
          properties: object                                    # 指定字段名及类型
          ...
  conversion:                                         # 记录转换版本的策略
    strategy: None | Webhook                             # 选用的转换版本的策略
    webhook: object                                      # 策略为webhook的配置信息
  names:                                              # 指定自定义资源的类型名称
    kind: string                                         # 自定义资源的类型名称
    plural: string                                       # 自定义资源的复数名称
    singular: string                                     # 自定义资源的单数名称
    shortNames:                                          # 提供的自定义资源的简写
    - string
    - string
    categories:                                          # 指定自定义资源所属的类别,用于客户端(如kubectl get all)查找
    - string
    - string
status:                                           # 描述当前状态,由Kubernetes自动更新
  acceptedNames: object                              # 记录已经使用的名称
  conditions:                                        # 记录自定义资源的特定状态
  - object
  - object
  storedVersions:                                    # 自定义资源使用过的版本列表
  - string
  - string

可以看下,CustomResourceDefinition中字段的定义遵循一般的API对象中要求的字段,核心字段都是由apiVersion、kind、metadata、spec等组成,只是在字段含义上进行了扩展。更多CustomResourceDefinition的spec字段说明可以参考Kubernetes官网CustomResourceDefinitionSpec v1 apiextensions一文。更多CustomResourceDefinition的status字段说明可以参考Kubernetes官网CustomResourceDefinitionStatus v1 apiextensions一文。
接下来给出一个自定义资源的定义示例:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: example.custom.resource.definition
spec:
  group: example.k8s.io
  versions:
  - name: v1
    served: true
    storage: true
    schema:
      openAPIV3Schema:
        type: object
        properties:
          host:
            type: string
          port:
            type: string
  names:
    kind: CustomResource
    plural: customresources
    singular: customresource
  scope: Namespaced

这样,就定义了Group为example.k8s.io,Version为v1,kind为CustomResource的自定义资源类型。其中该类型资源包含两个关键字段host和port。使用"kubectl apply"命令后,就可在Kubernetes中创建该自定义资源。
完成了自定义资源的创建后,接下来就可以创建一个自定义API对象了。基于上述自定义资源创建的API对象示例如下:

apiVersion: example.k8s.io/v1
kind: CustomResource
metadata:
  name: demo-custom-resource
spec:
  host: "192.168.1.1"
  port: "80"

这样就定义了一个资源类型为CustomResource的API对象,其中有两个业务字段:host、port。然后使用使用"kubectl apply"命令后,就可在Kubernetes中创建该API对象。然后使用"kubectl get customresource xxx"就可以查看到新创建的API对象。

自定义控制器

声明式API的特点是只需告知系统资源对象的"期望状态",而无需关心系统如何实现从"当前状态"迁移到"期望状态"。在Kubernetes中,使用控制器确保资源对象在"期望状态"的调谐。对于自定义API对象,如果需要维持其"期望状态",还需定义一个自定义控制器。这篇文章重点关注自定义控制器的原理,更多自定义控制器的编码实现可以参考《深入剖析Kubernetes》一书或参考网络上相关软文。
自定义控制器的实现逻辑遵循一般的控制器的实现逻辑,这里给出控制器的实现逻辑图:

请添加图片描述

在上图中,控制器通过Informer来跟踪资源。Informer的Reflector负责与API Server保持通信,并基于List & Watch机制观察所关心的资源。一旦资源发生事件变更,Informer的Reflector就将资源列表和后续的资源变化放到FIFO。然后Informer会将相应的callbacks存入WorkQueue并等待Worker将其取出运行。Worker首先会比较WorkQueue中资源对象的实际状态与期望状态的差别,然后执行相应的业务逻辑(如删除不再需要的资源、创建新的资源等),直到达成期望状态。Worker在执行业务逻辑时,需要调用Client提供的CRUD 方法,由其代为向API Server发送执行请求。Api Server完成执行后,执行结果会被写入etcd,相应的记录也会被更新到API Server。
在上述逻辑中,控制器部分只需要实现期望状态的调谐即可,也即Worker的工作。此外,在控制器里可以对自定义API对象和默认API对象进行协同,来实现更复杂的编排功能。
控制器的实现体现了标准的"Kubernetes编程范式",即如何使用控制器模式同Kubernetes里API对象的"增、删、改、查"进行协作,进而完成用户业务逻辑的编写过程。简言之,Kubernetes的编程范式就是CRD + 自定义控制器。其中CRD用来定义资源对象,控制器则用来处理相应API对象的控制逻辑。

Operator

Operator是一种封装、部署和管理Kubernetes应用的方法。Operator是一种特定于应用的控制器,可扩展 Kubernetes API的功能,来代表Kubernetes用户创建、配置和管理复杂应用的实例。Operator可以在不修改Kubernetes自身代码的情况下,通过为一个或多个自定义资源关联控制器来扩展Kubernetes集群的能力。Operator基于Kubernetes资源和控制器构建,但又涵盖了特定于领域或应用的知识,用于实现其所管理软件的整个生命周期的自动化。Operator是将运维人员对软件操作的知识给代码化,同时利用Kubernetes强大的抽象来管理大规模的软件应用。
Operator使用自定义资源和自定义控制器实现应用的管理。Operator实际上是利用Kubernetes的CRD来描述需要部署的应用,然后在自定义控制器里根据自定义API对象的变化,来完成具体的部署和运维工作。高级配置和设置由用户在自定义资源中提供。Operator基于嵌入在Operator逻辑中的最佳实践将高级指令转换为低级操作。自定义资源是Kubernetes中的API对象扩展机制。自定义资源定义会明确自定义资源并列出可用的所有配置。自定义控制器实施控制循环,反复比较集群的理想状态和实际状态。如果集群的实际状态与理想状态不符,控制器将采取措施解决此问题。
Kubernetes Operator几乎可执行任何操作:按需部署应用、扩展复杂的应用、应用的版本升级、使用专用硬件管理计算集群中节点的内核模块,等等。
Operator的实现由标准的开发流程,这里不展开说明,有需求的同学还请自行学习。

参考

《Kubernetes权威指南 从Docker到Kubernetes实践全接触》 龚正 吴治辉 闫健勇 编著
《深入剖析Kubernetes》 张磊 著
https://www.bookstack.cn/read/learning-cloudnative/7bb7c6d9505bf389.md 声明式API
https://blog.csdn.net/KevinBetterQ/article/details/104012293 Kubernetes 核心理解之声明式API和编程范式
https://www.meshcloud.io/en/blog/should-i-provide-a-declarative-api-you-probably-should/ Should I provide a declarative API?
https://www.educative.io/blog/declarative-vs-imperative-programming Declarative vs imperative programming: 5 key differences
https://learn.lianglianglee.com/专栏/深入剖析Kubernetes 深入剖析Kubernetes
http://docs.kubernetes.org.cn/144.html Kubernetes 使用准入控制器(Admission Controllers)
https://kubernetes.io/zh-cn/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/ 使用 CustomResourceDefinition 扩展 Kubernetes API
https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#customresourcedefinition-v1-apiextensions-k8s-io CustomResourceDefinition v1 apiextensions.k8s.io
https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definition-versioning/ Versions in CustomResourceDefinitions
https://hliangzhao.cn/post/2021/12/23/k8s-4/ 自定义资源对象与控制器的实现
https://zhuanlan.zhihu.com/p/141877334 如何在Kubernetes中创建一个自定义Controller?
https://blog.csdn.net/KevinBetterQ/article/details/104012293 Kubernetes 核心理解之声明式API和编程范式
https://kubernetes.io/zh-cn/docs/concepts/extend-kubernetes/operator/ Operator 模式
https://www.redhat.com/zh/topics/containers/what-is-a-kubernetes-operator 什么是 Kubernetes Operator?
https://jimmysong.io/kubernetes-handbook/develop/operator.html Operator
https://zhuanlan.zhihu.com/p/339966406 深入理解Kubernetes Operator
https://zhuanlan.zhihu.com/p/515524518 面向k8s编程,如何写一个Operator

  • 25
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值