健康检查不能依赖kube-apiserver单点,应直连kubelet探活、强制ResourceVersion="0"获取最新配置,并为每个集群单独设超时上下文以避免连锁失败。

健康检查必须绕过 kube-apiserver 的单点依赖
直接调用 kube-apiserver 做集群健康判断,在容灾场景下本身就是个陷阱——如果主集群 apiserver 不可用,你的“健康检查”就跟着一起失明。真实容灾中,得用不依赖控制平面的手段验证集群是否可调度、节点是否 Ready、关键组件是否在运行。
推荐组合:通过 kubectl --kubeconfig 连接各集群后,用 kubectl get nodes -o jsonpath='{.items[*].status.conditions[?(@.type=="Ready")].status}' 提取节点就绪状态;同时用 curl -k https://<node-ip>:10250/healthz</node-ip> 直连 kubelet(需提前开放 NodePort 或配置 SSH 跳转),避免被 control plane 网络故障误判。
- 不要用
kubectl get pods -A全量扫 —— 大集群下超时高、易被限流 - 所有 HTTP 请求必须设
timeout: 3s,Golang 中用http.Client{Timeout: 3 * time.Second} - 若集群启用了 NodeRestriction 准入,
/healthz需用 kubelet client 证书,不能只靠 IP 白名单
用 Go 写并发探测器时,别让 context.WithTimeout 毁掉整个探测链
多个集群并行探测时,常见错误是给整个 for range 循环套一个顶层 context.WithTimeout,结果某个集群网络卡顿,拖垮全部探测结果。正确做法是对每个集群单独建子 context,失败不影响其他。
for _, cluster := range clusters {
ctx, cancel := context.WithTimeout(parentCtx, 5*time.Second)
defer cancel() // 注意:这里不能写在循环外!
go func(c Cluster) {
result := checkCluster(ctx, c)
results <- result
}(cluster)
}- 每个 goroutine 必须有自己的
cancel(),否则泄漏 context 和底层 TCP 连接 - 用
sync.WaitGroup等待所有 goroutine 启动完成,再关闭resultschannel,避免 panic - 对 etcd 成员健康检查(如
etcdctl --endpoints=... endpoint health)建议走 exec.CommandContext,不走 HTTP —— 更准且能捕获证书过期等细节错误
StatefulSet 控制器状态 ≠ 集群健康,得看 Pod 级实际调度行为
很多人误以为 kubectl get statefulset -n kube-system 显示 READY 就代表集群可用,但 StatefulSet 可能因 PVC 绑定失败、污点未容忍、镜像拉取失败而卡在 0/1,Pod 根本没起来。容灾切换前,必须确认至少一个关键工作负载(比如你自己的 disaster-checker Pod)真正在目标集群跑起来了。
立即学习“go语言免费学习笔记(深入)”;
Go 中验证方式:用 clientset.CoreV1().Pods(namespace).Get(ctx, name, metav1.GetOptions{}) 拿到 Pod 对象后,重点检查:
pod.Status.Phase == corev1.PodRunning-
len(pod.Status.Conditions) > 0且最新 condition 是Ready: True -
pod.Status.ContainerStatuses[0].Ready == true(注意索引越界) - 如果 Pod 含 initContainer,还要遍历
pod.Status.InitContainerStatuses确保全部Ready == true
跨集群配置同步失败时,健康检查结果可能自相矛盾
当使用 ClusterResourceNamespace 或 Argo CD 同步 ConfigMap/Secret 到灾备集群,但同步卡住时,你的健康检查程序如果读的是本地缓存(比如用 cache.NewSharedIndexInformer),会看到“旧数据”,误判为配置已就位。这时候检查结果和实际不符。
解决方法很直接:所有关键配置检查都绕过 client-go cache,用 clientset.CoreV1().ConfigMaps(ns).Get(ctx, name, metav1.GetOptions{ResourceVersion: "0"}) 强制直连 apiserver 读最新值。ResourceVersion="0" 是硬刷新开关,别省略。
- 这个操作会增加 apiserver 压力,所以只对核心配置项(如
disaster-switch-flag)启用 - 若灾备集群本身 apiserver 不可用,
Get会报"connection refused"或"i/o timeout",这反而是你要捕获的关键信号 - 别用
list/watch替代 —— watch 流可能中断且无感知,不如一次 Get 来得确定
Golang 实现跨集群健康检查最难的不是并发或 API 调用,而是每一步都要预设“哪一环断了我还能信谁”。网络、证书、RBAC、etcd、kubelet、甚至 DNS 解析顺序,任意一层失效都会让检查结果变成幻觉。留出明确的 fallback 判断路径,比堆砌更多指标更重要。


















