Go client-go 实战:从 Informer 到 Lister 高级用法
K8s Operator 开发离不开 client-go。本文系统讲清 client-go 的核心 API 与最佳实践。
一、client-go 三大基础组件
- Clientset:高级抽象,提供 typed / dynamic client
- Informer:本地 cache 监听 API server 推送
- Lister:从 Informer 缓存里取数据,本地查询快
二、Clientset 简单使用
import"k8s.io/client-go/kubernetes"import"k8s.io/client-go/tools/clientcmd"cfg,_:=clientcmd.BuildConfigFromFlags("",kubeconfig)clientset,_:=kubernetes.NewForConfig(cfg)pods,_:=clientset.CoreV1().Pods("default").List(ctx,metav1.ListOptions{})for_,pod:=rangepods.Items{fmt.Println(pod.Name)}直接 call API Server 会带来 list/transform 负担,影响主流程。
三、SharedInformerFactory
factory:=informers.NewSharedInformerFactoryWithOptions(clientset,30*time.Second,informers.WithNamespace("default"),)podInformer:=factory.Core().V1().Pods()factory 同一资源只走一份 watcher。
四、Lister + Indexer
podLister:=podInformer.Lister()pods,_:=podLister.Pods("default").List(labels.Everything())Lister 直接从 indexer / cache 取,不打 API server。
五、Indexer 自定义索引
podInformer.Informer().GetIndexer().AddIndexers(map[string]IndexFunc{"podIP":func(objinterface{})([]string,error){pod:=obj.(*v1.Pod)return[]string{pod.Status.PodIP},nil},})podsByIP,_:=podLister.ByIndex("podIP","10.0.0.1")方便快速查询。
六、EventHandler 监听
podInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{OnAdd:func(objinterface{}){p:=obj.(*v1.Pod)log.Printf("POD ADDED: %s",p.Name)},OnUpdate:func(old,newinterface{}){// 监听变化},OnDelete:func(objinterface{}){// 清理资源},})七、Workqueue + 限速
import"k8s.io/client-go/util/workqueue"queue:=workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())podInformer.Informer().AddEventHandler(&cache.ResourceEventHandlerFuncs{OnAdd:func(objinterface{}){key,_:=cache.MetaNamespaceKeyFunc(obj)queue.Add(key)},})gofunc(){for{key,_:=queue.Get()deferqueue.Done(key)iferr:=process(key.(string));err!=nil{queue.AddRateLimited(key)return}queue.Forget(key)}}()八、Dynamic Client
无类型时用:
import"k8s.io/client-go/dynamic"dyn,_:=dynamic.NewForConfig(cfg)gvr:=schema.GroupVersionResource{Group:"apps",Version:"v1",Resource:"deployments"}unstr,_:=dyn.Resource(gvr).Namespace("default").Get(ctx,"myapp",metav1.GetOptions{})适合写通用 K8s 工具。
九、Discovery
import"k8s.io/client-go/discovery"discoveryClient,_:=discovery.NewDiscoveryClientForConfig(cfg)apiGroups,_,_:=discoveryClient.ServerGroupsAndResources()动态发现 API。
十、踩坑清单
- Informer 启动后再入队——否则会 miss 事件
- ResyncPeriod不要设 0,会触发全量重做
- Lister Get 不报错:可能数据仍是空
- Workqueue 别忘了 ShutDown
十一、实战:自定义控制器
funcprocess(keystring)error{ns,name,_:=cache.SplitMetaNamespaceKey(key)pod,err:=lister.Pods(ns).Get(name)iferr!=nil{returnerr}returnbusinessLogic(pod)}标准 Operator 流程:
Informer → Workqueue → Worker → Reconcile → Status Update十二、生产实战:cluster-operator
Deployment:
apiVersion:apps/v1kind:Deploymentmetadata:name:controllernamespace:controller-systemspec:replicas:1template:spec:serviceAccountName:controllercontainers:-name:controllerimage:my/controller:v1十三、总结与展望
client-go 是 K8s 编程的"圣杯"。Informer + Lister + Workqueue 三件套让资源高效监听 + 优雅处理。掌握它能写出生产级 Operator。
未来:controller-runtime 抽象层越来越普及,复杂 K8s 平台开发门槛进一步降低。
十四、参考文献
- client-go 仓库
- sample-controller 示例
- Programming Kubernetes book