Appearance
client-go基础
client-go 是 Kubernetes 官方 Go 客户端库。kubectl、控制器、Operator、运维平台里的集群查询功能,底层都绕不开“读取集群连接信息、构造请求配置、访问 API Server、监听资源变化”这条链路。
这条链路里最容易混的是 kubeconfig、RESTConfig、ClientSet 和 Informer。kubeconfig 是配置文件,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 --minifykubeconfig 里有三类核心信息:
| 字段 | 说明 |
|---|---|
clusters | API 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 config | Controller、Operator、Webhook 在集群内运行 |
RESTConfig 还不是客户端。它更像一份“怎么连接 API Server”的运行时配置。后续的 ClientSet、dynamic client、discovery client、Informer factory 都会基于它创建。
QPS 和 Burst 控制客户端请求速率。QPS 是平稳请求速率,Burst 是短时间突发上限。控制器或平台工具如果循环里大量 List,不设置或不理解这两个值,容易把 API Server 打出限流错误。小工具通常先用默认值,持续运行的控制器要关注请求量。
连接 API Server 时的常见错误也多出在这一层:
| 错误现象 | 常见原因 |
|---|---|
connection refused | API Server 地址不通、端口错、代理配置影响 |
x509: certificate signed by unknown authority | CA 证书不匹配或 kubeconfig 缺 CA |
Unauthorized | token、证书或 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 的 apiVersion 是 v1,属于 core group,所以通过 CoreV1() 访问;Deployment 的 apiVersion 是 apps/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 client | Operator 开发里最常用 |
五、资源 CRUD
client-go 对资源的基本操作和 kubectl 对应:
| client-go 方法 | kubectl 类比 | 说明 |
|---|---|---|
Get | kubectl get name | 读取单个对象 |
List | kubectl get | 读取对象列表 |
Create | kubectl create | 创建对象 |
Update | kubectl replace | 基于完整对象更新 |
Patch | kubectl patch | 局部修改 |
Delete | kubectl delete | 删除对象 |
Watch | kubectl get -w | 监听变化 |
Update 需要关注 resourceVersion。多个客户端同时修改同一个对象时,旧版本对象提交会返回 conflict。控制器通常在冲突后重新读取最新对象,再重新计算要写入的变化。
resourceVersion 是 API Server 给对象版本打的标记。读到对象后再提交更新时,API Server 会检查版本是否还是当前版本。如果中途已经被别人改过,旧版本更新会被拒绝,避免把别人的修改覆盖掉。
spec 和 status 也要分清。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.name、status.phase,支持范围由 API Server 决定。标签是业务和平台更常用的筛选方式。
大量对象分页时会用到 Limit 和 Continue。普通小工具可以暂时不处理分页,但平台类工具面向大集群时,分页和超时需要纳入设计。
八、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 对应验证 |
|---|---|
| 读取 kubeconfig | kubectl config view --minify |
| 测试集群连通 | kubectl get ns |
| List Pod | kubectl get pod -n <namespace> |
| LabelSelector | kubectl 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 调用和错误处理会清楚很多。