在茫茫代码中,很容易迷失。经过一段时间的艰苦探索,终于对client-go有了一定的了解,能够从整体上把相关功能串起来。本文主要是informer相关的内容,因为这是client-go比较核心的功能。后面还会发一篇关于client-go编程使用的文章,会从kubernetes.Interface,dynamic,discovery,workequeue和informer工厂函数等方面介绍,再结合一个operator作为实例进行分析(虽然大部分operator都使用kube-builder等类似的工具构建,但是本着求根问底的精神还是要分析一下)。
相关存储结构
在开始介绍之前,先介绍一下client-go中提供存储相关的接口,这些接口用来缓存api对象。Store是最基础的接口,提供增删改查等,还有一些继承于Store的接口,比Store可能会多实现一些方法,可以针对不同的场景进行使用。
Store
ExpirationStore
UndeltaStore
Queue
FIFO
DeltaFIFO
Indexer
cache
不同Store的引用
Store接口的具体定义可以看vendor/k8s.io/client-go/tools/cache/store.go这个文件。在IDE的帮助下,找到了所有实现了这个Store的相关实现。本文主要关心client-go,所以主要跟进client-go下面的相关实现。
delta_fifo.go
NewDeltaFIFO()
NewDeltaFIFOWithOptions() informer和sharedinformer使用到了
vendor/k8s.io/client-go/tools/cache/controller.go:NewInformer()
vendor/k8s.io/client-go/tools/cache/shared_informer.go:sharedIndexInformer.Run()
expiration_cache.go
NewTTLStore()
pkg/kubelet/util/cache/object_cache.go:NewObjectCache()
NewExpirationStore()
pkg/credentialprovider/aws/aws_credentials.go:newECRProvider()
pkg/credentialprovider/azure/azure_credentials.go:NewACRProvider()
fifo.go 该文件中定义了Queue接口,继承了Store,并且在该文件中通过FIFO实现了这个queue,另外一个实现是在delta_fifo.go中的DeltaFIFO
NewFIFO() 代码中未找到该引用
index.go 这个文件定义了Indexer接口,继承了Store,在store.go中通过cache实现了该接口
store.go
NewStore()
staging/src/k8s.io/client-go/tools/cache/controller.go:NewInformer()
staging/src/k8s.io/client-go/tools/cache/undelta_store.go:NewUndeltaStore()
NewIndexer()
staging/src/k8s.io/apiserver/pkg/storage/cacher/watch_cache.go:newWatchCache()
staging/src/k8s.io/client-go/tools/cache/controller.go:NewIndexerInformer()
staging/src/k8s.io/client-go/tools/cache/reflector.go:NewNamespaceKeyedIndexerAndReflector() 该函数在IDE中显示未被使用
staging/src/k8s.io/client-go/tools/cache/shared_informer.go:NewSharedIndexInformer()
undelata_store.go
NewUndeltaStore()
pkg/kubelet/config/apiserver.go:newSourceApiserverFromLW()
informer简要介绍
client-go中提供了普通informer和sharedInformer两种informer给我们使用。使用informer可以快速的构建各种资源的控制器,来对k8s进行扩展。informer提供了资源变化时执行回调的功能,可以在新增资源,修改资源和是删除资源时执行相应的控制器逻辑。
使用sharedInformer可以对同一个资源注册多个控制器,每个控制器独立执行自己的业务逻辑,互不干扰。多个控制器之间底层公用一个存储和一个到apiserver的连接,可以减少apiserver的压力的和本地内存的占用。informer则不具备这些能力,对于简单控制器可以使用informer,对于较复杂的控制器,会有针对同一个资源多个控制逻辑的推荐使用sharedInformer。
这两个informer在实现的复杂度上有很大差异,sharedInformer相对于普通informer来说要复杂的多。因此本文先从普通informer开始来进行分析,普通informer还分两种,一种是普通的informer,另外一种indexerinfomer。两种informer的区别在于前者使用Store作为对象存储,而后者采用Indexer作为对象存储,通过索引可以加快搜索。
普通informer分析
接下来我们以普通的informer进行分析,关于informer的使用可以参考这里。代码位于k8s.io/client-go/tools/cache/controller.go文件中,该文件中定义了Controller接口,并通过controller进行了实现。
controller分析
先看下接口定义以及controller包含的关键数据结构:
// 这两个函数用来创建informer实例
func NewInformer(lw ListerWatcher, objType runtime.Object, resyncPeriod time.Duration, h ResourceEventHandler,) (Store, Controller) {}
func NewIndexerInformer(lw ListerWatcher, objType…