用client-go获取节点列表需传空ListOptions并解析NodeStatus.Conditions中Type="Ready"且Status="True"的项,检查LastHeartbeatTime;更新节点须先Get再Update以满足ResourceVersion要求;驱逐Pod需policy/v1 Eviction权限;监听节点变化应使用Informer而非轮询。

如何用 client-go 获取节点列表和状态
直接调用 clientset.CoreV1().Nodes().List() 是最常用方式,但容易忽略两点:一是必须传 metav1.ListOptions{}(哪怕为空),二是返回的 NodeStatus.Conditions 需手动解析才能判断是否真正就绪。
常见错误现象:代码跑通但始终显示 Ready 为 Unknown,其实是没检查 NodeConditionTypeReady 对应的最新 condition。
- 遍历
node.Status.Conditions,找Type == "Ready"且Status == "True"的项 - 注意
LastHeartbeatTime是否过期(比如 >5 分钟),否则可能节点已失联但 condition 没及时更新 - 若需过滤 NotReady 节点,别只靠
node.Status.Phase == "NotReady"—— 这个字段早已被弃用,Kubernetes v1.24+ 中它恒为""
给节点打标签或加污点前必须先 Get 再 Update
直接构造 corev1.Node 并调用 Update() 会失败,因为 Kubernetes 强制要求乐观并发控制:ResourceVersion 必须与服务端一致。
典型报错:Operation cannot be fulfilled on nodes "node-1": the object has been modified; please apply your changes to the latest version and try again
立即学习“go语言免费学习笔记(深入)”;
- 先执行
clientset.CoreV1().Nodes().Get(ctx, "node-1", metav1.GetOptions{}) - 修改
node.Labels或node.Spec.Taints(注意:Spec.Taints是覆盖写,不是追加) - 再调用
clientset.CoreV1().Nodes().Update(ctx, node, metav1.UpdateOptions{}) - 如果只是加一个污点,别忘了用
append(node.Spec.Taints, newTaint),然后清空旧 taints 再赋值,否则会丢掉已有污点
驱逐节点上所有 Pod 的正确姿势
Evict 不是直接删 Pod,而是向调度器发起“请把这节点上的 Pod 挪走”的请求。它依赖 policy/v1 组的 eviction 子资源,且默认不启用——集群必须开启 PodSecurityPolicy(已废弃)或更常见的 NodeRestriction 插件才允许该操作。
容易踩的坑:本地测试时用 kubectl drain 成功,但 Go 程序调用 Evict() 却返回 Forbidden。
- 确保 ServiceAccount 绑定的 Role 包含
policy:evictions权限(Verb=create, Resource=evictions, Group=policy) - 驱逐单个 Pod 要用
clientset.PolicyV1().Evictions(namespace).Create(),而驱逐整个节点得循环调用或配合PodDisruptionBudget控制节奏 - 生产环境慎用:
Evict不等 Pod graceful termination 完成就返回,后续逻辑需自行轮询Pod.Status.Phase == "Succeeded" or "Failed"
监听节点事件要用 Informer,别用 List + 定时轮询
轮询 List() 查节点变化延迟高、API 压力大,且无法感知中间状态(如 NotReady → Unknown → Ready)。Informer 才是标准解法,它基于 Watch 流自动重连、缓存本地、触发回调。
但新手常卡在初始化:Informer 需要 SharedInformerFactory,而 factory 的启动必须显式调用 Start() 和 WaitForCacheSync(),否则 EventHandler 根本不会触发。
- 用
informers.NewSharedInformerFactory(clientset, 30*time.Second)创建 factory - 调
factory.Core().V1().Nodes().Informer()获取 informer 实例 - 注册
AddFunc/UpdateFunc/DeleteFunc,注意oldObj和newObj都是*corev1.Node类型指针 - 最后必须
factory.Start(ctx.Done())并factory.WaitForCacheSync(ctx.Done()),否则缓存为空,第一次事件就丢失
Node.Status.Conditions 里——你得结合 Node.Status.Addresses、Node.Status.Capacity 和自定义 metrics 接口做交叉验证。单纯靠 client-go 读取节点对象,只能看到调度层视角的“健康”,不是物理层真相。


















