Skip to content

client-go基础

client-go 是 Kubernetes 官方 Go 客户端库。kubectl、控制器、Operator、运维平台里的集群查询功能,底层都绕不开“读取集群连接信息、构造请求配置、访问 API Server、监听资源变化”这条链路。

这条链路里最容易混的是 kubeconfigRESTConfigClientSetInformerkubeconfig 是配置文件,RESTConfig 是程序运行时使用的连接配置,ClientSet 是访问 Kubernetes 内置资源的客户端,Informer 是持续监听资源变化并维护本地缓存的机制。

Kubernetes 的所有资源最终都要通过 API Server 读写。Pod、Deployment、Service 这些对象不是直接从节点上读取,而是由客户端向 API Server 发 HTTP 请求,API Server 再负责认证、鉴权、校验和持久化。client-go 解决的就是 Go 程序如何安全、稳定地发这些请求。

一、从 kubeconfig 到 API Server

人在命令行里执行 kubectl get pod 时,kubectl 会读取 kubeconfig,找到当前使用的集群地址、认证信息和 namespace,然后向 API Server 发请求。

client-go 写出来的工具也是同样路径。差别只是 kubectl 把这条链路封装成命令,Go 程序需要自己把配置和客户端对象建出来。

这几个对象各自处理不同问题。连接集群时需要先知道“连哪里、用什么证书或 token”,这是 kubeconfig 和 RESTConfig 解决的问题;实际读写资源时,需要知道“访问 Pod 还是 Deployment、在哪个 namespace、用 Get 还是 List”,这是 ClientSet 解决的问题;长期监听资源变化时,需要避免反复全量请求 API Server,这是 Informer 解决的问题。

二、kubeconfig

kubeconfig 是 YAML 文件,通常位于:

场景路径
Linux 普通用户~/.kube/config
管理节点 kubeadm/etc/kubernetes/admin.conf
多配置合并KUBECONFIG 环境变量指定多个文件

查看当前上下文:

bash
kubectl config current-context
kubectl config get-contexts
kubectl config view --minify

kubeconfig 里有三类核心信息:

字段说明
clustersAPI Server 地址、CA 证书
users用户认证信息,比如证书、token、exec 插件
contexts把 cluster、user、namespace 组合成一个使用场景

current-context 决定默认连接哪个集群。运维工具如果从本机 kubeconfig 读取配置,误用 context 可能查错集群。涉及生产和测试多集群时,命令行参数里显式传 --kubeconfig--context 更安全。

Pod 内运行的程序通常不用 kubeconfig 文件,而是使用 ServiceAccount。Kubernetes 会把 token、CA 和 namespace 挂载到 Pod 内的固定路径,client-go 可以用 in-cluster 配置读取这些信息。

ServiceAccount 可以看成 Pod 在集群里的身份。程序跑在集群外时,用 kubeconfig 里的用户身份;程序跑在 Pod 里时,用挂载进 Pod 的 ServiceAccount token。两种方式最后都会变成 RESTConfig。

三、RESTConfig

RESTConfig 对应 Go 里的 *rest.Config。它是 client-go 发请求时实际使用的连接配置,包含 API Server 地址、TLS 配置、认证信息、QPS、Burst、UserAgent 等。

常见来源有两种:

来源场景
kubeconfig本地命令行工具、运维平台从外部访问集群
in-cluster configController、Operator、Webhook 在集群内运行

RESTConfig 还不是客户端。它更像一份“怎么连接 API Server”的运行时配置。后续的 ClientSet、dynamic client、discovery client、Informer factory 都会基于它创建。

QPSBurst 控制客户端请求速率。QPS 是平稳请求速率,Burst 是短时间突发上限。控制器或平台工具如果循环里大量 List,不设置或不理解这两个值,容易把 API Server 打出限流错误。小工具通常先用默认值,持续运行的控制器要关注请求量。

连接 API Server 时的常见错误也多出在这一层:

错误现象常见原因
connection refusedAPI Server 地址不通、端口错、代理配置影响
x509: certificate signed by unknown authorityCA 证书不匹配或 kubeconfig 缺 CA
Unauthorizedtoken、证书或 exec 登录过期
context deadline exceeded网络超时、API Server 忙、DNS 慢
the server has asked for the client to provide credentials没有认证信息或认证插件没有执行成功

外部工具排查时先用同一份 kubeconfig 执行一次 kubectl get ns。如果 kubectl 都失败,Go 程序里创建 ClientSet 意义不大。

kubectl get ns 能验证三件事:API Server 地址能连通,认证信息能通过,当前身份至少有读取 namespace 的权限。它不能证明程序逻辑正确,但能先排除 kubeconfig 和网络这类基础问题。

四、ClientSet

ClientSet 是访问 Kubernetes 内置资源的类型化客户端。它把资源按 API Group 和 Version 分好,例如:

调用入口资源类型
CoreV1()Pod、Service、ConfigMap、Secret、Namespace
AppsV1()Deployment、StatefulSet、DaemonSet、ReplicaSet
BatchV1()Job、CronJob
NetworkingV1()Ingress、NetworkPolicy
RbacV1()Role、ClusterRole、RoleBinding、ClusterRoleBinding

API Group 和 Version 来自 Kubernetes 资源自己的 apiVersion。例如 Pod 的 apiVersionv1,属于 core group,所以通过 CoreV1() 访问;Deployment 的 apiVersionapps/v1,所以通过 AppsV1() 访问。这个对应关系和 YAML 里的字段是一致的。

概念关系清楚后,最小调用链只有几步:

go
config, err := clientcmd.BuildConfigFromFlags("", kubeconfigPath)
if err != nil {
	return err
}

clientset, err := kubernetes.NewForConfig(config)
if err != nil {
	return err
}

pods, err := clientset.CoreV1().Pods("default").List(ctx, metav1.ListOptions{})
if err != nil {
	return err
}

这段代码里:

对象作用
config从 kubeconfig 转出来的 RESTConfig
clientset内置资源客户端集合
CoreV1().Pods("default")default 命名空间里的 Pod 客户端
ListOptions标签过滤、字段过滤、分页、resourceVersion 等查询条件

Pods("default") 里的 default 是 namespace。Pod、Service、ConfigMap 这类资源是命名空间级别的;Node、Namespace、ClusterRole 这类资源是集群级别的,调用时不传 namespace 或使用不同入口。

ClientSet 适合访问 Kubernetes 内置资源。CRD 资源不能直接用内置 ClientSet 访问,通常有三种方式:

方式场景
dynamic client临时工具、未知资源类型、少量 CRD 查询
生成 typed client需要和自定义类型强绑定的控制器
controller-runtime clientOperator 开发里最常用

五、资源 CRUD

client-go 对资源的基本操作和 kubectl 对应:

client-go 方法kubectl 类比说明
Getkubectl get name读取单个对象
Listkubectl get读取对象列表
Createkubectl create创建对象
Updatekubectl replace基于完整对象更新
Patchkubectl patch局部修改
Deletekubectl delete删除对象
Watchkubectl get -w监听变化

Update 需要关注 resourceVersion。多个客户端同时修改同一个对象时,旧版本对象提交会返回 conflict。控制器通常在冲突后重新读取最新对象,再重新计算要写入的变化。

resourceVersion 是 API Server 给对象版本打的标记。读到对象后再提交更新时,API Server 会检查版本是否还是当前版本。如果中途已经被别人改过,旧版本更新会被拒绝,避免把别人的修改覆盖掉。

specstatus 也要分清。spec 表示期望状态,通常由用户或上层系统写入;status 表示当前状态,通常由控制器写入。很多资源的 status 有单独的子资源,不能用普通 update 混着改。

例如 Deployment 的 spec.replicas=3 表示期望副本数是 3;status.availableReplicas=2 表示当前可用副本只有 2。平台工具如果把 status 当成可随便写的配置,会和控制器维护的状态冲突。

六、Informer

直接用 ClientSet 定时 List 能做简单巡检,但控制器和 Operator 不能靠不断轮询全部资源。集群对象多时,频繁全量 List 会增加 API Server 压力,也容易漏掉变化过程。

Informer 的工作方式是 List + Watch + 本地缓存

Informer 主要提供三件事:

能力说明
本地缓存控制器读取对象时尽量从缓存读,减少 API Server 压力
事件通知对象新增、更新、删除时触发处理函数
自动重连watch 断开后重新 list/watch

第一次 List 用来拿到当前已有对象,后面的 Watch 用来接收变化事件。只 Watch 不 List,会漏掉程序启动前已经存在的对象;只 List 不 Watch,又需要反复全量查询。

本地缓存保存的是对象副本。控制器读缓存速度快,对 API Server 压力小,但缓存和 API Server 之间可能有很短延迟。写完对象后立刻从缓存读,有时会读到旧对象,这在 Operator 开发里很常见。

事件处理函数里通常不直接做重操作,而是把对象的 namespace/name 放进 workqueue。实际处理逻辑再从队列里取 key,读取最新对象并执行 reconcile。这样能合并短时间内的多次变化,也能对失败任务做限速重试。

workqueue 是控制器内部的任务队列。事件可能连续出现很多次,比如一个 Pod 从 Pending 到 Running 会触发多次更新;队列里通常保留“这个对象需要重新处理”这个 key,处理时再读取最新状态。

七、ListOptions 和过滤

ListOptions 常用于减少查询范围:

go
pods, err := clientset.CoreV1().Pods("default").List(ctx, metav1.ListOptions{
	LabelSelector: "app=nginx",
	FieldSelector: "status.phase=Running",
})

标签过滤对应 kubectl get pod -l app=nginx,字段过滤对应 --field-selector。字段过滤不是所有字段都支持,资源类型不同支持范围也不同。查询不到结果时,可以先用 kubectl 验证 selector 是否符合预期。

LabelSelector 过滤的是 metadata.labels,适合按应用、环境、组件筛选。FieldSelector 过滤的是对象字段,比如 metadata.namestatus.phase,支持范围由 API Server 决定。标签是业务和平台更常用的筛选方式。

大量对象分页时会用到 LimitContinue。普通小工具可以暂时不处理分页,但平台类工具面向大集群时,分页和超时需要纳入设计。

八、RBAC

client-go 访问 Kubernetes 资源时,最终仍然受 RBAC 控制。程序能否 list Pod、watch Deployment、update status,取决于它使用的用户、证书或 ServiceAccount。

常用检查:

bash
kubectl auth can-i list pods -A
kubectl auth can-i watch deployments.apps -n default
kubectl auth can-i update deployments/status -n default

kubectl auth can-i list pods \
  --as=system:serviceaccount:ops-system:ops-controller \
  -A

权限不足时,Go 程序通常收到 forbidden。控制器场景里,用户侧现象往往是自定义资源已经创建,但关联的 Pod、Service 或 Secret 没有变化;排查入口是 controller 日志和 kubectl auth can-i

九、client-go 和 kubectl 的排查对应

Go 程序里的动作kubectl 对应验证
读取 kubeconfigkubectl config view --minify
测试集群连通kubectl get ns
List Podkubectl get pod -n <namespace>
LabelSelectorkubectl get pod -l app=nginx
Watch 资源kubectl get pod -w
RBAC 检查kubectl auth can-i ...
查看对象完整字段kubectl get pod <name> -o yaml

client-go 的代码问题经常可以先用 kubectl 缩小范围。kubectl 使用同一份 kubeconfig、同一个 namespace、同一个 label selector 能查到对象,再回到程序里看 RESTConfig、ClientSet 调用和错误处理会清楚很多。