必须用 client-go 而非裸 HTTP,因其自动处理认证刷新、410 重同步、resourceVersion 递增、WatchEvent 解析、TLS 双向认证及强类型资源;初始化 config 需按 in-cluster/kubeconfig/手动三种场景适配,漏设 CAData 最易 panic。
直接用 client-go 是唯一可行的生产级方案,别试图手写 HTTP 请求调 Kubernetes API —— 认证、重试、watch 机制、资源版本处理全得自己实现,成本远超收益。
Kubernetes API 不是普通 RE

410 Gone 要触发 list+watch 重同步、resourceVersion 必须严格递增、WatchEvent 流式响应需按帧解析。这些逻辑 client-go 已封装进 SharedInformer 和 RESTClient,裸调只会卡在 401 或 watch 断连不重连。
client-go 自动处理 TLS 双向认证(ca.crt + client.crt + client.key)Pod, Deployment 等)都有强类型 Go struct,避免 JSON 字段拼写错误DynamicClient 操作 CRD,无需为每个 CRD 手动生成 clientset配置对象决定 client 如何连集群,不同环境来源完全不同,不能硬编码。
rest.InClusterConfig(),自动读取 /var/run/secrets/kubernetes.io/serviceaccount/ 下的 token 和 caclientcmd.BuildConfigFromFlags("", kubeconfigPath),依赖 ~/.kube/config 或指定路径rest.Config,必须显式设置 Host、BearerToken、TLSClientConfig.Insecure(仅测试)和 TLSClientConfig.CAData
漏设 TLSClientConfig.CAData 是最常见 panic 原因 —— client-go 默认拒绝不带 CA 的 HTTPS 连接。
以下代码能真正跑通 list/watch,不是玩具:
package main
import (
"context"
"fmt"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/rest"
)
func main() {
var config *rest.Config
var err error
// 优先尝试 in-cluster,失败则 fallback 到 kubeconfig
if config, err = rest.InClusterConfig(); err != nil {
config, err = clientcmd.BuildConfigFromFlags("", "/root/.kube/config")
if err != nil {
panic(err)
}
}
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
panic(err)
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
pods, err := clientset.CoreV1().Pods("default").List(ctx, metav1.ListOptions{})
if err != nil {
panic(err)
}
fmt.Printf("found %d pods\n", len(pods.Items))
}
注意:metav1.ListOptions{} 为空时会返回全部 Pod;加 FieldSelector: "status.phase=Running" 可过滤状态;Limit: 500 防止大 namespace 下 OOM。
不要用轮询,用 Watch 实现事件驱动:
watch.Interface,其 ResultChan() 是阻塞 channel,每次推送一个 watch.Event
Event.Type 是 Added/Modified/Deleted,Event.Object 是具体资源实例(需断言为 *appsv1.Deployment)context.WithCancel 控制生命周期,否则 goroutine 泄漏err != nil 并重试(client-go 的 RetryWatcher 封装了该逻辑)真实项目里,90% 的 Watch 问题出在没处理 Event.Type == "ERROR" 场景 —— 此时 Event.Object 是 metav1.Status,要检查 Status.Reason 是否为 Expired,然后重新 List 获取最新 resourceVersion。