go, k8s

client-go入门

介绍

什么是client-go?client-go是Kubernetes官方提供的Go语言客户端库,用于与Kubernetes API交互。它提供了一组丰富的功能和工具,使开发人员能够轻松地编写Go语言应用程序来管理和操作Kubernetes集群中的资源。
github地址:https://github.com/kubernetes/client-go

主要的目录如下:

  • discovery:提供DisconveryClient客户端。
  • dynamic:提供DynamicClient客户端。
  • examples:提供代码示例。
  • informers:每种Kubernetes资源的Informer实现。
  • kubernetes:提供ClientSet客户端。
  • listers:为每一个Kubernetes资源提供Lister功能,提供只读缓存数据。
  • plugin/pkg/client/auth:提供云服务商授权插件。
  • rest:提供RESTClient客户端。
  • tools:提供常用工具。
  • transport:提供安全的TCP连接。
  • util:提供常用方法。

主要功能如下:

  1. 与Kubernetes ApiServer进行通信:client-go提供了对Kubernetes API资源的CRUD(创建、读取、更新、删除)操作。开发人员可以使用client-go来创建、查询、更新和删除各种 Kubernetes 资源,如Pod、Service、Deployment等。
  2. 控制器开发工具:client-go 提供了一组工具和框架,用于开发自定义控制器。控制器是 Kubernetes 中用于监控和管理资源状态的核心组件,而client-go提供了构建控制器所需的事件监听、状态更新等功能。
  3. 动态客户端:client-go提供了动态客户端库,允许开发人员与Kubernetes API进行交互。包括使用ClientSet进行基于对象的访问和使用DynamicClient进行基于无类型的访问。
  4. Informers 和 Lister:client-go提供了Informer和Lister工具,用于监听Kubernetes资源的变化并保持本地缓存的同步。这使得开发人员能够高效地处理资源事件,并避免频繁地向API服务器发出查询请求。
  5. 身份验证和授权:client-go提供了对Kubernetes集群的身份验证和授权支持。它可以与Kubernetes API服务器进行安全通信,并根据用户的RBAC角色进行授权。

安装

我k8s集群版本是1.24.16,所以这里安装1.24.16版本的client-go,安装文档参考:https://github.com/kubernetes/client-go/blob/master/INSTALL.md

go get k8s.io/client-go@v0.24.16
go get k8s.io/apimachinery/pkg/apis/meta/v1
# apimachinery是最基础的库,包括了核心的数据结构,比如 Scheme、Group、Version、Kind、Resource,即GK、GV、GVK、GVR等
# 如果有的包没有就执行 go mod tidy

客户端

client-go支持4种客户端,RESTClient是最基础的客户端。ClientSet、DynamicClient、DiscoveryClient客户端都是基于RESTClient进行实现的。

file

下面分别介绍一下每种客户端的用法。

RESTClient

源码地址:https://github.com/kubernetes/client-go/blob/306b201a2d292fd78483fdf6147131926ed25a78/rest/client.go

包rest定义了一个REST客户端库,包括了1个接口Interface和两个结构体ClienContentConfig,RESTClient。

Interface接口:

type Interface interface {
    GetRateLimiter() flowcontrol.RateLimiter
    Verb(verb string) *Request
    Post() *Request
    Put() *Request
    Patch(pt types.PatchType) *Request
    Get() *Request
    Delete() *Request
    APIVersion() schema.GroupVersion
}

Interface接口定义了与Kubernetes REST API进行通信的一组操作,包括Post,Get,Patch,Delete等。

ClienContentConfig结构体:

// ClientContentConfig controls how RESTClient communicates with the server.
type ClientContentConfig struct {
    // AcceptContentTypes指定客户端将接受的类型,这是可选的。如果没有设置,ContentType将被用来定义Accept报头
    AcceptContentTypes string
    // ContentType指定用于与服务器通信格式。此值将被设置为向服务器发出请求时的Accept标头。如果未设置AcceptContentTypes,将使用默认content类型。即使用“application/json”。
    ContentType string
    // GroupVersion是要通信的API版本。必须在直接初始化RESTClient时提供。初始化客户端时,将使用默认代码版本进行设置。这被用作VersionedParams的默认组版本。
    GroupVersion schema.GroupVersion
    // 协商器用于获取多种支持的媒体类型的编码器和解码器。
    Negotiator runtime.ClientNegotiator
}

RESTClient结构体:

file

  • base:用于构建客户端的URL。
  • versionedAPIPath:连接base URL和root 资源的路径段。
  • content:描述RESTClient如何对响应进行编码和解码的配置。
  • createBackoffMgr:用于创建BackoffManager的函数,该函数将传递给请求。
  • rateLimiter:在没有特别指定的情况下,该速率限制器将在此客户端创建的所有请求之间共享。
  • warningHandler:在此客户端创建的所有请求之间共享的警告处理程序。如果未设置,将使用默认的警告处理程序。
  • Client:用于发送HTTP请求的特定客户端行为。如果未设置,将使用http.DefaultClient。

NewRESTClient函数

func NewRESTClient(baseURL *url.URL, versionedAPIPath string, config ClientContentConfig, rateLimiter flowcontrol.RateLimiter, client *http.Client) (*RESTClient, error) {

NewRESTClient是一个函数,用于创建一个新的RESTClient实例。它接受base、versionedAPIPath、ClientContentConfig、rateLimiter和Client作为参数,并返回一个新的RESTClient实例。检查并设置默认ContentType,检查base的路径是否以斜杠结尾,没有则添加斜杠,并清除URL中的查询参数和片段标识符。

GetRateLimiter方法

返回RESTClient实例的速率限制器。

readExpBackoffConfig函数

func readExpBackoffConfig() BackoffManager {
    backoffBase := os.Getenv(envBackoffBase)
    backoffDuration := os.Getenv(envBackoffDuration)

    backoffBaseInt, errBase := strconv.ParseInt(backoffBase, 10, 64)
    backoffDurationInt, errDuration := strconv.ParseInt(backoffDuration, 10, 64)
    if errBase != nil || errDuration != nil {
        return &NoBackoff{}
    }
    return &URLBackoff{
        Backoff: flowcontrol.NewBackOff(
            time.Duration(backoffBaseInt)*time.Second,
            time.Duration(backoffDurationInt)*time.Second)}
}

readExpBackoffConfig函数用于读取环境变量配置并返回一个BackoffManager实例。先获取环境变量并赋值,使用strconv.ParseInt函数将backoffBase和backoffDuration解析为int64类型的整数值,并分别赋给backoffBaseInt和backoffDurationInt变量。

如果有解析错误发生,则返回一个NoBackoff的实例。如果没有解析错误发生,则使用flowcontrol.NewBackOff函数创建一个新的BackOff实例,该实例具有指定的backoffBaseInt和backoffDurationInt,使用time.Duration转换为time.Duration类型,然后将其乘以time.Second确保退避时间以秒为单位。

最后将URLBackoff示例作为BackoffManager返回。

Verb方法

返回一个新的Request实例,用于执行指定的REST操作,包括Post、Put、Patch、Get和Delete方法,分别返回相应的Request实例。

APIVersion方法

返回RESTClient实例的API版本。

代码示例

要想在集群外连接k8s集群,首先得有kubeconfig,这就需要k8s.io/client-go/tools/clientcmd包。或者在集群中使用rest.InClusterConfig()来挂载serviceaccount到pod的/var/run/secrets/kubernetes.io/serviceaccount目录来和apiserver进行认证。

下面的代码会列出kube-system命名空间下的所有Pod的状态信息。

package main

import (
    "context"
    "fmt"
    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"
)

var configFile = "E:\\client-go\\config"
var ApiPath = "api"
var namespace = "kube-system"
var resource = "pods"

func main() {
    // 生成kubeconfig配置
    config, err := clientcmd.BuildConfigFromFlags("", configFile)
    if err != nil {
        panic(err)
    }
    // 指定Kubernetes API路径
    config.APIPath = ApiPath
    // 指定要使用的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请求参数
    // Get请求
    if restClient.Get().
        // 指定命名空间
        Namespace(namespace).
        // 指定要获取的资源类型
        Resource(resource).
        // 设置请求参数,使用metav1.ListOptions结构体设置了Limit参数为500,并使用scheme.ParameterCodec进行参数编码。
        VersionedParams(&metav1.ListOptions{Limit: 500}, scheme.ParameterCodec).
        // 发送请求并获取响应,使用context.TODO()作为上下文
        Do(context.TODO()).
        // 将响应解码为podList
        Into(podList); err != nil {
        panic(err)
    }

    for _, v := range podList.Items {
        fmt.Printf("NameSpace: %v  Name: %v  Status: %v \n", v.Namespace, v.Name, v.Status.Phase)
    }
}

运行结果:

file

ClientSet

从上面的例子中可以看到,在连接RestClient之前,需要配置基础路径(api)以及资源的GroupVersion和编解码的方式,对于不同的资源(属于不同的GroupVersion)需要不同的配置,操作还是过于繁琐。

所以就出现了ClientSet,它是在RESTClient的基础上封装了对Resource和Version的管理方法。每一个Resource可以理解为一个客户端,而ClientSet则是多个客户端的集合。通过ClientSet,开发人员只需要建立一次连接,便可以通过不同的接口操作k8s中不同的资源,不用再重新定义GroupVersion、再重新建立连接。ClientSet是最常用的客户端对象。

ClientSet操作资源对象时需要指定Group、Version,然后根据Resource获取,仅能访问Kubernetes内置资源,不能直接访问CRD。

源码地址:https://github.com/kubernetes/client-go/blob/306b201a2d292fd78483fdf6147131926ed25a78/kubernetes/clientset.go

首先定义了一个Interface接口和一个Clientset结构体,包括了k8s资源的很多字段,然后实现接口。Discovery()方法返回了DiscoveryClient客户端。

NewForConfig函数

  • 该函数接受一个rest.Config对象作为参数,并返回一个新的Clientset实例和一个可选的错误。
  • 创建了configShallowCopy变量,将传入的rest.Config参数进行浅拷贝。避免对rest.Config进行修改。
  • 检查UserAgent是否为空,如果为空设置默认的UserAgent,UserAgent通常用于标识客户端的身份和版本信息。
  • 使用rest.HTTPClientFor(&configShallowCopy)函数创建一个HTTP客户端。该函数根据配置对象创建一个适当的HTTP客户端,并将其与配置对象共享。这样做可以确保多个客户端共享同一个底层的HTTP传输实例,以提高性能和效率。
  • 调用NewForConfigAndClient函数,将拷贝的配置对象&configShallowCopy和创建的HTTP客户端作为参数传递给它,以创建一个新的Clientset实例。

NewForConfigAndClient函数

  • 该函数接受一个rest.Config对象和一个http.Client对象作为参数,并返回一个新的Clientset实例和一个可选的错误。
  • 首先同样对rest.Config参数进行浅拷贝。
  • 检查configShallowCopyRateLimiter字段是否为nil,并且QPS大于0。如果满足这两个条件,说明需要设置速率限制器。同时检查Burst是否大于0,因为当RateLimiter未设置且QPS大于0时,要求Burst大于 0。如果Burst不满足要求,将返回一个错误。
  • 如果满足设置速率限制器的条件,将使用flowcontrol.NewTokenBucketRateLimiter函数基于QPS和Burst创建一个令牌桶速率限制器,并将其赋值给configShallowCopyRateLimiter字段。
  • 调用资源对应的函数,创建对应的客户端并赋值。
  • 返回指向新创建的Clientset实例的指针&cs和可能存在的错误。

NewForConfigOrDie函数

  • 该函数接受一个rest.Config对象作为参数,并返回一个新的Clientset实例。
  • 在创建Clientset实例时,如果发生错误则终止程序的执行,以确保创建过程中的任何问题都能被立即发现和处理。

New函数

  • 该函数接受一个rest.Interface接口类型的参数c,并返回一个新的Clientset实例。
  • 首先,创建一个空的Clientset实例cs。然后,使用对象c通过调用相应API包中的New函数创建对应的API客户端。
  • 再通过调用discovery.NewDiscoveryClient函数,使用传入的对象c创建客户端,并将其赋值给cs.DiscoveryClient字段。
  • 最终,返回指向新创建的Clientset实例的指针&cs
  • 这样通过New函数创建的Clientset实例包含了各个API客户端,可以通过调用相应的客户端方法来与Kubernetes API进行交互。调用方可以使用返回的Clientset实例对不同API资源类型进行操作,如创建、更新、删除等。

代码示例

package main

import (
    "context"
    "fmt"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/tools/clientcmd"
)

func generateClient() (*kubernetes.Clientset, error) {
    var kubeConfig = "E:\\client-go\\config"
    config, err := clientcmd.BuildConfigFromFlags("", kubeConfig)
    if err != nil {
        return nil, err
    }
    clientSet, err := kubernetes.NewForConfig(config)
    if err != nil {
        return clientSet, err
    }
    return clientSet, err
}
// clientset通过CoreV1()去访问核心api资源
func ListNodes(c *kubernetes.Clientset) error {
    nodeList, err := c.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
    if err != nil {
        return err
    }
    for _, node := range nodeList.Items {
        fmt.Printf("nodeName: %v, hostName: %v \n", node.Name, node.Status.Addresses[1].Address)
    }
    return nil
}

func ListPods(c *kubernetes.Clientset, ns string) error {
    pods, err := c.CoreV1().Pods(ns).List(context.TODO(), metav1.ListOptions{})
    if err != nil {
        return err
    }
    for _, pod := range pods.Items {
        fmt.Printf("NameSpace: %v  Name: %v  Status: %v \n", pod.Namespace, pod.Name, pod.Status.Phase)
    }
    return nil
}

func ListDeployments(c *kubernetes.Clientset, ns string) error {
    deployment, err := c.AppsV1().Deployments(ns).List(context.TODO(), metav1.ListOptions{})
    if err != nil {
        return err
    }
    for _, deploy := range deployment.Items {
        fmt.Printf("deploymentName: %v, availableReplicas: %v \n", deploy.Name, deploy.Status.AvailableReplicas)
    }
    return nil
}

func ListStatefulSets(c *kubernetes.Clientset, ns string) error {
    statefulSet, err := c.AppsV1().StatefulSets(ns).List(context.TODO(), metav1.ListOptions{})
    if err != nil {
        return err
    }
    for _, sts := range statefulSet.Items {
        fmt.Printf("statefulSetName: %v, replicas: %v \n", sts.Name, sts.Status.Replicas)
    }
    return nil
}

func ListCm(c *kubernetes.Clientset, ns string) error {
    cm, err := c.CoreV1().ConfigMaps(ns).List(context.TODO(), metav1.ListOptions{})
    if err != nil {
        return err
    }
    for _, cm := range cm.Items {
        fmt.Printf("configMapName: %v \n", cm.Name)
    }
    return nil
}

func ListSvc(c *kubernetes.Clientset, ns string) error {
    svc, err := c.CoreV1().Services(ns).List(context.TODO(), metav1.ListOptions{})
    if err != nil {
        return err
    }
    for _, svc := range svc.Items {
        fmt.Printf("serviceName: %v \n", svc.Name)
    }
    return nil
}

func ListSecret(c *kubernetes.Clientset, ns string) error {
    secret, err := c.CoreV1().Secrets(ns).List(context.TODO(), metav1.ListOptions{})
    if err != nil {
        return err
    }
    for _, secret := range secret.Items {
        fmt.Printf("secretName: %v \n", secret.Name)
    }
    return nil
}

func ListPvc(c *kubernetes.Clientset, ns string) error {
    pvc, err := c.CoreV1().PersistentVolumeClaims(ns).List(context.TODO(), metav1.ListOptions{})
    if err != nil {
        return err
    }
    for _, pvc := range pvc.Items {
        fmt.Printf("pvcName: %v \n", pvc.Name)
    }
    return nil
}

func main() {
    var namespace = "kube-system"
    // 生成clientSet
    client, err := generateClient()
    if err != nil {
        fmt.Printf("failed to generate client: %v", err)
    }

    fmt.Println("List Nodes:")
    if err = ListNodes(client); err != nil {
        fmt.Printf("failed to list nodes: %v", err)
    }

    fmt.Println("List Pods:")
    if err = ListPods(client, namespace); err != nil {
        fmt.Printf("failed to list pods: %v", err)
    }

    fmt.Println("List Deployments:")
    if err = ListDeployments(client, namespace); err != nil {
        fmt.Printf("failed to list deployments: %v", err)
    }

    fmt.Println("List StatefulSets:")
    if err = ListStatefulSets(client, namespace); err != nil {
        fmt.Printf("failed to list statefulSets: %v", err)
    }

    fmt.Println("List ConfigMaps:")
    if err = ListCm(client, namespace); err != nil {
        fmt.Printf("failed to list configMaps: %v", err)
    }

    fmt.Println("List Services:")
    if err = ListSvc(client, namespace); err != nil {
        fmt.Printf("failed to list services: %v", err)
    }

    fmt.Println("List Secrets:")
    if err = ListSecret(client, namespace); err != nil {
        fmt.Printf("failed to list secrets: %v", err)
    }

    fmt.Println("List PersistentVolumeClaims:")
    if err = ListPvc(client, namespace); err != nil {
        fmt.Printf("failed to list pvc: %v", err)
    }
}

file

通过kubeconfig字节创建ClientSet客户端

如果没有kubeconfig文件,只有kubeconfig字节,该如何构造ClientSet客户端对象,进而访问APIServer呢?

可以通过clientcmd.RESTConfigFromKubeConfig方法来构造restConfig,通过restConfig构造clientset,进而访问Kubernetes集群。

源码地址:https://github.com/kubernetes/client-go/blob/master/tools/clientcmd/client_config.go#L127

func RESTConfigFromKubeConfig(configBytes []byte) (*restclient.Config, error) {
    clientConfig, err := NewClientConfigFromBytes(configBytes)
    if err != nil {
        return nil, err
    }
    return clientConfig.ClientConfig()
}

代码示例

package main

import (
    "bytes"
    "context"
    "encoding/json"
    "fmt"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/rest"
    "k8s.io/client-go/tools/clientcmd"
)

var kubeConfig string = `apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: LS0tLS1...LS0tLQo=
    server: https://1.2.3.4:6443
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    user: kubernetes-admin
  name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users:
- name: kubernetes-admin
  user:
    client-certificate-data: LS0tLS1...LS0tCg==
    client-key-data: LS0tLS1...LS0tCg==`

func genConfig() (*rest.Config, error) {
    restConfig, err := clientcmd.RESTConfigFromKubeConfig([]byte(kubeConfig))
    if err != nil {
        fmt.Printf("failed to generate config: %v", err)
    }
    return restConfig, err
}

func main() {
    // 生成rest.Config
    restConfig, err := genConfig()
    if err != nil {
        fmt.Printf("failed to generate config: %v", err)
    }
    // 生成clientSet
    clientSet, err := kubernetes.NewForConfig(restConfig)
    if err != nil {
        fmt.Printf("failed to generate client: %v", err)
    }
    // 获取default namespace
    nsList, err := clientSet.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{
        FieldSelector: "metadata.name=default",
    })
    if err != nil {
        fmt.Printf("failed to list namespaces: %v", err)
    }
    // 将namespace列表转为json
    nsListEncode, err := json.Marshal(nsList)
    if err != nil {
        fmt.Printf("failed to encode namespaces: %v", err)
    }
    // 创建bytes.Buffer类型的目标缓冲区
    var nsListBuffer bytes.Buffer
    // 将json格式化输出
    err = json.Indent(&nsListBuffer, nsListEncode, "", "  ")

    fmt.Printf("namespace list: %v \n", nsListBuffer.String())
}

file

其中json.Indent用法如下:

func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error
dst:目标缓冲区,用于存储格式化后的 JSON 数据。
src:源 JSON 数据,以字节切片形式传入。
prefix:每一行缩进的前缀字符串,通常是空格或制表符。
indent:每一个缩进级别的字符串,通常是空格或制表符。

DynamicClient客户端

DynamicClient是一种动态客户端,可以对任意k8s资源进行操作,包括CRD自定义资源。DynamicClient内部实现了Unstructured,用于处理非结构化数据结构。将Resource例如podlist转换为unstructured类型,k8s的所有resource都可以转换为这个结构类型,处理完之后在转换为podlist。

unstructured源码地址:https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured.go

需要处理的对象元数据必须是v1类型,才可以使用unstructured。

type Unstructured struct {
    // Object可以是string、float、int、bool、[]interface{}或map[string]interface{}等任意JSON对象。
    Object map[string]interface{}
}

如果一个controller中需要控制所有的API,那么可以使用DynamicClient。

代码示例

package main

import (
    "context"
    "fmt"
    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"
)

func main() {
    var configFile = "E:\\client-go\\config"
    var ns = "kube-system"
    config, err := clientcmd.BuildConfigFromFlags("", configFile)
    if err != nil {
        panic(err)
    }
    // 根据配置创建dynamicClient
    dynamicClient, err := dynamic.NewForConfig(config)
    if err != nil {
        panic(err)
    }
    // 指定要操作资源的版本和类型
    gvr := schema.GroupVersionResource{
        Version:  "v1",
        Resource: "pods",
    }
    // 获取资源,返回的是*unstructured.UnstructuredList指针类型
    unStructObj, err := dynamicClient.Resource(gvr).Namespace(ns).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)
    }
}

file

代码示例2

上面的代码是获取集群中的资源,这个例子是根据提供的deploy.yaml文件内容,使用client-go与apiserver交互,在default namespace下创建一个deployment。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:stable-alpine
package main

import (
    "context"
    _ "embed"
    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"
    "log"
)

//go:embed deploy.yaml
var deployYaml string

func main() {
    var configFile = "E:\\client-go\\config"
    config, err := clientcmd.BuildConfigFromFlags("", configFile)
    if err != nil {
        panic(err)
    }

    // 根据配置创建dynamicClient
    dynamicClient, err := dynamic.NewForConfig(config)
    if err != nil {
        panic(err)
    }

    deployGVR := schema.GroupVersionResource{
        Group:    "apps",
        Version:  "v1",
        Resource: "deployments",
    }
    // 使用yaml.Unmarshal()函数将deployYaml解析为unstructured.Unstructured对象
    deployObj := &unstructured.Unstructured{}
    if err := yaml.Unmarshal([]byte(deployYaml), deployObj); err != nil {
        panic(err)
    }
    // 使用dynamicClient.Resource()指定命名空间和资源选项, Create()方法创建deployment
    _, err = dynamicClient.Resource(deployGVR).Namespace("default").Create(context.TODO(), deployObj, v1.CreateOptions{})
    if err != nil {
        log.Fatalln(err)
    }

    log.Println("create deployment success")
}

file

file

DiscoveryClient

DiscoveryClient是发现客户端,主要用于发现apiserver支持的资源组,资源版本,资源信息。
kubectl的api-version和api-resource也是通过DiscoveryClient来实现的,还可以将信息缓存在本地cache,以减轻api的访问压力,默认在./kube/cache下。

file

源码地址:https://github.com/kubernetes/client-go/blob/306b201a2d292fd78483fdf6147131926ed25a78/discovery/discovery_client.go

代码示例

func (d *DiscoveryClient) ServerGroupsAndResources() ([]*v1.APIGroup, []*v1.APIResourceList, error)
// ServerGroupsAndResources 返回所有组和版本支持的资源
func ParseGroupVersion(gv string) (GroupVersion, error)
// ParseGroupVersion 将“group/version”字符串转换为 GroupVersion 结构。如果无法解析字符串,则报告错误。
package main

import (
    "fmt"
    "k8s.io/apimachinery/pkg/runtime/schema"
    "k8s.io/client-go/discovery"
    "k8s.io/client-go/tools/clientcmd"
)

func main() {
    var configFile = "E:\\client-go\\config"
    config, err := clientcmd.BuildConfigFromFlags("", configFile)
    if err != nil {
        panic(err)
    }
    // 根据配置创建dynamicClient
    discoveryClient, err := discovery.NewDiscoveryClientForConfig(config)
    if err != nil {
        panic(err)
    }
    // 获取api资源租和资源列表
    _, apiResourceList, err := discoveryClient.ServerGroupsAndResources()
    for _, v := range apiResourceList {
        // 遍历apiResourceList
        gv, err := schema.ParseGroupVersion(v.GroupVersion)
        if err != nil {
            panic(err)
        }
        // 太多了加了limit
        limit := 3
        count := 0
        for _, resource := range v.APIResources {
            // 输出每个资源组中的前三个资源的组、版本和名称。
            fmt.Printf("group: %s, version: %s, name: %s\n", gv.Group, gv.Version, resource.Name)
            count++
            if count >= limit {
                break
            }
        }
    }
}

file

0 0 投票数
文章评分
订阅评论
提醒
guest

0 评论
内联反馈
查看所有评论

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部
0
希望看到您的想法,请您发表评论x