Skip to content

client-go介绍 原创

client-go 支持 4 种 Client 客户端对象与 Kubernetes API Server 交互的方式,Client 交互对象如图所示。

7bcd751c58d13412e002cfd82d806ed5 MD5

RESTClient 是最基础的客户端。RESTClient 对 HTTP Request 进行了封装,实现了 RESTful 风格的 API。ClientSet、DynamicClient 及 DiscoveryClient 客户端都是基于 RESTClient 实现的。

ClientSet 在 RESTClient 的基础上封装了对 Resource 和 Version 的管理方法。每一个 Resource 可以理解为一个客户端,而 ClientSet 则是多个客户端的集合,每一个 Resource 和 Version 都以函数的方式暴露给开发者。ClientSet 只能够处理 Kubernetes 内置资源,它是通过 client-gen 代码生成器自动生成的。

DynamicClient 与 ClientSet 最大的不同之处是,ClientSet 仅能访问 Kubernetes 自带的资源(即 Client 集合内的资源),不能直接访问 CRD 自定义资源。DynamicClient 能够处理 Kubernetes 中的所有资源对象,包括 Kubernetes 内置资源与 CRD 自定义资源。

DiscoveryClient 发现客户端,用于发现 kube-apiserver 所支持的资源组、资源版本、资源信息(即 Group、Versions、Resources)。

上面四种客户端都可以通过 kubeconfig 连接到集群。

一、kubeconfig

kubeconfig 用于管理访问集群 Kube-apiserver 的配置信息。上面介绍的几个客户端都通过 kubeconfig 来访问集群,其步骤可分为两步:

  • 第 1 步,加载 kubeconfig 配置信息;
  • 第 2 步,合并多个 kubeconfig 配置信息

源码位置:staging\src\k8s.io\client-go\tools\clientcmd\loader.go

go
func (rules *ClientConfigLoadingRules) Load() (*clientcmdapi.Config, error) {
	if err := rules.Migrate(); err != nil {
		return nil, err
	}

	errlist := []error{}
	missingList := []string{}

	kubeConfigFiles := []string{}

	// Make sure a file we were explicitly told to use exists
	if len(rules.ExplicitPath) > 0 {
		if _, err := os.Stat(rules.ExplicitPath); os.IsNotExist(err) {
			return nil, err
		}
		kubeConfigFiles = append(kubeConfigFiles, rules.ExplicitPath)

	} else {
		kubeConfigFiles = append(kubeConfigFiles, rules.Precedence...)
	}

	kubeconfigs := []*clientcmdapi.Config{}
	// read and cache the config files so that we only look at them once
	for _, filename := range kubeConfigFiles {
		if len(filename) == 0 {
			// no work to do
			continue
		}

		config, err := LoadFromFile(filename)

		if os.IsNotExist(err) {
			// skip missing files
			// Add to the missing list to produce a warning
			missingList = append(missingList, filename)
			continue
		}

		if err != nil {
			errlist = append(errlist, fmt.Errorf("error loading config file \"%s\": %v", filename, err))
			continue
		}

		kubeconfigs = append(kubeconfigs, config)
	}

	if rules.WarnIfAllMissing && len(missingList) > 0 && len(kubeconfigs) == 0 {
		klog.Warningf("Config not found: %s", strings.Join(missingList, ", "))
	}

	// first merge all of our maps
	mapConfig := clientcmdapi.NewConfig()

	for _, kubeconfig := range kubeconfigs {
		mergo.MergeWithOverwrite(mapConfig, kubeconfig)
	}

	// merge all of the struct values in the reverse order so that priority is given correctly
	// errors are not added to the list the second time
	nonMapConfig := clientcmdapi.NewConfig()
	for i := len(kubeconfigs) - 1; i >= 0; i-- {
		kubeconfig := kubeconfigs[i]
		mergo.MergeWithOverwrite(nonMapConfig, kubeconfig)
	}

	// since values are overwritten, but maps values are not, we can merge the non-map config on top of the map config and
	// get the values we expect.
	config := clientcmdapi.NewConfig()
	mergo.MergeWithOverwrite(config, mapConfig)
	mergo.MergeWithOverwrite(config, nonMapConfig)

	if rules.ResolvePaths() {
		if err := ResolveLocalPaths(config); err != nil {
			errlist = append(errlist, err)
		}
	}
	return config, utilerrors.NewAggregate(errlist)
}

获取配置文件以及合并配置文件都在同一个方法Load中,其中kubeConfigFiles是记录获取到的配置信息,然后会循环它通过LoadFromFile把配置信息读取出来记录到config对象中,然后通过kubeconfigs将所有的 config 存起来,然后通过mergo.MergeWithOverwrite将所有的config进行合并。

二、RESTClient

RESTClient 是最基础的客户端。其他的 ClientSet、DynamicClient 及 DiscoveryClient 都是基于 RESTClient 实现的。RESTClient 对 HTTP Request 进行了封装,实现了 RESTful 风格的 API。它具有很高的灵活性,数据不依赖于方法和资源,因此 RESTClient 能够处理多种类型的调用,返回不同的数据格式。

三、ClientSet

ClientSet 在 RESTClient 的基础上封装了对 Resource 和 Version 的管理方法。每一个 Resource 可以理解为一个客户端,而 ClientSet 则是多个客户端的集合,每一个 Resource 和 Version 都以函数的方式暴露给开发者。

注意:ClientSet 仅能访问 Kubernetes 自身内置的资源(即客户端集合内的资源),不能直接访问 CRD 自定义资源。

代码示例如下:

go
package main

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

func main() {
	config, err := clientcmd.BuildConfigFromFlags("", "./config")
	if err != nil {
		panic(err)
	}
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		panic(err)
	}
	podClient := clientset.CoreV1().Pods(apiv1.NamespaceDefault)
	podList, err := podClient.List(metav1.ListOptions{Limit: 10})
	if err != nil {
		panic(err)
	}
	for _,pod := range podList.Items{
		fmt.Printf("NAMESPACE: %v \nNAME: %v \nSTATUS: %v \n",pod.Namespace,pod.Name,pod.Status)
	}
}

通过clientcmd.BuildConfigFromFlags来加载配置文件,然后通过kubernetes.NewForConfig来创建clientset对象。然后通过 clientset 对象来操作集群,比如上面的获取 default 命名空间下的 pod。

四、DynamicClient

DynamicClient 是一种动态客户端,它可以对任意 Kubernetes 资源进行 RESTful 操作,包括 CRD 自定义资源。DynamicClient 与 ClientSet 操作类似,同样封装了 RESTClient,同样提供了 Create、Update、Delete、Get、List、Watch、Patch 等方法。

DynamicClient 与 ClientSet 最大的不同之处是,ClientSet 仅能访问 Kubernetes 自带的资源,而 DynamicClient 可以。

注意:DynamicClient 不是类型安全的,因此在访问 CRD 自定义资源时需要特别注意。例如,在操作指针不当的情况下可能会导致程序崩溃。

DynamicClient 的处理过程将 Resource(例如 PodList)转换成 Unstructured 结构类型,Kubernetes 的所有 Resource 都可以转换为该结构类型。处理完成后,再将 Unstructured 转换成 PodList。整个过程类似于 Go 语言的 interface{}断言转换过程。另外,Unstructured 结构类型是通过 map[string]interface{}转换的。

示例如下:

go
package main

import (
	"fmt"
	apiv1 "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() {
	config, err := clientcmd.BuildConfigFromFlags("", "./config")
	if err != nil {
		panic(err)
	}
	dynamicClient, err := dynamic.NewForConfig(config)
	if err != nil {
		panic(err)
	}
	resource := schema.GroupVersionResource{
		Version:  "v1",
		Resource: "pods",
	}
	unstructObj, err := dynamicClient.Resource(resource).Namespace(apiv1.NamespaceDefault).List(metav1.ListOptions{Limit: 10})
	if err != nil {
		panic(err)
	}
	podList := &apiv1.PodList{}
	err = runtime.DefaultUnstructuredConverter.FromUnstructured(unstructObj.UnstructuredContent(), podList)
	if err != nil {
		panic(err)
	}
	for _, pod := range podList.Items {
		fmt.Printf("NAMESPACE: %v \nNAME: %v \nSTATUS: %v \n", pod.Namespace, pod.Name, pod.Status)
	}
}

首先加载 kubeconfig 配置信息,dynamic.NewForConfig 通过 kubeconfig 配置信息实例化 dynamicClient 对象,该对象用于管理 Kubernetes 的所有 Resource 的客户端,例如对 Resource 执行 Create、Update、Delete、Get、List、Watch、Patch 等操作。

dynamicClient.Resource(resource)函数用于设置请求的资源组、资源版本、资源名称。Namespace 函数用于设置请求的命名空间。List 函数用于获取 Pod 列表。得到的 Pod 列表为 unstructured.UnstructuredList 指针类型,然后通过 runtime.DefaultUnstructuredConverter.FromUnstructured 函数将 unstructured.UnstructuredList 转换成 PodList 类型。

五、DiscoveryClient

DiscoveryClient 是发现客户端,它主要用于发现 Kubernetes API Server 所支持的资源组、资源版本、资源信息。还可以将这些信息存储到本地,用于本地缓存(Cache),以减轻对 Kubernetes API Server 访问的压力。在运行 Kubernetes 组件的机器上,缓存信息默认存储于~/.kube/cache 和~/.kube/http-cache 下。

kubectl 的 api-versions 和 api-resources 命令输出也是通过 DiscoveryClient 实现的。

示例代码:

go
package main

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

func main() {
	config, err := clientcmd.BuildConfigFromFlags("", "./config")
	if err != nil {
		panic(err)
	}
	discoveryClient, err := discovery.NewDiscoveryClientForConfig(config)
	if err != nil {
		panic(err)
	}
	_, APIResourceLists, err := discoveryClient.ServerGroupsAndResources()
	if err != nil {
		panic(err)
	}
	for _,list := range APIResourceLists{
		groupVersion, err := schema.ParseGroupVersion(list.GroupVersion)
		if err != nil {
			panic(err)
		}
		for _, resource := range list.APIResources{
			fmt.Printf("name:%v,group:%v,version:%v \n",resource.Name,groupVersion.Group,groupVersion.Version)
		}
	}
}

运行以上代码,列出 Kubernetes API Server 所支持的资源组、资源版本、资源信息。首先加载 kubeconfig 配置信息,discovery.NewDiscoveryClientForConfig通过 kubeconfig 配置信息实例化 discoveryClient 对象,该对象是用于发现 Kubernetes API Server 所支持的资源组、资源版本、资源信息的客户端。

discoveryClient.ServerGroupsAndResources函数会返回 Kubernetes APIServer 所支持的资源组、资源版本、资源信息(即 APIResourceList),通过遍历 APIResourceList 输出信息。

最近更新