解读Kubenetes Controller Manager工作原理
解读 kubernetes Controller Manager 工作原理 :: Yingchi Blog
https://blog.csdn.net/qq_61039408/article/details/130432825
Kubernetes master节点最重要的三个组件是:kube-apiserver、kube-controller-manager、kube-scheduler,分别负责Kubernetes资源访问入口、集群状态管理、资源调度。
这篇文章的主角就是其中的kube-controller-manager组件,分析一下它以及核心组件informer是如何有效管理集群的
Controller Manager&Controller
我们都知道 kubernetes 中管理资源的方式比较简单,通常就是写一个 YAML 清单,简单的可以通过 kubectl 命令直接解决,在这个过程中,我们定义了某个资源的「期望状态」,比如 YAML 清单文件中的 spec 字段,Deployment 的 YAML 中的 spec 字段可能定义了期望的 replicas,我们期望集群的某个 pod 的副本数维持在某个数量上,当我们提交清单给集群,kubernetes 会在一段时间内将集群中的某些资源调整至我们期望的状态;亦或是另一个场景,集群中某个 Pod 挂掉了,或者我们将 Pod 从某个 Worker Node 上驱逐了,然后我们没有做任何操作,Pod 又会自动重建,并且达到指定的副本数,这是很常见的场景。
上面说的这些资源的状态管理是由谁实现的呢?没错,就是Controller Manager,Controller Manager是Kubernetes的灵魂组件之一,可以说通过定义资源期望状态实现急群资源编排管理的思想其底层就是依赖Controller manager这个组件。
Controller Manager的作用简而言之:保证急群中各种资源的实际状态(status)和用户定义的期望状态(spec)一致。
按照官方定义kube-controller-manager运行控制器,它们是处理集群常规任务的后台线程。
Controller Manager 就是集群内部的管理控制中心,刚才说 Controller Manager 的作用是保证集群中各种资源的实际状态和用户定义的期望状态一致,但是如果出现不一致的情况怎么办?是由 Controller Manager 自己来对各种资源进行调整吗?
这时候就要说到Controller的概念了,之所以叫Controller Manager,是因为Controller Manager由负责不同资源的多个Controller构成,如 Deployment Controller、Node Controller、Namespace Controller、Service Controller 等,这些 Controllers 各自明确分工负责集群内资源的管理。
如图,Controller Manager发现资源的实际状态和期望有偏差之后,会触发相应 Controller 注册的 Event Handler,让它们去根据资源本身的特点进行调整。
比如当通过Deployment创建的某个Pod发生异常退出时,Deployment Controller变回接受并处理该退出的Event,并创建新的Pod来维持期望副本数。这样设计的原因也很好理解,可以将Controller Manager与具体的状态管理工作相解耦,因为不同的资源对于状态的管理多种多样,Deployment Controller关注Pod副本数,而Service则关注Service的IP、Port等。
Client-go
Controller Manager中一个很关键的部分就是client-go,client-go在Controller manager起到向controllers 进行事件分发的作用。目前client-go已经被单独抽取出来成为一个项目了,除了在Kubernetes中经常被用到,在Kubernetes二次开发过程中也会经常用到client-go,比如可以通过client-go开发自定义Controller。
client-go包中非常核心的工具就是informer,informer可以让与kube-apiserver的交互更加优雅。
informer主要功能可以概括为俩点:
- 资源数据缓存功能,缓解对kube-apiserver的访问压力
- 资源事件分发,触发实现注册号的ResourceEventHandler
informer另一块内容在于提供了事件的handler机制,并会触发回调,这样Controller就可以基于回调处理具体业务逻辑。因为Informer通过List、Watch机制可以监控到所有资源的所有事件,因此只要给Informer添加ResourceEventHandler实例的回调函数实现OnAdd(obj interface{})
、 OnUpdate(oldObj, newObj interface{})
和 OnDelete(obj interface{})
这三个方法,就可以处理好资源的创建、更新和删除操作
Client-go工作机制
上图是官方给出的client-go与自定义Controller的实现原理。
Reflector
反射器,具有以下几个功能:
- 采用List、Watch机制与kube-apiserver交互,List短连接获取全量数据,Watch长连接获取增量数据
- 可以Watch任何资源包括CRD
- Watch的增量Object添加到Delta FIFO队列,然后informer会从队列里取数据
Informer
Informer 是 client-go 中较为核心的一个模块,其主要作用包括如下两个方面:
- 同步数据到本地缓存。Informer 会不断读取 Delta FIFO 队列中的 Object,在触发事件回调之前先更新本地的 store,如果是新增 Object,如果事件类型是 Added(添加对象),那么 Informer 会通过 Indexer 的库把这个增量里的 API 对象保存到本地的缓存中,并为它创建索引。之后通过 Lister 对资源进行 List / Get 操作时会直接读取本地的 store 缓存,通过这种方式避免对 kube-apiserver 的大量不必要请求,缓解其访问压力;
- 根据对应的事件类型,触发事先注册好的 ResourceEventHandler。client-go 的 informer 模块启动时会创建一个 shardProcessor,各种 controller(如 Deployment Controller、自定义 Controller…)的事件 handler 注册到 informer 的时候会转换为一个 processorListener 实例,然后 processorListener 会被 append 到 shardProcessor 的 Listeners 切片中,shardProcessor 会管理这些 listeners。
processorListener 的重要作用就是当事件到来时触发对应的处理方法,因此不停地从 nextCh 中拿到事件并执行对应的 handler。sharedProcessor
的职责便是管理所有的 Handler 以及分发事件,而真正做分发工作的是 distribute
方法。
梳理一下这中间的过程:
- Controller 将 Handler 注册给 Informer;
- Informer 通过
sharedProcessor
维护了所有转换为 processorListener 的 Handler; - Informer 收到事件时,通过
sharedProcessor.distribute
将事件分发下去; - Controller 被触发对应的 Handler 来处理自己的逻辑。
Reflactor 启动后会执行一个 processLoop 死循环,Loop 中不停地将 Delta FIFO 队列中的事件 Pop 出来,Pop 时会取出该资源的所有事件,并交给 sharedIndexInformer
的 HandleDeltas
方法(创建 controller
时赋值给了 config.Process
,传递到 Pop 参数的处理函数中 Pop(PopProcessFunc(c.config.Process))
),HandleDeltas
调用了 processor.distribute
完成事件的分发。
在注册的 ResourceEventHandler 回调函数中,只是做了一些很简单的过滤,然后将关心变更的 Object 放到 workqueue 里面。之后 Controller 从 workqueue 里面取出 Object,启动一个 worker 来执行自己的业务逻辑,通常是对比资源的当前运行状态与期望状态,做出相应的处理,实现运行状态向期望状态的收敛。
注意,在 worker 中就可以使用 lister 来获取 resource,这个时候不需要频繁的访问 kube-apiserver 了,对于资源的 List / Get 会直接访问 informer 本地 store 缓存,apiserver 中资源的的变更都会反映到这个缓存之中。同时,LocalStore 会周期性地把所有的 Pod 信息重新放到 DeltaFIFO 中。