使用client-go枚举Kubernetes所有资源速度过慢,求优化方案
优化Kubernetes全资源枚举代码的执行速度
你的代码执行慢的核心原因是串行遍历并调用每个资源的List接口,每个请求都要等待前一个完成,累积起来耗时就很长。以下是具体的优化手段:
核心优化手段
1. 并行执行资源查询
将串行的List请求改为并行处理,利用goroutine同时发起多个资源查询,这能大幅降低总耗时。使用sync.WaitGroup来等待所有goroutine完成,同时加锁避免打印输出混乱。
2. 修正API资源发现与GVR处理逻辑
原代码中GVR的构建存在错误:
group.GroupVersion是group/version格式的字符串(比如apps/v1),直接赋值给gvr.Group会导致GVR格式错误,需要拆分Group和Version- 原
containsSlash函数判断逻辑错误,子资源是形如pod/logs的格式(斜杠在中间),不是开头,导致没跳过子资源,产生大量无效请求
3. 过滤不必要的资源
跳过不需要的资源类型(比如events这类数据量大但非必需的资源,以及所有带斜杠的子资源),减少无效API调用。可根据需求添加更多过滤规则。
4. 精简List请求返回数据
通过metav1.ListOptions指定只返回必要字段(比如仅获取资源名称),减少数据传输量和序列化开销,进一步提升请求速度。
5. 添加上下文超时
使用带超时的上下文,避免某个资源请求卡住导致整体耗时过长。
优化后的代码示例
package main import ( "context" "fmt" "strings" "sync" "time" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" ) func main() { loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() configOverrides := &clientcmd.ConfigOverrides{} kubeconfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides) config, err := kubeconfig.ClientConfig() if err != nil { panic(err.Error()) } clientset, err := kubernetes.NewForConfig(config) if err != nil { panic(err.Error()) } dynClient, err := dynamic.NewForConfig(config) if err != nil { panic(err.Error()) } discoveryClient := clientset.Discovery() // 获取所有API资源 serverResources, err := discoveryClient.ServerPreferredResources() if err != nil { panic(err.Error()) } // 创建带超时的上下文,防止请求无限阻塞 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() var wg sync.WaitGroup var printMu sync.Mutex // 保证打印输出不混乱 for _, group := range serverResources { // 拆分GroupVersion为Group和Version gvParts := strings.Split(group.GroupVersion, "/") var groupName, version string if len(gvParts) == 2 { groupName = gvParts[0] version = gvParts[1] } else { // 核心v1资源,Group为空 version = gvParts[0] } for _, resource := range group.APIResources { // 跳过子资源(名称包含斜杠) if strings.Contains(resource.Name, "/") { continue } // 跳过非必需的资源,比如events if resource.Name == "events" { continue } gvr := schema.GroupVersionResource{ Group: groupName, Version: version, Resource: resource.Name, } wg.Add(1) // 启动goroutine并行查询 go func(gvr schema.GroupVersionResource, resName string, namespaced bool) { defer wg.Done() var list *unstructured.UnstructuredList var err error // 精简返回字段,只获取metadata.name,减少数据传输 listOpts := metav1.ListOptions{ FieldSelector: "metadata.name", } if namespaced { list, err = dynClient.Resource(gvr).List(ctx, listOpts) } else { list, err = dynClient.Resource(gvr).List(ctx, listOpts) } if err != nil { printMu.Lock() fmt.Printf("..Error listing %s: %v. group %q version %q resource %q\n", resName, err, gvr.Group, gvr.Version, gvr.Resource) printMu.Unlock() return } printMu.Lock() fmt.Printf("Found %d resources of type %s. group %q version %q resource %q\n", len(list.Items), resName, gvr.Group, gvr.Version, gvr.Resource) printMu.Unlock() }(gvr, resource.Name, resource.Namespaced) } } // 等待所有并行任务完成 wg.Wait() }
额外优化建议
- 若担心并行请求对API Server压力过大,可添加限流逻辑(比如用带缓冲的channel控制并发数)
- 可缓存Discovery的API资源列表结果,避免每次运行都重新获取
- 针对自定义CRD,可根据需求添加过滤规则,只枚举需要的CRD资源
内容的提问来源于stack exchange,提问作者guettli
相关产品推荐
相关产品推荐

