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 原理实现。