一、controller-runtime

controller-runtime由kubebuilder和operator-SDK共同完成;

1、WithEventFilter和EventHandler

- 使⽤WithEventFilter配置变更过滤器,是针对reconciler watch的所有资源,统⼀地设置事件监听规则;

- 使⽤EventHandler,能够在reconciler watch特定资源时,对该资源设置单独的事件监听规则。

2、控制器的执行链条

Run ==> go worker ==> for processNextWorkItem ==> syncHandler。Run 方法作为 Controller 逻辑的统一入口,启动指定数量个协程,协程的逻辑为:wait.Until(dc.worker, time.Second, stopCh) ,control loop 具体为Controller 的worker 方法,for 循环具体为 for processNextWorkItem(){}

3、ControllerManager的NewCache与NewClient

ControllerManager 正如其名,它会负责管理一个或多个控制器,其中一项重要工作就是初始化一个 K8s Client,在创建 CtrMgr 的方法参数中,ctrl.Options 包含了 2 个重要配置项:新建 Cache 与 新建 Client。

NewCache 接口定义了 2 个功能,一是维护一组 Informer,每个 Informer 持续监听(ListAndWatch)某一类(GVK) K8s 对象的资源事件,并按照特定的索引计算方式,将对象数据存储在本地缓存中;二是提供 Get/List 方法以读取 Informers 缓存的 K8s 对象。

NewClient 默认是将使用 DelegatingClient 作为默认的 Client 实现,DelegatingClient中Cache 被用作了 DelegatingClient 中 Reader 的一部分 CacheReader,与之并列的还有一个不使用缓存、直连 api-server 的 ClientReader。Cache Reader 专用在 typed object 的 Get/List 操作上,而 untyped object(unstructured) 的 Get/List 、所有 Object 的写(Create/Update/Delete ,etc.)操作全部使用直连 api-server 的 Client。

因此ControllerManager 使用默认方式创建的 Client 只会对 typed 对象使用缓存, untyped 对象不使用缓存机制。

controller-manager中被缓存的数据并不是 Client 初始化时就预加载好的,而是直到 Client 首次请求该资源时才会去创建 Informer 进而加载缓存数据,这时候会创建创建并启动一个新的 SharedIndexInformer,并等待informer hasSynced。假如 HasSynced 没有完成,那么同步到 DeltaFIFO 中的某类资源,相对比 api-server/etcd 中存储的对象一定是不完整的或者说不一致的,所以这里必须阻塞等待 HasSynced。

(1)只缓存(List&Watch)部分资源

在 controller-runtime v0.9.0+ 版本,为了缓解缓存导致内存过高的问题,通过配置 LabelSelector 或 FieldSelector,将 Client 设置为只缓存某个类型资源的一部分对象。

mgr, err := ctrlmanager.New(ctrl.GetConfigOrDie(), ctrl.Options{ NewCache: cache.BuilderWithOptions(cache.Options{ Scheme: scheme, SelectorsByObject: cache.SelectorsByObject{ &appsv1.ReplicaSet{}: { Label: labels.SelectorFromSet(labels.Set{"foo":"bar"}), }, &appsv1.ReplicaSet{}: { Field: fields.SelectorFromSet(fields.Set{"metadata.name": "foo"}), }, }, }), })

(2)只缓存(List&Watch)资源的 metadata

controller-runtime 在 v0.7.0+ 版本中也加入了对该功能的封装支持。在这种场景下,控制器只关心资源的 metadata,并不关心 spec 和 status。

关于 metadata-only watch 的使用请参考:

实践样例:Four ways to use Controller Runtime - SoByte

实现 MR::sparkles: metadata-only watches by DirectXMan12 · Pull Request #1174 · kubernetes-sigs/controller-runtime · GitHub

二、kubebuilder

kubebuiler简单使用

cd /mnt/d/GoLand/workspace/src/proj_test/client-go/practice06-kubebuilder/

go mod init github.com/practice06-kubebuilder

kubebuilder init --domain baiding.tech

kubebuilder create api --group ingress --version v1beta1 --kind App

kubebuilder create webhook --group ingress --version v1beta1 --kind App --defaulting --programatic-validation

kubebuilder简介

kubebuilder的manager,用于创建和管理controllers(controllerManager是manager的具体实现)
kubebuilder流程
(1)创建controller并把controller作为Runnable add到manager中,在manager.start时一块启动controller,并且每个controller循环的从workQueue中取元素进行reconcile处理。    

    Complete(r)
    if err := blder.doController(r); err != nil {   
    blder.ctrl, err = newController(controllerName, blder.mgr, ctrlOptions)
    c, err := NewUnmanaged(name, mgr, options)
    return &controller.Controller{   同文件的start方法
    for i := 0; i < c.MaxConcurrentReconciles; i++ {
    c.reconcileHandler(ctx, obj)
    result, err := c.Reconcile(ctx, req)
    return c.Do.Reconcile(ctx, req)

(2)创建watch,通过informer将事件过滤后放入WorkQueue,从而触发reconcile处理

   Complete(r)
    if err := blder.doWatch(); err != nil {   


(3)可以通过WithEventFilter添加predicates,predicates来决定create、update、delete、generic事件是否需要被处理。
kubebuiler的reconciler的client默认是delegatingClient,读走的是informer来读indexer的缓存,写是直接写到apiserver;apireader是读写直接走apiserver。

(4)webhook参考https://zhuanlan.zhihu.com/p/404764407

kubebuilder封装的整体流程

1、初始化一个manager,并创建cache、client。创建Cache主要是创建InformersMap,Scheme里每个gvk都创建了对应的informer,通过 InformersMap做gvk到informer的映射,每个informer会根据对应的gvk进行List和Watch。创建client主要是读操作使用上面创建的cache,写操作使用客户端直连apiserver。

2、初始化reconciler/controller, 并对Controller 负责的 CRD 及CRD 管理的其他资源进行watch,并且能够注册eventHandler,kubebuilder注册的handler 就是将发生变更的对象的 NamespacedName放入workQueue队列,然后执行 Reconcile逻辑;

3、启动 Manager,分别启动cache、controller。Cache的初始化核心是初始化informerMap,即所有的 Informer;Controller的初始化是启动goroutine不断地查询队列,如果有变更消息则触发到我们自定义的reconcile逻辑。

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐