Kubernetes CRD 控制器开发系列 Part 2: CRD controller 实现原理

文章主要讲解了关于 CRD controller 实现原理以及 client-go 源码实现过程。


我们自定义的资源 CRD 创建完成以后,其实如果没有一个 controller 运行它的话,它并没有起到任何用处。于是,我们需要通过创建 controller 来运行 CRD。 要想了解 controller 的实现原理和方式,我们就需要了解下 client-go 这个库的实现,Kubernetes 部分代码也是基于这个库实现的,也包含了开发自定义控制器时可以使用的各种机制。 下图显示了 client-go 中的各个组件是如何公众的以及与我们要编写的自定义控制器代码的交互入口:

Figure 1.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 可以直接创建上述两个引用对象去处理,也可以采用工厂模式,官方都有相关示例

自定义 Controller 的控制流:

Figure 2.controller 的控制流

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

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

本文摘录于 Kubernetes 文档中的 CRD controller 原理实现

翻译: