k8s-client-go 源码剖析 (二)
简介:云原生社区活动---Kubernetes源码剖析第一期第二周
本周是K8S源码研习社第一期第二周,学习内容是学习Informer机制,本文以这个课题进行展开。
本周研习社社长挺忙的,将本次课程推迟到下一周结束,任何事情都是这样,计划总有可能会被其他事情打破,但最终只要能够回归到对应的主线上,就不是什么问题。就像参与开源一样,最开始的开放源代码只是开始,需要的是能够坚持下去,而这一点往往是很重要的。
好了,开始正文。
本文主题:
Informer机制架构设计总览
Reflector理解
DeltaFIFO理解
Indexer理解
如果涉及到资源的内容,本文以Deployment资源进行相关内容讲述。
Informer机制架构设计总览
下面是我根据理解画的一个数据流转图,从全局视角看一下数据的整体走向是怎么样的。
其中虚线的表示的是代码中的方法。
首先讲一个结论:
通过Informer机制获取数据的情况下,在初始化的时候会从Kubernetes API Server获取对应Resource的全部Object,后续只会通过Watch机制接收API Server推送过来的数据,不会再主动从API Server拉取数据,直接使用本地缓存中的数据以减少API Server的压力。
Watch机制基于HTTP的Chunk实现,维护一个长连接,这是一个优化点,减少请求的数据量。第二个优化点是SharedInformer,它可以让同一种资源使用的是同一个Informer,例如v1版本的Deployment和v1beta1版本的Deployment同时存在的时候,共享一个Informer。
上面图中可以看到Informer分为三个部分,可以理解为三大逻辑。
其中Reflector主要是把从API Server数据获取到的数据放到DeltaFIFO队列中,充当生产者角色。
SharedInformer主要是从DeltaFIFIO队列中获取数据并分发数据,充当消费者角色。
最后Indexer是作为本地缓存的存储组件存在。
Reflector理解
Reflector中主要看Run、ListAndWatch、watchHandler三个地方就足够了。
源码位置是 tools/cache/reflector.go
数据的生产就结束了,就两点:
初始化时从API Server请求数据
监听后续从Watch推送来的数据
DeltaFIFO理解
先看一下数据结构:
其中queue存储的是Object的id,而items存储的是以ObjectID为key的这个Object的事件列表,
可以想象到是这样的一个数据结构,左边是Key,右边是一个数组对象,其中每个元素都是由type和obj组成.
DeltaFIFO顾名思义存放Delta数据的先入先出队列,相当于一个数据的中转站,将数据从一个地方转移另一个地方。
主要看的内容是queueActionLocked、Pop、Resync
queueActionLocked方法:
Pop方法:
Resync机制:
小总结:每次从本地缓存Indexer中获取数据重新放到DeltaFIFO中执行任务逻辑。
启动的Resync地方是reflector.go的resyncChan()方法,在reflector.go的ListAndWatch方法中的调用开始定时执行。
SharedInformer消费消息理解
主要看HandleDeltas方法就好,消费消息然后分发数据并且存储数据到缓存的地方
Indexer理解
这块不会讲述太多内容,因为我认为Informer机制最主要的还是前面数据的流转,当然这并不代表数据存储不重要,而是先理清楚整体的思路,后续再详细更新存储的部分。
Indexer使用的是threadsafe_store.go中的threadSafeMap存储数据,是一个线程安全并且带有索引功能的map,数据只会存放在内存中,每次涉及操作都会进行加锁。
Indexer还有一个索引相关的内容就暂时不展开讲述。
Example代码
以上示例代码中程序启动后会拉取一次Deployment数据,并且拉取数据完成后从本地缓存中List一次default命名空间的Deployment资源并打印,然后每60秒Resync一次Deployment资源。
QA
为什么需要Resync?
在本周有同学提出一个,我看到这个问题后也感觉挺奇怪的,因为Resync是从本地缓存的数据缓存到本地缓存(从开始到结束来说是这样),为什么需要把数据拿出来又走一遍流程呢?当时钻牛角尖也是想不明白,后来换个角度想就知道了。
数据从API Server过来并且经过处理后放到缓存中,但数据并不一定就可以正常处理,也就是说可能报错了,而这个Resync相当于一个重试的机制。
可以尝试实践一下: 部署有状态服务,存储使用LocalPV(也可以换成自己熟悉的),这时候pod会由于存储目录不存在而启动失败. 然后在pod启动失败后再创建好对应的目录,过一会pod就启动成功了。 这是我理解的一种情况。
总结:
Informer机制在K8S中是各个组件通讯的基石,理解透彻是非常有益的,我也还在进一步理解的过程中,欢迎一起交流。
前置阅读:
版权声明: 本文为 InfoQ 作者【LanLiang】的原创文章。
原文链接:【http://xie.infoq.cn/article/04453b78affa84c0544602bfc】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论 (2 条评论)