Table of Contents
1. informer机制简介
在Kubernetes系统中,组件之间通过HTTP协议进行通信,在不依赖任何中间件的情况下需要保证消息的实时性、可靠性、顺序性是通过list-watch机制实现的。
作为客户端,client-go也实现了一套对应的list-watch进行用来处理对象的变化。这个机制在client-go就是informer机制。
Kubernetes的其他组件(kcm, kubelet等等)都是通过client-go的Informer机制与Kubernetes API Server进行通信的。
Informer机制运行原理如图:
大体流程如下:
(1)new一个informer,然后informer的时候指定了 listAndwatcher(这个就是获取apiserver数据)
(2)informer.Run的时候,会new 一个 Reflector对象。Reflector包含了listAndwatcher,接下来基本就是Reflector进行操作了
(3)Reflector对listWatcher来的数据进行处理,这里使用到了DeltaFIFO队列对watch来的数据一个个的处理,HandleDeltas函数
(4)具体的处理逻辑分为两部分,第一部分是,通过操作cache.indexer,更新本地缓存+索引; 第二部分是,将watch的数据发送给 Informer自定义的处理函数进行处理
本节就先总结一下informer机制的大概流程,然后简单介绍一个流程中出现的几个概念。后面的章节一个一个进行详细研究
1.2. informer机制 example介绍
直接阅读Informer机制代码会比较晦涩,通过Informers Example代码示例来理解Informer,印象会更深刻。Informers Example代码示例如下:
package main import ( "log" "time" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/clientcmd" ) func main() { config, err := clientcmd.BuildConfigFromFlags("", "/root/.kube/config") if err!= nil { pannic(er) } clientset, err := kubernetes.NewForConfig(config) if err!=nil { panic(err) } stopCh := make(chan struct{}) defer close(stopCh) sharedInformers := informers.NewSharedInformerFactory(clientset, time.Minute) informer := sharedInformers.Core().V1().Pods().Informer() informers.AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { mObj := obj.(v1.Object) log.Printf("New Pod Added to Store:%s", mObj.GetName()) }, UpdateFunc: func(oldObj, newObj interface{}) { oObj := oldObj.(v1.Object) nObj := newObj.(v1.Object) log.Printf("%s Pod Updated to %s", oObj.GetName(), nObj.GetName()) }, DeleteFunc: func(obj interface{}) { mObj := obj.(v1.Object) log.Printf("Pod Deleted from Store:%s", mObj.GetName()) }, }) informer.Run(stopCh) }
首先通过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种资源事件回调方法,分别介绍如下。
● AddFunc:当创建Pod资源对象时触发的事件回调方法。
● UpdateFunc:当更新Pod资源对象时触发的事件回调方法。
● DeleteFunc:当删除Pod资源对象时触发的事件回调方法。在正常的情况下,Kubernetes的其他组件在使用Informer机制时触发资源事件回调方法,将资源对象推送到WorkQueue或其他队列中(实际过程中大都是这样的),在InformersExample代码示例中,我们直接输出触发的资源事件。最后通过informer.Run函数运行当前的Informer,内部为Pod资源类型创建Informer。
2. informer
每一个Kubernetes资源上都实现了Informer机制。每一个Informer上都会实现Informer和Lister方法,例如PodInformer,代码示例如下
// PodInformer provides access to a shared informer and lister for // Pods. type PodInformer interface { Informer() cache.SharedIndexInformer Lister() v1.PodLister }
用不同资源的Informer,代码示例如下:
podInformer := sharedInformers.Core().V1().Pods().Informer() nodeInformer := sharedInformers.Node().V1beta1().RuntimeClasses().Informer
定义不同资源的Informer,允许监控不同资源的资源事件,例如,监听Node资源对象,当Kubernetes集群中有新的节点(Node)加入时,client-go能够及时收到资源对象的变更信息。
2.1 shared informer
可以认为 informer都是 shared informer
Informer也被称为Shared Informer,它是可以共享使用的。在用client-go编写代码程序时,若同一资源的Informer被实例化了多次,每个Informer使用一个Reflector,那么会运行过多相同的ListAndWatch,太多重复的序列化和反序列化操作会导致Kubernetes API Server负载过重。Shared Informer可以使同一类资源Informer共享一个Reflector,这样可以节约很多资源。通过map数据结构实现共享的Informer机制。SharedInformer定义了一个map数据结构,用于存放所有Informer的字段,代码示例如下:
type sharedInformerFactory struct { client kubernetes.Interface namespace string tweakListOptions internalinterfaces.TweakListOptionsFunc lock sync.Mutex defaultResync time.Duration customResync map[reflect.Type]time.Duration informers map[reflect.Type]cache.SharedIndexInformer // startedInformers is used for tracking which informers have been started. // This allows Start() to be called multiple times safely. startedInformers map[reflect.Type]bool }
informers字段中存储了资源类型和对应于SharedIndexInformer的映射关系。InformerFor函数添加了不同资源的Informer,在添加过程中如果已经存在同类型的资源Informer,则返回当前Informer,不再继续添加。最后通过Shared Informer的Start方法使f.informers中的每个informer通过goroutine持久运行。
同一个factory定义的shareInformer可以复用复用。
2.2 shared informer是如何实现的
从结构体可以看出来:有一个字段 Store,这里就是保存从apiserver同步过来的数据。
还有一个函数Run(),这个函数会调用controller.Run --> Reflector.Run->ListAndWatch()
而ListAndWatch()就是从apiserver获取数据。
// SharedInformer has a shared data cache and is capable of distributing notifications for changes // to the cache to multiple listeners who registered via AddEventHandler. If you use this, there is // one behavior change compared to a standard Informer. When you receive a notification, the cache // will be AT LEAST as fresh as the notification, but it MAY be more fresh. You should NOT depend // on the contents of the cache exactly matching the notification you've received in handler // functions. If there was a create, followed by a delete, the cache may NOT have your item. This // has advantages over the broadcaste