SharedInformerFactory 接口

SharedInformerFactory 接口

由于 K8S 支持很多种资源类型,有些 Controller 需要能注册和接收多种资源类型的事件并提供本地缓存,如果挨个为每种类型创建 Informer/Lister 则代码显得有点臃肿。

SharedInformerFactory 是 codegen 生成的,可以用来创建各资源类型 SharedInformer 的工厂函数。

SharedInformerFactory 的 InformerFor() 方法,使用传入的资源对象的 NewInformerFunc 类型函数从 K8S ClientSet 创建对象相关的实现 cache.SharedIndexInformer 接口的对象。

实现自定义 Controller 时,一般先后创建 clienset -> InformerFactory -> Infofmer -> Lister

自定向下

// 创建 K8S Client 配置参数
cfg, err := clientcmd.BuildConfigFromFlags(masterURL, kubeconfig)

// 根据配置参数创建一个 K8S Client
kubeClient, err := kubernetes.NewForConfig(cfg)

// 使用 K8S Client 创建一个 SharedInformerFactory
kubeInformerFactory := kubeinformers.NewFilteredSharedInformerFactory(kubeClient, time.Second*30, "default", nil)

// 从 SharedInformerFactory 创建一个 DeploymentInformer
// 注意:该过程会将 DeploymentInformer 注册到 kubeInformerFactory
deployInformer extensionslistersv1beta1.DeploymentInformer := kubeInformerFactory.Extensions().V1beta1().Deployments()

// 运行 kubeInformerFactory 中已注册的所有 Infomer,所以必须在创建 DeploymentInformer 之后才能执行 kubeInformerFactory 的 Start 方法!
kubeInformerFactory.Start(stopCh)

// 再从 DeploymentInformer 创建 DeploymentLister
deployLister extensionslistersv1beta1.DeploymentLister := deployInformer.Lister()

// 从 DeploymentLister 查询 Deployment
deploy, err := c.deployLister.Deployments(aolDeploy.ObjectMeta.Namespace).Get(aolDeployName)

internalinterfaces.SharedInformerFactory 接口

在介绍 SharedInformerFactory 前,先介绍它使用的 internalinterfaces.SharedInformerFactory 接口:

// 来源于 k8s.io/client-go/informers/internalinterfaces/factory_interfaces.go
// 根据传入的 K8S Client 和同步时间,创建一个实现 cache.SharedIndexInformer 接口的对象
type NewInformerFunc func(kubernetes.Interface, time.Duration) cache.SharedIndexInformer

type SharedInformerFactory interface {
	// 开始运行 SharedInformerFactory
	Start(stopCh <-chan struct{})
	// 使用 newFunc 为特定资源对象 obj 创建一个实现 cache.SharedIndexInformer 接口的对象
	InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer
}

type TweakListOptionsFunc func(*v1.ListOptions)

然后看看 SharedInformerFactory 接口的定义:

// 来源于 k8s.io/client-go/informers/factory.go
type SharedInformerFactory interface {
	internalinterfaces.SharedInformerFactory

	// 为特定 Group Version 的 Resource 创建一个实现 GenericInformer 接口的对象
	ForResource(resource schema.GroupVersionResource) (GenericInformer, error)

	// 等待 Cache 都同步完毕
	WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool

	// 以下这些是 K8S 内置的各 API Group
	Admissionregistration() admissionregistration.Interface
	Apps() apps.Interface
	Auditregistration() auditregistration.Interface
	Autoscaling() autoscaling.Interface
	Batch() batch.Interface
	Certificates() certificates.Interface
	Coordination() coordination.Interface
	Core() core.Interface
	Events() events.Interface
	Extensions() extensions.Interface
	Networking() networking.Interface
	Policy() policy.Interface
	Rbac() rbac.Interface
	Scheduling() scheduling.Interface
	Settings() settings.Interface
	Storage() storage.Interface
}

SharedInformerFactory 可以为各 API Group 下所有版本的资源对象创建对应的 SharedInformer,所以称之为 Factory。

函数 NewSharedInformerFactory()NewFilteredSharedInformerFactory()NewSharedInformerFactoryWithOptions() 返回实现 SharedInformerFactory 接口的内置类型 sharedInformerFactory 的实例:

// 来源于 k8s.io/client-go/informers/factory.go
// 创建一个监控所有 Namespace 中特定类型资源对象的 sharedInformerFactory 实例
func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory {
	return NewSharedInformerFactoryWithOptions(client, defaultResync)
}

// 创建一个监控指定 Namespace,用 List 选项过滤结果的 sharedInformerFactory 实例
// 该函数已过时,应该使用 NewSharedInformerFactoryWithOptions 替换它
func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory {
	return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions))
}

// 根据指定选项创建一个 sharedInformerFactory 实例
func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory {
	factory := &sharedInformerFactory{
		client:           client,
		namespace:        v1.NamespaceAll,
		defaultResync:    defaultResync,
		informers:        make(map[reflect.Type]cache.SharedIndexInformer),
		startedInformers: make(map[reflect.Type]bool),
		customResync:     make(map[reflect.Type]time.Duration),
	}
	// 应用配置选项
	for _, opt := range options {
		factory = opt(factory)
	}
	return factory
}

client-go 的 informers package 提供了如下三个预定义的 SharedInformerOption:

  1. WithCustomResyncConfig:为指定资源类型指定 Resync 同步周期;
  2. WithTweakListOptions:使用 internalinterfaces.TweakListOptionsFunc 更新 SharedInformerOption;
  3. WithNamespace:指定只监控指定的 Namespace;

实现 SharedInformerFactory 接口的类型 sharedInformerFactory

sharedInformerFactory 类型定义如下:

// 来源于 k8s.io/client-go/informers/factory.go
type sharedInformerFactory struct {
	// Kubernetes ClientSet,SharedInformer 使用它和 apiserver 通信,ListAndWatch 特定类型资源对象
	client           kubernetes.Interface
	// 监控的 Namespace
	namespace        string
	// List 选项
	tweakListOptions internalinterfaces.TweakListOptionsFunc
	lock             sync.Mutex
	// 缺省的 Rsync 周期
	defaultResync    time.Duration
	// 各资源对象类型对应的 Rsync 周期
	customResync     map[reflect.Type]time.Duration
	// 各资源对象类型对应的 cache.SharedIndexInformer
	informers map[reflect.Type]cache.SharedIndexInformer
	// 记录各资源对象类型的 cache.SharedIndexInformer 是否以启动运行
	startedInformers map[reflect.Type]bool
}

InformerFor() 方法

为特定资源对象类型创建 cache.SharedIndexInformer,并将他们注册到 sharedInformerFactory:

// 来源于 k8s.io/client-go/informers/factory.go
// InternalInformerFor returns the SharedIndexInformer for obj using an internal
// client.
func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer {
	f.lock.Lock()
	defer f.lock.Unlock()

	informerType := reflect.TypeOf(obj)
	informer, exists := f.informers[informerType]
	if exists {
		return informer
	}

	resyncPeriod, exists := f.customResync[informerType]
	if !exists {
		resyncPeriod = f.defaultResync
	}

	informer = newFunc(f.client, resyncPeriod)
	f.informers[informerType] = informer

	return informer
}

后文会以 Deployment 为例介绍,各内置 K8S 资源对象是如何注册到 sharedInformerFactory 的。

Start() 方法

运行所有已注册的资源对象类型的 cache.SharedIndexInformer:

// 来源于 k8s.io/client-go/informers/factory.go
// Start initializes all requested informers.
func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) {
	f.lock.Lock()
	defer f.lock.Unlock()

	for informerType, informer := range f.informers {
		// 如果对应类型的 informer 没有运行,则运行它
		if !f.startedInformers[informerType] {
			go informer.Run(stopCh)
			f.startedInformers[informerType] = true
		}
	}
}

由于有锁保护,同时保存的有运行状态指示,所以可以并发、多次调用 Start() 方法,从而确保新注册的 Informer 能被运行。

ForResource() 方法

返回实现 GenericInformer 接口的对象,该接口的定义如下:

// 来源于 8s.io/client-go/informers/generic.go
func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) {
	switch resource {
	...
	// Group=extensions, Version=v1beta1
	case extensionsv1beta1.SchemeGroupVersion.WithResource("deployments"):
		return &genericInformer{resource: resource.GroupResource(), informer: f.Extensions().V1beta1().Deployments().Informer()}, nil
	...
	}
}

WaitForCacheSync() 方法

等待所有已经启动的 Informer 的 Cache 同步完成:

// 来源于 k8s.io/client-go/informers/factory.go
func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool {
	// 获取已经启动的 Informer 集合
	informers := func() map[reflect.Type]cache.SharedIndexInformer {
		f.lock.Lock()
		defer f.lock.Unlock()

		informers := map[reflect.Type]cache.SharedIndexInformer{}
		for informerType, informer := range f.informers {
			if f.startedInformers[informerType] {
				informers[informerType] = informer
			}
		}
		return informers
	}()
	// 等待他们的 Cache 都同步完成
	res := map[reflect.Type]bool{}
	for informType, informer := range informers {
		res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced)
	}
	return res
}

TODO: 以 DeltaFIFO 为例,说明 WaitForCacheSync() 方法的意义。

使用 InformerFactory 创建特定资源类型的 SharedIndexInformer 过程分析

以 Extensions api group 下的 Deployment 为例。

// 从 SharedInformerFactory 创建一个 DeploymentInformer
deployInformer extensionslistersv1beta1.DeploymentInformer := kubeInformerFactory.Extensions().V1beta1().Deployments()

sharedInformerFactory 的 Extensions() 方法定义如下:

// 来源于 k8s.io/client-go/informers/factory.go
...
func (f *sharedInformerFactory) Core() core.Interface {
	return core.New(f, f.namespace, f.tweakListOptions)
}
...
func (f *sharedInformerFactory) Extensions() extensions.Interface {
	return extensions.New(f, f.namespace, f.tweakListOptions)
}
...

extensions.New() 函数和 extensions.Interface 都位于 client-go/informers/extensions 目录下。

extensions informers

// 来源于 k8s.io/client-go/informers/extensions
func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {
	return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
}

type Interface interface {
	V1beta1() v1beta1.Interface
}

接着我们看下一级方法调用 V1beta1():

// 来源于 k8s.io/client-go/informers/extensions
// 继续向下一级传递 factory、namespace 和 tweakListOptions
func (g *group) V1beta1() v1beta1.Interface {
	return v1beta1.New(g.factory, g.namespace, g.tweakListOptions)
}

externsions 是 API Group,它可以包含多个 API 版本子目录,v1beat1 就是其中一个 API 版本的目录。

extensions v1beat1 informers

// 来源于 k8s.io/client-go/informers/extensions/v1beta1/interface.go
func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {
	return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
}

type Interface interface {
    ...
	Deployments() DeploymentInformer
    ...
}

func (v *version) Deployments() DeploymentInformer {
	return &deploymentInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
}
// 来源于 k8s.io/client-go/informers/extensions/v1beta1/deployment.go
type DeploymentInformer interface {
	Informer() cache.SharedIndexInformer
	Lister() v1beta1.DeploymentLister
}

f.factory.InformerFor() 只是创建 cache.SharedIndexInformer 并将它注册到 f.factory,并不会实际运行它,故需要调用返回的 cache.SharedIndexInformer 的 Run() 方法来实际运行它,或者(再)次调用 f.factory 的 Start() 方法(参考 sample-controller 的 main.go,在 NewController() 后调用 factory 的 Start() 方法)

// 来源于:k8s.io/client-go/informers/extensions/v1beta1/deployment.go
// 使用 Factory 的 InformerFor() 方法将创建 Depooyment 的函数注册到 Factory;
// 注册的时候传入了 Deployment 的 Scheme 定义!后续的处理会自动编解码;
func (f *deploymentInformer) Informer() cache.SharedIndexInformer {
	return f.factory.InformerFor(&extensionsv1beta1.Deployment{}, f.defaultInformer)
}

// defaultInformer() :使用 K8S Rest Client 创建 Deployment Informer 的方法
func (f *deploymentInformer) defaultInformer(client kubernetes.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
	return NewFilteredDeploymentInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
}

// 调用 K8S 的 clientset 进行  List 和 Watch
func NewFilteredDeploymentInformer(client kubernetes.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
	return cache.NewSharedIndexInformer(
		&cache.ListWatch{
			ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
				if tweakListOptions != nil {
					tweakListOptions(&options)
				}
				return client.ExtensionsV1beta1().Deployments(namespace).List(options)
			},
			WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
				if tweakListOptions != nil {
					tweakListOptions(&options)
				}
				return client.ExtensionsV1beta1().Deployments(namespace).Watch(options)
			},
		},
		&extensionsv1beta1.Deployment{},
		resyncPeriod,
		indexers,
	)
}

从 Informer 获取 Lister,注意,都是针对 Deployment 这种特定资源对象的:

func (f *deploymentInformer) Lister() v1beta1.DeploymentLister {
	return v1beta1.NewDeploymentLister(f.Informer().GetIndexer())
}

GenericInformer 接口

GenericInformer 接口封装了 SharedIndexInformer 接口,codegen 为各 K8S 资源类型生成的 XXXInformer(如上面的 DeploymentInformer) 均实现了该接口:

// 来源于 k8s.io/client-go/informers/generic.go
type GenericInformer interface {
	Informer() cache.SharedIndexInformer
	Lister() cache.GenericLister
}

内置类型 genericInformer 实现了该接口:

type genericInformer struct {
	informer cache.SharedIndexInformer
	resource schema.GroupResource
}
// Informer returns the SharedIndexInformer.
func (f *genericInformer) Informer() cache.SharedIndexInformer {
	return f.informer
}

// Lister returns the GenericLister.
func (f *genericInformer) Lister() cache.GenericLister {
	return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource)
}

一般情况下,我们可用通过以下两种方式创建 GenericInformer 接口:

  1. 直接使用 codegen 为各 K8S 资源类型生成的 XXXInformer;
  2. 或者使用 sharedInformerFactory 的 ForResource() 方法;
// 来源于 8s.io/client-go/informers/generic.go
func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) {
	switch resource {
	...
	// Group=extensions, Version=v1beta1
	case extensionsv1beta1.SchemeGroupVersion.WithResource("deployments"):
		return &genericInformer{resource: resource.GroupResource(), informer: f.Extensions().V1beta1().Deployments().Informer()}, nil
	...
	}
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值