K8S控制器原理可以用下面这张图来表示。在这里,一个对象会被分配一个如下图这样的控制器,
在informer中,Reflector通过APISever从etcd中获取监视的对象。而监视,用的是ListAndWatch的方式。在ListAndWatch下,一旦APISever下有新API被创建或者删除或者修改,则这个对象和相应事件的组合会被放到一个Delta FIFO Queue(即:增量先进先出队列)中。Indexer的库会读出放入Delta FIFO Queue的增量,根据增量事件对本地更新本地缓存。这里的缓存就是Store。
这个同步本地缓存的工作,是Informer的第一个职责,也是它最重要的职责。而Informer的第二个职责,则是根据这些事件的类型,触发事先注册好的ResourceEventHandler。具体的,注册更新,删除等等业务逻辑的函数。具体的操作,是将事件API对象的Key放入workQueue中。
后面则是控制循环的部分。控制循环
首先,等待Informer完成一次本地缓存的数据同步操作;
然后,直接通过goroutine启动一个(或者并发启动多个)“无限循环”的任务。
在这个循环里,首先从workQueue中取出一个key,然后用这个key获取具体的API对象。获取后就可以执行期望状态和实际状态的对比。这里APIServer里保存的就是“期望状态”,即:用户通过YAML文件提交到APIServer里的信息。当然,在我们的例子里,它已经被Informer缓存在了本地。
“实际状态“就是实际集群的状态了。这样,我们就通过对比“期望状态”和“实际状态”的差异,完成了一次调协(Reconcile)的过程。
另外用key取对象时,会有取不到的情况,那么表示这个对象期望删除,于是控制循环从实际集群中删除这个对象。
总结
所谓Informer,其实就是一个带有本地缓存和索引机制的、可以注册EventHandler的client。它是自定义控制器跟APIServer进行数据同步的重要组件。这个本地缓存在Kubernetes中一般被称为Store,索引一般被称为Index。Informer使用了Reflector包,它是一个可以通过ListAndWatch机制获取并监视API对象变化的客户端封装。Reflector和Informer之间,用到了一个“增量先进先出队列”进行协同。而Informer与你要编写的控制循环之间,则使用了一个工作队列来进行协同。
更具体地说,Informer通过一种叫作ListAndWatch的方法,把APIServer中的API对象缓存在了本地,并负责更新和维护这个缓存。
在实际应用中,除了控制循环之外的所有代码,实际上都是Kubernetes为你自动生成的,即:pkg/client/{informers, listers, clientset}里的内容。
而这些自动生成的代码,就为我们提供了一个可靠而高效地获取API对象“期望状态”的编程库。
所以,接下来,作为开发者,你就只需要关注如何拿到“实际状态”,然后如何拿它去跟“期望状态”做对比,从而决定接下来要做的业务逻辑即可。