在Kubernetes系统中,组件之间通过HTTP协议进行通信,在不依赖任何中间件的情况下需要保证消息的实时性、可靠性、顺序性等。那么Kubernetes是如何做到的呢?答案就是Informer机制。Kubernetes的其他组件都是通过client-go的Informer机制与Kubernetes API Server进行通信的。
在Informer架构设计中,有多个核心组件,分别介绍如下。
Reflector用于监控(Watch)指定的Kubernetes资源,当监控的资源发生变化时,触发相应的变更事件,例如Added(资源添加)事件、Updated(资源更新)事件、Deleted(资源删除)事件,并将其资源对象存放到本地缓存DeltaFIFO中。
DeltaFIFO可以分开理解,FIFO是一个先进先出的队列,它拥有队列操作的基本方法,例如Add、Update、Delete、List、Pop、Close等,而Delta是一个资源对象存储,它可以保存资源对象的操作类型,例如Added(添加)操作类型、Updated(更新)操作类型、Deleted(删除)操作类型、Sync(同步)操作类型等。
Indexer是client-go用来存储资源对象并自带索引功能的本地存储,Reflector从DeltaFIFO中将消费出来的资源对象存储至Indexer。Indexer与Etcd集群中的数据完全保持一致。client-go可以很方便地从本地存储中读取相应的资源对象数据,而无须每次从远程Etcd集群中读取,以减轻Kubernetes API Server和Etcd集群的压力。
直接阅读Informer机制代码会比较晦涩,通过Informers Example代码示例来理解Informer,印象会更深刻。Informers Example代码示例如下:
首先通过kubernetes.NewForConfig创建clientset对象,Informer需要通过ClientSet与Kubernetes API Server进行交互。另外,创建stopCh对象,该对象用于在程序进程退出之前通知Informer提前退出,因为Informer是一个持久运行的goroutine。
informers.NewSharedInformerFactory函数实例化了SharedInformer对象,它接收两个参数:第1个参数clientset是用于与Kubernetes API Server交互的客户端,第2个参数time.Minute用于设置多久进行一次resync(重新同步),resync会周期性地执行List操作,将所有的资源存放在Informer Store中,如果该参数为0,则禁用resync功能。
在Informers Example代码示例中,通过sharedInformers.Core().V1().Pods().Informer可以得到具体Pod资源的informer对象。通过informer.AddEventHandler函数可以为Pod资源添加资源事件回调方法,支持3种资源事件回调方法,分别介绍如下。
在正常的情况下,Kubernetes的其他组件在使用Informer机制时触发资源事件回调方法,将资源对象推送到WorkQueue或其他队列中,在Informers Example代码示例中,我们直接输出触发的资源事件。最后通过informer.Run函数运行当前的Informer,内部为Pod资源类型创建Informer。
通过Informer机制可以很容易地监控我们所关心的资源事件,例如,当监控Kubernetes Pod资源时,如果Pod资源发生了Added(资源添加)事件、Updated(资源更新)事件、Deleted(资源删除)事件,就通知client-go,告知Kubernetes资源事件变更了并且需要进行相应的处理。
每一个Kubernetes资源上都实现了Informer机制。每一个Informer上都会实现Informer和Lister方法,例如PodInformer,代码示例如下:
vendor/k8s.io/client-go/informers/core/v1/pod.go
调用不同资源的Informer,代码示例如下:
informer := shardInformer.Core().V1().Pods().Informer()
nodeinformer := shardInformer.Node().V1beta1().RuntimeClasses().Informer()
定义不同资源的Informer,允许监控不同资源的资源事件,例如,监听Node资源对象,当Kubernetes集群中有新的节点(Node)加入时,client-go能够及时收到资源对象的变更信息。
Informer也被称为Shared Informer,它是可以共享使用的。在用client-go编写代码程序时,若同一资源的Informer被实例化了多次,每个Informer使用一个Reflector,那么会运行过多相同的ListAndWatch,太多重复的序列化和反序列化操作会导致Kubernetes API Server负载过重。
Shared Informer可以使同一类资源Informer共享一个Reflector,这样可以节约很多资源。通过map数据结构实现共享的Informer机制。Shared Informer定义了一个map数据结构,用于存放所有Informer的字段,代码示例如下:
vendor/k8s.io/client-go/informers/factory.go
informers字段中存储了资源类型和对应于SharedIndexInformer的映射关系。InformerFor函数添加了不同资源的Informer,在添加过程中如果已经存在同类型的资源Informer,则返回当前Informer,不再继续添加。
最后通过Shared Informer的Start方法使f.informers中的每个informer通过goroutine持久运行。
以上就是针对k8s informer机制架构的讲解。
后续将会继续详细介绍Reflector、DeltaFIFO、Indexer。关注我,带你学习更多的K8S知识。
任何不会的问题都可以关注「程序员溪昂」,后台私信求解或者下方留言哦