首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >K8s Client 独孤九剑:选对客户端,破尽天下武功

K8s Client 独孤九剑:选对客户端,破尽天下武功

作者头像
希里安
发布2025-06-09 13:30:30
发布2025-06-09 13:30:30
2640
举报
文章被收录于专栏:希里安希里安

希里安近日见闻

一转眼,又是一年高考时,清晨赶考的紧张,笔尖沙沙作响的专注,还有交卷后如释重负的瞬间——那种感觉一定刻骨铭心!

高考已经结束,祝所有高考学子心想事成,金榜题名!无论结果如何,这段拼搏的青春都将闪闪发光,加油!

K8s客户端

上周,有CILIKUBE社区里的小伙伴咨询,开发k8s管理平台的时候,客户端到底有什么作用,应该怎么用?不要着急,我们这就一起来学习探讨下。

想象一下,Kubernetes(K8s)集群是一个巨大的宇宙飞船,里面装满了资源(Node、Pod、Deployment 等),而你作为“舰长”,需要一个控制台来指挥这艘飞船。这个控制台就是 Kubernetes 客户端!通过客户端,你可以查看飞船状态(查询资源)、发射新模块(创建 Pod)、调整航线(更新 Deployment),甚至实时监控(Watch)。

在 Kubernetes 开发中,client-go 库是官方提供的“控制台”,包含多种客户端:RESTClientClientsetDynamic Client,还有基于 client-go 的高级工具 Controller-runtime。这些客户端就像飞船上的不同仪表盘,各有特点,适用不同场景。那么,它们有什么区别?如何选择?

希里安将带你从“小白”到“舰长”,全面解析 Kubernetes 客户端的秘密!

k8s客户端本质:与API服务器“对话”

Kubernetes 客户端是开发者与 Kubernetes API 服务器交互的桥梁。API 服务器提供 RESTful 接口(HTTP 请求),客户端通过这些接口管理集群资源。客户端的核心功能包括:

  • • 查询资源:获取 Node、Pod、Service 等信息(GET)
  • • 创建/更新资源:部署新 Pod 或修改 Deployment(POST/PUT)
  • • 删除资源:清理无用资源(DELETE)
  • • 列表查询:列出所有 Pod 或节点(GET with List)
  • • 实时监控:通过 Watch 接口监听资源变化
  • • 多集群管理:同时操作多个集群

client-go 是 Kubernetes 官方的 Go 语言客户端库,被广泛用于 kubectl、Operator 和自定义管理平台,它提供了以下客户端:

  1. 1. RESTClient:底层的 HTTP 封装,灵活但繁琐
  2. 2. Clientset:类型安全的静态客户端,专为内置资源设计
  3. 3. Dynamic Client:灵活的动态客户端,支持任意资源
  4. 4. Controller-runtime:高级抽象,Operator 开发的首选
  5. 5. Discovery Client:辅助工具,查询 API 支持情况

接下来,我们逐一介绍这些客户端

RESTClient?

RESTClient 是 client-go 中最原始、最底层(k8s.io/client-go/rest)的客户端。直接封装 HTTP 请求,与 Kubernetes API 服务器进行“原始对话”。它不提供类型化的资源操作,而是手动指定 API 路径和 JSON 数据。你可以把它想象成一个“半自动”的 HTTP 客户端,专门用于和符合 Kubernetes API 规范的 RESTful 接口进行通信。

如何使用?

RESTClient 需要手动构造请求路径和解析响应。以下是获取节点信息的示例:

代码语言:javascript
复制
package main

import (
    "context"
    "fmt"
    "os"
    "path/filepath"

    corev1 "k8s.io/api/core/v1"
    "k8s.io/client-go/kubernetes/scheme"
    "k8s.io/client-go/rest"
    "k8s.io/client-go/tools/clientcmd"
)

func main() {
    // 获取 kubeconfig 文件路径(默认 ~/.kube/config)
    kubeconfig := filepath.Join(os.Getenv("HOME"), ".kube", "config")
    if envKubeconfig := os.Getenv("KUBECONFIG"); envKubeconfig != "" {
        kubeconfig = envKubeconfig
    }

    // 从 kubeconfig 构建配置
    config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
    if err != nil {
        fmt.Printf("无法加载 kubeconfig: %v\n", err)
        os.Exit()
    }

    // 设置 GroupVersion 和 APIPath
    config.GroupVersion = &corev1.SchemeGroupVersion // 设置为 core/v1
    config.APIPath = "/api"                          // core 资源的 API 路径
    config.NegotiatedSerializer = scheme.Codecs      // 使用 scheme.Codecs 提供序列化器

    // 创建 RESTClient
    restClient, err := rest.RESTClientFor(config)
    if err != nil {
        fmt.Printf("无法创建 RESTClient: %v\n", err)
        os.Exit()
    }

    // 获取节点列表
    nodeList := &corev1.NodeList{}
    err = restClient.Get().
        AbsPath("/api/v1/nodes").
        Do(context.TODO()).
        Into(nodeList)
    if err != nil {
        fmt.Printf("无法获取节点列表: %v\n", err)
        os.Exit()
    }

    // 打印所有节点名称
    fmt.Println("集群中的节点:")
    for _, node := range nodeList.Items {
        fmt.Printf("- %s\n", node.Name)
    }
}
图片
图片

特点:

  • • 高度原始:它不关心任何 Go 的数据类型(如 v1.Pod),它只负责构建 HTTP 请求(指定 VerbPathBody 等),然后发送给 API Server,并接收返回的 []byte 数据
  • • 不涉及编解码RESTClient 本身不负责将 JSON/Protobuf 数据转换成 Go 结构体。这个工作需要一个叫做 Scheme 的组件来配合完成
  • • 所有客户端的基石:后面我们要讲的所有高级客户端,其底层实现都封装了 RESTClient

优缺点

  • • 优点:灵活性极高,适合自定义请求或调试
  • • 缺点:使用繁琐,无类型安全,代码量大,易出错

注意:除非你在从零构建一个 Kubernetes 客户端库,否则不建议用!

使用场景:

  • • 调试和原型开发:快速测试 API 服务器的响应
  • • 特殊 API 调用:访问非标准或实验性 API
  • • 客户端库开发:Clientset 和 Dynamic Client 底层依赖 RESTClient

说实话,你几乎永远不会直接使用它。它太底层了,使用起来非常繁琐。它的存在主要是为了给上层客户端提供统一的 HTTP 通信基础。了解它,是为了帮助我们理解 K8s 客户端的通信原理

Clientset

Clientset 是 client-go 的静态客户端(k8s.io/client-go/kubernetes),为 Kubernetes 内置资源(如 Node、Pod、Deployment)提供类型化的 Go 接口。它基于 Clientset 结构体,包含所有 API 组的客户端(CoreV1、AppsV1 等),这是最广为人知的客户端,也是处理 K8s 内置资源的首选。

如何使用?

Clientset 通过 kubernetes.NewForConfig 创建,提供直观的方法。以下是获取节点的示例:

代码语言:javascript
复制
import (
    "context"
    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/rest"
    "fmt"
    "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func main() {
    config, err := rest.InClusterConfig()
    if err != nil {
        panic(err)
    }
    clientset, err := kubernetes.NewForConfig(config)
    if err != nil {
        panic(err)
    }
    node, err := clientset.CoreV1().Nodes().Get(context.TODO(), "node-1", v1.GetOptions{})
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    fmt.Printf("Node: %s\n", node.Name)
}
图片
图片

特点

  • • 强类型:每个资源(corev1.Node, appsv1.Deployment)有对应的 Go 结构体,编译器检查类型错误
  • • IDE 友好:方法直观(Get, List, Create 等),支持代码补全
  • • 内置资源专属:覆盖所有标准资源(Pod、Node、Service 等)
  • • Watch 支持:内置实时监控接口,适合事件驱动开发

优缺点

  • • 优点:类型安全,简单直接,开发效率高,适合内置资源
  • • 缺点:无法直接操作自定义资源(CRD),需要额外代码生成注意:操作 Kubernetes 内置资源的不二之选!

适用场景

  • • 标准资源操作:管理 Node、Pod、Deployment 等内置资源
  • • 控制器开发:如 Kubernetes 核心控制器或简单 Operator
  • • 快速开发:需要类型安全和高效开发的场景

Dynamic Client

当 clientset 面对未知的自定义资源(CRD)时,就无能为力了。这时,动态客户端闪亮登场。Dynamic Client 是 client-go 的动态客户端(k8s.io/client-go/dynamic),支持操作任意 Kubernetes 资源,包括自定义资源(CRD)。它使用 unstructured.Unstructured 表示资源,基于 JSON 的键值对

如何使用?

Dynamic Client 需要指定 GroupVersionResource(GVR)。以下是获取节点的示例:

代码语言:javascript
复制
import (
    "context"
    "k8s.io/client-go/dynamic"
    "k8s.io/client-go/rest"
    "k8s.io/apimachinery/pkg/runtime/schema"
    "fmt"
)

func main() {
    config, err := rest.InClusterConfig()
    if err != nil {
        panic(err)
    }
    dynamicClient, err := dynamic.NewForConfig(config)
    if err != nil {
        panic(err)
    }
    gvr := schema.GroupVersionResource{Group: "", Version: "v1", Resource: "nodes"}
    node, err := dynamicClient.Resource(gvr).Get(context.TODO(), "node-1", v1.GetOptions{})
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    fmt.Printf("Node: %s\n", node.GetName())
}
图片
图片

特点

  • • 弱类型:资源以 unstructured.Unstructured 表示,无需预定义结构体,但无类型安全,易出错(比如拼错字段名),开发体验较差
  • • 通吃资源:支持内置资源和 CRD,灵活性极高,适合 CRD 和动态场景
  • • 动态性:适合运行时确定的资源类型
  • • Watch 支持:与 Clientset 类似,支持实时监控。

如何工作

它同样基于 RESTClient。但它不依赖 Scheme 进行强类型转换,而是将所有资源都处理为 unstructured.Unstructured 类型,这本质上是一个 map[string]interface{}。你需要手动从这个 map 中提取你需要的字段

注意:CRD 和通用工具的救星,但小心“灵活”的代价!

适用场景

  • • 自定义资源(CRD):管理 Operator 定义的资源(如 MyApp.v1.example.com)
  • • 动态资源管理:资源类型在运行时确定(比如基于用户输入)
  • • 通用工具开发:如通用的 Kubernetes 管理平台
  • • 多版本兼容:操作不同版本的资源

Controller-runtime

随着 Operator 模式的兴起,开发者发现直接使用 clientset 和 dynamic client 来编写复杂的控制器逻辑,需要处理大量的缓存、索引和错误重试等模板代码。于是,controller-runtime 项目应运而生。

controller-runtime/pkg/client 提供了一个更高层次的抽象客户端,它堪称是 K8s 客户端的“集大成者”。Controller-runtime 是基于 client-go 的高级库(sigs.k8s.io/controller-runtime),专为开发 Kubernetes 控制器和 Operator 设计。它封装了 Clientset 和 Dynamic Client,并引入了缓存机制和事件驱动模型。

核心组件:

  • • Client:统一的接口,支持内置资源和 CRD
  • • Informer:缓存机制,减少 API 服务器请求
  • • Reconciler:控制器逻辑,处理资源变化

优缺点 优点:智能缓存,统一接口,Operator 开发的最佳实践 缺点:有学习成本,需理解缓存和 Reconcile 机制

注意:Operator 开发的“智能大脑”,省心但需先学会!

特点:

  • • 统一接口:它提供了一个统一的 client.Client 接口,可以无缝地处理内置资源和 CRD。你不再需要在代码中维护两个不同的客户端实例。
  • • 内置智能缓存 (Cache):这是它最大的亮点!该客户端会智能地将 GET 和 LIST 请求路由到一个本地的 Informer 缓存中,而不是直接请求 API Server。这极大地降低了 API Server 的负载,对于需要频繁读取资源的 Operator 来说是至关重要的。
  • • 写入操作直通:所有的写入操作(CreateUpdateDelete)会绕过缓存,直接发送给 API Server,保证了数据的一致性。
  • • 与 Scheme 紧密集成:在初始化时,你将所有需要处理的类型(内置和 CRD)注册到 Scheme 中,controller-runtime 客户端就能自动地、类型安全地处理它们。

适用场景:

几乎是所有现代 K8s Operator 或控制器的不二之选。如果你在使用 Kubebuilder 或 Operator SDK 框架来构建你的应用,你接触到的就是这个客户端。它优雅地解决了直接使用低级客户端的各种痛点。

如何使用? Controller-runtime 通常与 Operator SDK 一起使用。以下是监控 Pod 变化的示例:

代码语言:javascript
复制
import (
    "context"
    "k8s.io/client-go/rest"
    "sigs.k8s.io/controller-runtime/pkg/client"
    "sigs.k8s.io/controller-runtime/pkg/client/config"
    "sigs.k8s.io/controller-runtime/pkg/manager"
    corev1 "k8s.io/api/core/v1"
    "fmt"
)

func main() {
    cfg, err := config.GetConfig()
    if err != nil {
        panic(err)
    }
    mgr, err := manager.New(cfg, manager.Options{})
    if err != nil {
        panic(err)
    }
    c, err := client.New(cfg, client.Options{})
    if err != nil {
        panic(err)
    }
    podList := &corev1.PodList{}
    err = c.List(context.TODO(), podList, client.InNamespace("default"))
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    for _, pod := range podList.Items {
        fmt.Printf("Pod: %s\n", pod.Name)
    }
}
图片
图片

Discovery Client

Discovery Client(k8s.io/client-go/discovery)用于查询 Kubernetes API 服务器支持的 API 版本和资源类型,辅助其他客户端

如何使用? 以下是获取服务器版本的示例:

代码语言:javascript
复制
import (
    "k8s.io/client-go/discovery"
    "k8s.io/client-go/rest"
    "fmt"
)

func main() {
    config, err := rest.InClusterConfig()
    if err != nil {
        panic(err)
    }
    discoveryClient, err := discovery.NewDiscoveryClientForConfig(config)
    if err != nil {
        panic(err)
    }
    version, err := discoveryClient.ServerVersion()
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    fmt.Printf("Kubernetes Version: %s\n", version.GitVersion)
}
图片
图片

特点

  • • 轻量:只查询元数据,不操作资源,可辅助其他客户端
  • • 辅助功能:提供 API 组、资源列表和版本信息,功能有限,仅元数据查询

适用场景

  • • API 发现:检查集群支持的资源和版本。
  • • 动态客户端辅助:为 Dynamic Client 提供 GVR 信息.
  • • 多集群兼容性:你的 ClusterManager 可以用 Discovery Client 验证集群版本

如何选择

客户端类型

主要特点

核心优势

核心劣势

一句话选择指南

RESTClient

底层 HTTP 封装

究极灵活

使用极其繁琐,无类型

(别用)除非你在从零构建一个 K8s 客户端库

Clientset

强类型,面向内置资源

类型安全,IDE 友好,简单直接

无法操作 CRD

操作 K8s 内置资源(Pod, Deployment 等)

Dynamic Client

弱类型,面向任意资源

无需编译时类型,通吃所有资源

无类型安全,易出错,体验差

需要操作不确定的 CRD 或编写通用工具

Controller-runtime

高层抽象,内置缓存

统一接口,智能缓存,Operator 最佳实践

需要理解其缓存机制,略有学习成本

编写任何需要监听资源变化的 Operator/Controller

总结:解锁 Kubernetes 开发的“超级控制台”

Kubernetes 客户端是开发资源管理平台、控制器和 Operator 的核心工具。client-go 提供了从底层到高层的完整解决方案:

  • • RESTClient 是地基,处理最底层的通信
  • • Clientset 和 Dynamic Client 是第一层楼,提供了两种不同风格的、可以直接使用的工具:一个专精,一个通用
  • • Controller-runtime Client 是顶层豪华套房,它整合了下层的能力,并增加了缓存等高级功能,为复杂的控制器开发提供了最佳实践

行动起来:

  • • 学习 client-go 的官方示例(k8s.io/client-go/examples)
  • • 尝试 Controller-runtime 和 Operator SDK,开发一个简单的 Operator
  • • 参与CILIKUBE社区,实践动手开发k8s管理平台

参与开源社区

如果你想实践以上k8s客户端,CILIKUBE适合你,还不知道CILIKUBE是什么?查看这篇文章CiliKube开源啦!让小白一次学会K8s 运维 + Web 开发 + k8s二次开发(Vue3+Go 全栈,免费开源),觉得 CILIKUBE对你有帮助,或者你对云原生和 Kubernetes 充满热情,诚挚邀请你:

  • • 点亮Star:前往 GitHub 给我们一个 ⭐!
  • • 体验与反馈:部署试用,并通过 Issue 反馈你的想法和遇到的问题
  • • 贡献代码:我们欢迎各种形式的 PR,无论是新功能、Bug修复还是文档改进
  • • 参与讨论:加入我们的社区,与其他用户和开发者交流心得

GitHub 项目地址: https://github.com/ciliverse/cilikube 在线演示: https://cilikubedemo.cillian.website/

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-06-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 希里安 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 希里安近日见闻
  • K8s客户端
  • k8s客户端本质:与API服务器“对话”
  • RESTClient?
  • Clientset
  • Dynamic Client
  • Controller-runtime
  • Discovery Client
  • 如何选择
  • 总结:解锁 Kubernetes 开发的“超级控制台”
  • 参与开源社区
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档