Client-go 里的4种 Client 电脑版发表于:2025/11/4 16:56  >#Client-go 里的4种 Client [TOC] Client-go 里的4种Client ------------  tn2>`RestClient`是最基础的Client客户端,其他`ClientSet`、`DynamicClient`、`DynamicClient`都是继承最基础的`RestClient`客户端请求。 ### RestClient tn2>源码:https://github.com/kubernetes/client-go/blob/master/rest/client.go 最底层的客户端,直接跟Rest API 交互,下面这个代码是Rest的相关请求接口。  tn2>我们进行代码示例一下: ```bash # 初始化项目 go mod init bob ``` ```go package main import ( "context" "flag" "fmt" "path/filepath" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/util/homedir" ) func main() { // 加载 kubeconfig 配置 var kubeconfig *string if home := homedir.HomeDir(); home != "" { kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "[可选] kubeconfig 绝对路径") } else { kubeconfig = flag.String("kubeconfig", "", "kubeconfig 绝对路径") } config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig) if err != nil { panic(err) } // 指定Kubernetes API路径 config.APIPath = "api" // 指定要使用的API组和版本 config.GroupVersion = &corev1.SchemeGroupVersion // 指定编解码器 config.NegotiatedSerializer = scheme.Codecs // 生成RESTClient实例 restClient, err := rest.RESTClientFor(config) if err != nil { panic(err) } // 创建空的结构体,存储pod列表 podList := &corev1.PodList{} // 构建HTTP请求参数,直接定义 GVR // Get请求 restClient.Get(). // 指定命名空间 Namespace("kube-system"). // 指定要获取的资源类型 Resource("pods"). // 设置请求参数,使用metav1.ListOptions结构体设置了Limit参数为500,并使用scheme.ParameterCodec进行参数编码。 VersionedParams(&metav1.ListOptions{Limit: 500}, scheme.ParameterCodec). // 发送请求并获取响应,使用context.TODO()作为上下文 Do(context.TODO()). // 将响应解码为podList Into(podList) for _, v := range podList.Items { fmt.Printf("NameSpace: %v Name: %v Status: %v \n", v.Namespace, v.Name, v.Status.Phase) } } ``` ```bash go mod tidy ``` tn2>然后运行输出测试。  ### ClientSet tn2>源码:https://github.com/kubernetes/client-go/blob/master/kubernetes/clientset.go 这是最常用的Client,实现了所有K8s标准对象的接口,但是用起来比较麻烦,而且不支持自定义的CRD。  tn2>创建一个新的文件夹`bobo`,并且进行初始化 ```bash mkdir bobo cd bobo # 初始化项目 go mod init bob ``` tn2>使用`main.go`编写创建一个Deployment进行部署nginx的代码,代码如下: ```go package main import ( "context" "flag" "fmt" "path/filepath" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/util/homedir" ) func main() { // 加载 kubeconfig 配置 var kubeconfig *string if home := homedir.HomeDir(); home != "" { kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "[可选] kubeconfig 绝对路径") } else { kubeconfig = flag.String("kubeconfig", "", "kubeconfig 绝对路径") } config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig) if err != nil { fmt.Printf("error %s", err.Error()) } // 创建 clientset clientset, err := kubernetes.NewForConfig(config) if err != nil { panic(err.Error()) } // 定义 Nginx Deployment replicas := int32(1) deployment := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: "nginx-deployment", }, Spec: appsv1.DeploymentSpec{ Replicas: &replicas, Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{ "app": "nginx", }, }, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: map[string]string{ "app": "nginx", }, }, Spec: corev1.PodSpec{ Containers: []corev1.Container{ { Name: "nginx", Image: "nginx:1.14.2", Ports: []corev1.ContainerPort{ { ContainerPort: 80, }, }, }, }, }, }, }, } // 创建 Deployment deploymentsClient := clientset.AppsV1().Deployments(corev1.NamespaceDefault) result, err := deploymentsClient.Create(context.TODO(), deployment, metav1.CreateOptions{}) if err != nil { panic(err) } fmt.Printf("Created deployment %q.\n", result.GetObjectMeta().GetName()) } ``` tn2>运行测试。 ```bash go mod tidy go run main.go ```  tn2>我们进行验证一下,发现果然已经创建相关deployment和pod。 ```bash kubectl get pod,deploy ```  ### DynamicClient tn2>源码:https://github.com/kubernetes/client-go/tree/master/dynamic 动态客户端,可以对任何资源进行操作(包括 CRD)   tn2>示例代码如下: ```go package main import ( "context" "flag" "fmt" "path/filepath" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/dynamic" "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/util/homedir" ) func main() { // 加载 kubeconfig 配置 var kubeconfig *string if home := homedir.HomeDir(); home != "" { kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "[可选] kubeconfig 绝对路径") } else { kubeconfig = flag.String("kubeconfig", "", "kubeconfig 绝对路径") } config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig) if err != nil { panic(err) } // 根据配置创建dynamicClient dynamicClient, err := dynamic.NewForConfig(config) if err != nil { panic(err) } // 指定要操作资源的版本和类型 // 这里直接用 gvr 来指定资源类型 gvr := schema.GroupVersionResource{ Version: "v1", Resource: "pods", } // 获取资源,返回的是*unstructured.UnstructuredList指针类型 unStructObj, err := dynamicClient.Resource(gvr).Namespace("kube-system").List( context.TODO(), metav1.ListOptions{}, ) if err != nil { panic(err) } podList := &corev1.PodList{} // 将unstructured对象转换为podList if err = runtime.DefaultUnstructuredConverter.FromUnstructured(unStructObj.UnstructuredContent(), podList); err != nil { panic(err) } for _, v := range podList.Items { fmt.Printf("namespace: %s, name: %s, status: %s\n", v.Namespace, v.Name, v.Status.Phase) } } ```  tn2>通过 `dynamic.Interface` 和 `GroupVersionResource`,代码把任意 YAML 的 apiVersion/kind 映射为 GVR,无需预生成客户端类型即可调用 `dynamicClient.Resource(gvr).Namespace(...).Create(...)`,将 embed 的 deploy.yaml 直接写进集群。(简单来说可通过 GVR 来创建任何资源) 首先我们定义一个`deploy.yaml`。 ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: nginx-dynamic spec: replicas: 1 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx ``` tn2>然后我们通过GVR进行创建。 ```go package main import ( "context" _ "embed" "flag" "log" "strings" "path/filepath" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/yaml" "k8s.io/client-go/dynamic" "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/util/homedir" ) //go:embed deploy.yaml var deployYaml string func main() { // 加载 kubeconfig 配置 var kubeconfig *string if home := homedir.HomeDir(); home != "" { kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "[可选] kubeconfig 绝对路径") } else { kubeconfig = flag.String("kubeconfig", "", "kubeconfig 绝对路径") } config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig) if err != nil { panic(err) } dynamicClient, err := dynamic.NewForConfig(config) if err != nil { panic(err) } // 使用yaml.Unmarshal()将deployYaml解析为unstructured.Unstructured对象 deployObj := &unstructured.Unstructured{} if err := yaml.Unmarshal([]byte(deployYaml), deployObj); err != nil { panic(err) } // 从deployObj中提取apiVersion和kind以确定GVR apiVersion, found, err := unstructured.NestedString(deployObj.Object, "apiVersion") if err != nil || !found { log.Fatalln("apiVersion not found:", err) } kind, found, err := unstructured.NestedString(deployObj.Object, "kind") if err != nil || !found { log.Fatalln("kind not found:", err) } // 根据apiVersion生成GVR gvr := schema.GroupVersionResource{} versionParts := strings.Split(apiVersion, "/") if len(versionParts) == 2 { gvr.Group = versionParts[0] gvr.Version = versionParts[1] } else { // Pod 的话没有 group,只有 version gvr.Version = versionParts[0] } // 根据kind确定资源名称 switch kind { case "Deployment": gvr.Resource = "deployments" case "Pod": gvr.Resource = "pods" // 可以根据需要添加更多的kind default: log.Fatalf("unsupported kind: %s", kind) } // 使用dynamicClient.Resource()指定命名空间和资源选项, Create()方法创建deployment _, err = dynamicClient.Resource(gvr).Namespace("default").Create(context.TODO(), deployObj, v1.CreateOptions{}) if err != nil { log.Fatalln(err) } log.Println("create deployment success") } ``` tn2>运行测试一下: ```bash go mod tidy go run main.go ```  DiscoveryClient ------------ tn2>DiscoveryClient:发现客户端,主要用于发现 apiserver 支持的 Group、Version、Resource kubectl 的 api-version 和 api-resource 就是通过 DiscoveryClient 实现的,它可以将信息缓存在本地 Cache,以减轻 API 的访问压力,默认在 ~/.kube/cache/discovery 目录 