Golang之 Kubenetes client-go informers添加转换函数, apiserver 内存优化

Kubenetes client-go informers添加转换函数, apiserver 内存优化

Posted by 董江 on Thursday, October 12, 2023

Kubenetes client-go informers添加转换函数,apiserver 内存优化"

背景

client-go 函数转换机制 - transform functions

Client-go 中,对象放入缓存之前对其进行转换。Client-go 允许使用转换功能配置核心信息者。在将对象放入缓存之前,将使用该对象作为参数调用这些函数。如果转换器想要检索其字段,则需要将对象转换为具体或元数据类型。与仅元数据缓存相比,这是一个较少使用的功能。几个用法示例:

  • 在controller-runtime中添加了对转换函数的支持,目的是允许用户删除托管字段和注释
  • Istio 的试点控制器使用此机制来配置其 client-go informer,以在将对象放入缓存之前删除托管字段。 我还没有看到任何使用此机制修改非元数据字段的使用示例。我看不出为什么无法添加新字段(即表明已应用转换的标签)以及删除字段的原因。

使用场景

在 Secret 和 ConfigMap 大量(200+)使用的集群中,Client-go 通过watch informer方式, watch 全部资源很耗内存。

本地KinD实验: 在200个Secret下,通过Client-go的demo 去watch数据性能

func (f *filteredSecretInformer) Informer() Informer {
	typedInformer := f.typedInformerFactory.InformerFor(&corev1.Secret{}, f.newTyped)
	// TODO: 将在这边设置transform
	metadataInformer := f.metadataInformerFactory.ForResource(secretsGVR).Informer()
	// TODO: 设置transform 来删除应用不用到注释、label和ManagedFields等
	return &informer{
		typedInformer:    typedInformer,
		metadataInformer: metadataInformer,
	}
}
func (f *filteredSecretInformer) Lister() SecretLister {
	typedLister := corev1listers.NewSecretLister(f.typedInformerFactory.InformerFor(&corev1.Secret{}, f.newTyped).GetIndexer())
	metadataLister := metadatalister.New(f.metadataInformerFactory.ForResource(secretsGVR).Informer().GetIndexer(), secretsGVR)
	return &secretLister{
		typedClient:           f.typedClient,
		namespace:             f.namespace,
		typedLister:           typedLister,
		partialMetadataLister: metadataLister,
		ctx:                   f.ctx,
	}
}

结果:

apiserver metrics:

  1. 峰值内存使用:~80MiB,约 5 分钟后降至约 70MiB。
  2. 峰值CPU使用:0.06% core

添加清理函数

var_cache.TransformFunc=partialMetadataRemoveAll

// partialMetadataRemoveAll 实现了一个删除的cache.TransformFunc标签、注释和托管 PartialObjectMetadata 中的字段。
func partialMetadataRemoveAll(obj interface{}) (interface{}, error) {
	partialMeta, ok := obj.(*metav1.PartialObjectMetadata)
	if !ok {
		return nil, fmt.Errorf("internal error: cannot cast object %v to PartialObjectMetadata", obj)
	}
	partialMeta.Annotations = nil
	partialMeta.ManagedFields = nil
	partialMeta.Labels = nil
	return partialMeta, nil
}


func (f *filteredSecretInformer) Informer() Informer {
	typedInformer := f.typedInformerFactory.InformerFor(&corev1.Secret{}, f.newTyped)

	metadataInformer := f.metadataInformerFactory.ForResource(secretsGVR).Informer()
	// 设置transform 来删除应用不用到注释、label和ManagedFields等
+	if err := metadataInformer.SetTransform(partialMetadataRemoveAll); err != nil {
+		panic(fmt.Sprintf("internal error: error setting transfomer on the metadata informer: %v", err))
+	}
	return &informer{
		typedInformer:    typedInformer,
		metadataInformer: metadataInformer,
	}
}

apiserver metrics:

  1. 峰值内存使用:~37MiB, 约 5 分钟后降至约 20MiB.
  2. 峰值CPU使用:0.15% core, 约 5 分钟后降至约 0.05% core.

结果

对于大量读写 SecretConfigMap 场景下, 使用自定义的transform, 可将少 60% api-server内存使用;

「如果这篇文章对你有用,请随意打赏」

Kubeservice博客

如果这篇文章对你有用,请随意打赏

使用微信扫描二维码完成支付