Kubernetes client-go Informer监听Deployment镜像变更生产集群失效求助
作为Golang新手,我开发了一款部署在K8s集群的应用,核心需求是检测Deployment的镜像变更。使用client-go Informer实现的代码在本地K3s集群能正常捕获镜像变更,但部署到生产集群后,Informer完全无法识别镜像变更,日志仅输出"Starting stopper"。已验证容器内可通过curl获取Deployment资源,ClusterRole、ClusterRoleBinding及ServiceAccount配置与本地环境完全一致,目前无法定位问题,请求帮助排查。
func main() { clientset, err := createKubernetesClient() if err != nil { log.Fatalf("Error creating Kubernetes client: %v", err) } gitlabClient, err := createGitLabClient() if err != nil { log.Fatalf("Failed to create GitLab client: %v", err) } updateGitLabFileAndFetchProject(clientset, gitlabClient) factory := informers.NewSharedInformerFactoryWithOptions(clientset, 0) informer := factory.Apps().V1().Deployments().Informer() stopper := make(chan struct{}) defer close(stopper) defer runtime.HandleCrash() informer.AddEventHandler(cache.ResourceEventHandlerFuncs{ UpdateFunc: func(oldObj, newObj interface{}) { onUpdate(clientset, gitlabClient, oldObj, newObj) }, AddFunc: func(obj interface{}) { onAdd(clientset, gitlabClient, obj) }, }) go informer.Run(stopper) fmt.Println("Starting stopper") if !cache.WaitForCacheSync(stopper, informer.HasSynced) { runtime.HandleError(fmt.Errorf("Timed out waiting for caches to sync")) return } <-stopper } func onUpdate(clientset *kubernetes.Clientset, gitlabClient *gitlab.Client, oldObj, newObj interface{}) { oldDepl := oldObj.(*v1.Deployment) newDepl := newObj.(*v1.Deployment) updateMutex.Lock() defer updateMutex.Unlock() for oldContainerID := range oldDepl.Spec.Template.Spec.Containers { for newContainerID := range newDepl.Spec.Template.Spec.Containers { if oldDepl.Spec.Template.Spec.Containers[oldContainerID].Name == newDepl.Spec.Template.Spec.Containers[newContainerID].Name { if oldDepl.Spec.Template.Spec.Containers[oldContainerID].Image != newDepl.Spec.Template.Spec.Containers[newContainerID].Image { fmt.Printf("OLD DEPLOYMENT %s IN NS %s UPDATED FROM IMAGE %s to NEW DEPLOYMENT %s IN NS %s TO IMAGE %s", oldDepl.Name, oldDepl.Namespace, oldDepl.Spec.Template.Spec.Containers[oldContainerID].Image, newDepl.Name, newDepl.Namespace, newDepl.Spec.Template.Spec.Containers[newContainerID].Image) updateGitLabFileForSingleDeployment(clientset, gitlabClient, newDepl.Name, newDepl.Namespace, newDepl.Spec.Template.Spec.Containers[newContainerID].Image) } } } } }
确认Informer监听范围:当前代码未指定命名空间参数,默认监听所有命名空间。检查生产集群目标Deployment所在命名空间是否正常,再次验证ClusterRole是否包含
list、watch权限。若只需监听特定命名空间,修改Factory初始化代码:factory := informers.NewSharedInformerFactoryWithOptions(clientset, 0, informers.WithNamespace("your-target-namespace"))排查缓存同步阻塞:日志停在"Starting stopper"说明程序卡在缓存同步步骤,添加日志输出同步状态:
fmt.Println("Waiting for cache sync...") if !cache.WaitForCacheSync(stopper, informer.HasSynced) { log.Fatalf("Timed out waiting for caches to sync") } fmt.Println("Cache synced successfully")也可设置明确超时避免无限等待:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) defer cancel() if !cache.WaitForCacheSync(ctx.Done(), informer.HasSynced) { log.Fatalf("Timed out waiting for caches to sync") }验证版本兼容性:本地K3s与生产集群K8s版本可能存在差异,client-go版本需与集群API Server版本兼容(建议client-go版本≤集群版本,差距不超过1个小版本),检查并调整依赖版本。
测试Watch连接可用性:Informer依赖Watch长连接,在容器内执行命令测试:
curl -v "https://kubernetes.default.svc/apis/apps/v1/deployments?watch=true"若连接中断或无响应,说明生产集群存在网络策略、防火墙限制,导致Informer无法接收事件。
增强日志排查:在事件处理函数中添加基础日志,确认Informer是否接收到事件:
AddFunc: func(obj interface{}) { depl := obj.(*v1.Deployment) log.Printf("Received Add event for Deployment %s/%s", depl.Namespace, depl.Name) onAdd(clientset, gitlabClient, obj) }, UpdateFunc: func(oldObj, newObj interface{}) { oldDepl := oldObj.(*v1.Deployment) newDepl := newObj.(*v1.Deployment) log.Printf("Received Update event for Deployment %s/%s (old resourceVersion: %s, new: %s)", oldDepl.Namespace, oldDepl.Name, oldDepl.ResourceVersion, newDepl.ResourceVersion) onUpdate(clientset, gitlabClient, oldObj, newObj) },若启动后无Add日志,说明Informer未获取到Deployment资源,重点排查权限或集群连接问题。
检查Deployment更新逻辑:确认生产集群中镜像变更确实修改了
spec.template.spec.containers.image字段,且触发了Deployment的resourceVersion变化(可通过kubectl get deploy <name> -o yaml对比),若resourceVersion未变化,Informer不会触发Update事件。
内容的提问来源于stack exchange,提问作者roman5595

