Kubernetes CRD 控制器开发系列 Part 2: CRD controller 实现原理
文章主要讲解了关于 CRD controller 实现原理以及 client-go
源码实现过程。
我们自定义的资源 CRD 创建完成以后,其实如果没有一个 controller 运行它的话,它并没有起到任何用处。于是,我们需要通过创建 controller 来运行 CRD。
要想了解 controller 的实现原理和方式,我们就需要了解下 client-go
这个库的实现,Kubernetes 部分代码也是基于这个库实现的,也包含了开发自定义控制器时可以使用的各种机制。
下图显示了 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 会回调这些 handlersWork queue
: Resource Event Handlers 被回调后将 key 写到工作队列,这里的 key 相当于事件通知,后面根据取出事件后,做后续的处理Process Item
:从工作队列中取出 key 后进行后续处理,具体处理可以通过 Indexer reference controller 可以直接创建上述两个引用对象去处理,也可以采用工厂模式,官方都有相关示例
自定义 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
里面实现。
本文摘录于 Kubernetes 文档中的 CRD controller 原理实现。