跳到主要内容

2 篇博文 含有标签「Client-go」

查看所有标签

Client-go 四种客户端交互对象

· 阅读需 16 分钟

Client-go 四种客户端交互对象

![Client-go四种客户端交互对象.png](/img/Client-go 四种客户端交互对象/Client-go四种客户端交互对象.png)

RESTClient 是最基础的客户端。 RESTClientHTTP Request 进⾏了封装,实现了 RESTful ⻛格的 API 。 ClientSetDynamicClientDiscoveryClient 客户端都是基于 RESTClient 实现的。

ClientSetRESTClient 的基础上封装了对 ResourceVersion 的管理⽅法。每⼀个 Resource 可以理解为⼀个客户端,⽽ ClientSet 则是多个客户端的集合,每⼀个 ResourceVersion 都以函数的⽅式暴露给开发者。 ClientSet 只能够处理 Kubernetes 内 置资源,它是通过 client-gen 代码⽣成器⾃动⽣成的。

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

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

以上 4 种客户端: RESTClientClientSetDynamicClientDiscoveryClient 都可以通过 kubeconfig 配置信息连接到指定的 Kubernetes API Server

RestClient 客户端对象

RESTClient 是最基础的客户端。其他的 ClientSetDynamicClientDiscoveryClient 都是基于 RESTClient 实现的。 RESTClientHTTP Request 进⾏了封装,实现了 RESTful ⻛格的 API 。它具有很⾼的灵活性,数据不依赖于⽅法和资源,因此 RESTClient 能够处理多种类型的调⽤,返回不同的数据格式。

类似于 kubectl 命令,通过 RESTClient 列出 kube-system 运⾏的 Pod 资源对象, RESTClient Example 代码⽰例 :

package main

import (
"context"
"flag"
"fmt"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/homedir"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"path/filepath"
)

func main() {
var kubeconfig *string

// home是家目录,如果能取得家目录的值,就可以用来做默认值
if home:=homedir.HomeDir(); home != "" {
// 如果输入了kubeconfig参数,该参数的值就是kubeconfig文件的绝对路径,
// 如果没有输入kubeconfig参数,就用默认路径~/.kube/config
kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
} else {
// 如果取不到当前用户的家目录,就没办法设置kubeconfig的默认目录了,只能从入参中取
kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
}

flag.Parse()

// 从本机加载kubeconfig配置文件,因此第一个参数为空字符串
config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)

// kubeconfig加载失败就直接退出了
if err != nil {
panic(err.Error())
}

// 参考path : /api/v1/namespaces/{namespace}/pods
config.APIPath = "api"
// pod的group是空字符串
config.GroupVersion = &corev1.SchemeGroupVersion
// 指定序列化工具
config.NegotiatedSerializer = scheme.Codecs

// 根据配置信息构建restClient实例
restClient, err := rest.RESTClientFor(config)

if err!=nil {
panic(err.Error())
}

// 保存pod结果的数据结构实例
result := &corev1.PodList{}

// 指定namespace
namespace := "kube-system"
// 设置请求参数,然后发起请求
// GET请求
err = restClient.Get().
// 指定namespace,参考path : /api/v1/namespaces/{namespace}/pods
Namespace(namespace).
// 查找多个pod,参考path : /api/v1/namespaces/{namespace}/pods
Resource("pods").
// 指定大小限制和序列化工具
VersionedParams(&metav1.ListOptions{Limit:100}, scheme.ParameterCodec).
// 请求
Do(context.TODO()).
// 结果存入result
Into(result)

if err != nil {
panic(err.Error())
}

// 打印名称
fmt.Printf("Namespace\t Status\t\t Name\n")

// 每个pod都打印Namespace、Status.Phase、Name三个字段
for _, d := range result.Items {
fmt.Printf("%v\t %v\t %v\n",
d.Namespace,
d.Status.Phase,
d.Name)
}
}

运⾏以上代码,列出 kube-system 命名空间下的所有 Pod 资源对象的相关信息。⾸先加载 kubeconfig 配置信息,并设置 config.APIPath 请求的 HTTP 路径。然后设置 config.GroupVersion 请求的资源组/资源版本。最后设置 config.NegotiatedSerializer 数据的编解码器。

rest.RESTClientFor 函数通过 kubeconfig 配置信息实例化 RESTClient 对象, RESTClient 对象构建 HTTP 请求参数,例如 Get 函数设置请求⽅法为 get 操作,它还⽀持 PostPutDeletePatch 等请求⽅法。 Namespace 函数设置请求的命名空间。 Resource 函数设置请求的资源名称。 VersionedParams 函数将⼀些查询选项(如 limit、TimeoutSeconds 等)添加到请求参数中。通过 Do 函数执⾏该请求,并将 kube-apiserver 返回的结果( Result 对象)解析到 corev1.PodList 对象中。最终格式化输出结果。

RESTClient 发送请求的过程对 Go 语⾔标准库 net/http 进⾏了封装,由 Do→request 函数实现,代码⽰例如下:

代码路径: vendor/k8s.io/client-go/rest/request.go

func (r *Request) Do(ctx context.Context) Result { 
var result Result
err := r.request(ctx, func(req *http.Request, resp *http.Response) {
result = r.transformResponse(resp, req)
})
if err != nil {
return Result{err: err}
}return result
}

请求发送之前需要根据请求参数⽣成请求的 RESTful URL ,由 r.URL.String 函数完成。例如,在 RESTClient Example 代码⽰例中,根据请求参数⽣成请求的 RESTful URL http://127.0.0.1:8080/api/v1/namespaces/kube-system/pods?limit=500 ,其中 api 参数为 v1namespace 参数为 system ,请求的资源为 podslimit 参数表⽰最多检索出 500 条信息。

最后通过 Go 语⾔标准库 net/httpRESTful URL (即 kube-apiserver )发送请求,请求得到的结果存放在 http.ResponseBody 对象中, fn 函数(即 transformResponse )将结果转换为资源对象。当函数退出时,会通过 resp.Body.Close 命令进⾏关闭,防⽌内存溢出。

ClientSet客户端对象

RESTClient 是⼀种最基础的客户端,使⽤时需要指定 ResourceVersion 等信息,编写代码时需要提前知道 Resource 所在的 Group 和对应的 Version 信息。相⽐ RESTClientClientSet 使⽤起来更加便捷,⼀般情况下,开发者对 Kubernetes 进⾏⼆次开发时通常使⽤ ClientSet

ClientSetRESTClient 的基础上封装了对 ResourceVersion 的管理⽅法。每⼀个 Resource 可以理解为⼀个客户端,⽽ ClientSet 则是多个客户端的集合,每⼀个 ResourceVersion 都以函数的⽅式暴露给开发者,例如, ClientSet 提供的 RbacV1CoreV1NetworkingV1 等接⼝函数

注意: ClientSet 仅能访问 Kubernetes ⾃⾝内置的资源(即客户端集合内的资源),不能直接访问 CRD ⾃定义资源。 如果需要 ClientSet 访问 CRD ⾃定义资源,可以通过 client-gen 代码⽣成器重新⽣成 ClientSet ,在 ClientSet 集合中⾃动⽣成与 CRD 操作相关的接⼝

![ClientSet.png](/img/Client-go 四种客户端交互对象/ClientSet.png)

通过 ClientSet 创建⼀个新的命名空间 xiaoyuan 以及⼀个新的 deploymentClientSet Example 代码,类似于 kubectl 命令

⽰例如下:

package main

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

appsv1 "k8s.io/api/apps/v1"
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"
"k8s.io/client-go/util/homedir"
)

const (
NAMESPACE = "xiaoyuan"
DEPLOYMENTNAME = "kubecto-deploy"
IMAGE = "nginx:1.13"
PORT = 80
REPLICAS = 2
)

func main() {
var kubeconfig *string
// home是家目录,如果能取得家目录的值,就可以用来做默认值
if home := homedir.HomeDir(); home != "" {
// 如果输入了kubeconfig参数,该参数的值就是kubeconfig文件的绝对路径,
// 如果没有输入kubeconfig参数,就用默认路径~/.kube/config
kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) kubeconfig文件的绝对路径")
} else {
kubeconfig = flag.String("kubeconfig", "", "kubeconfig文件的绝对路径")
}
flag.Parse()

// 从本机加载kubeconfig配置文件,因此第一个参数为空字符串
config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
// kubeconfig加载失败就直接退出了
if err != nil {
panic(err)
}
// 实例化clientset对象
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
panic(err)
}
// 引用namespace的函数
createNamespace(clientset)

// 引用deployment的函数
createDeployment(clientset)
}

// 新建namespace
func createNamespace(clientset *kubernetes.Clientset) {
namespaceClient := clientset.CoreV1().Namespaces()

namespace := &apiv1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: NAMESPACE,
},
}

result, err := namespaceClient.Create(context.TODO(), namespace, metav1.CreateOptions{})

if err!=nil {
panic(err.Error())
}

fmt.Printf("Create namespace %s \n", result.GetName())
}
// 新建deployment
func createDeployment(clientset *kubernetes.Clientset) {
//如果希望在default命名空间下场景可以引用apiv1.NamespaceDefault默认字符
//deploymentsClient := clientset.AppsV1().Deployments(apiv1.NamespaceDefault)
//拿到deployment的客户端
deploymentsClient := clientset.AppsV1().Deployments(NAMESPACE)

deployment := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: DEPLOYMENTNAME,
},
Spec: appsv1.DeploymentSpec{
Replicas: int32Ptr(REPLICAS),
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"app": "kubecto",
},
},
Template: apiv1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
"app": "kubecto",
},
},
Spec: apiv1.PodSpec{
Containers: []apiv1.Container{
{
Name: "web",
Image: IMAGE,
Ports: []apiv1.ContainerPort{
{
Name: "http",
Protocol: apiv1.ProtocolTCP,
ContainerPort: PORT,
},
},
},
},
},
},
},
}

// Create Deployment
fmt.Println("Creating deployment...")
result, err := deploymentsClient.Create(context.TODO(), deployment, metav1.CreateOptions{})
if err != nil {
panic(err)
}
fmt.Printf("Created deployment %q.\n", result.GetObjectMeta().GetName())
}
//引用replicas带入副本集
func int32Ptr(i int32) *int32 { return &i }

运⾏以上代码,会创建 2nginxdeployment 。⾸先加载 kubeconfig 配置信息, kubernetes.NewForConfig 通过 kubeconfig 配置信息实例化 clientset 对象,该对象⽤于管理所有 Resource 的客户端。

DynamicClient客户端对象

DynamicClient 是⼀种动态客户端,它可以对任意 Kubernetes 资源进⾏ RESTful 操作,包括 CRD ⾃定义资源。 DynamicClientClientSet 操作类似,同样封装了 RESTClient ,同样提供了 CreateUpdateDeleteGetListWatchPatch 等⽅法

DynamicClientClientSet 最⼤的不同之处是, ClientSet 仅能访问 Kubernetes ⾃带的资源(即客户端集合内的资源),不能直接访问 CRD ⾃定义资源。 ClientSet 需要预先实现每种 ResourceVersion 的操作,其内部的数据都是结构化数据(即已知数据结构)。⽽ DynamicClient 内部实现了 Unstructured ,⽤于处理⾮结构化数据结构(即⽆法提前预知数据结构),这也是 DynamicClient 能够处理 CRD ⾃定义资源的关键

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

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

通过 DynamicClient 创建 deployment ,并使⽤ list 列出当前 pod 的名称和数量,类似 kubectl 命令 ,DynamicClient Example 代码⽰例:

package main

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

apiv1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"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() {
var kubeconfig *string
// home是家目录,如果能取得家目录的值,就可以用来做默认值
// 如果输入了kubeconfig参数,该参数的值就是kubeconfig文件的绝对路径,
// 如果没有输入kubeconfig参数,就用默认路径~/.kube/config
if home := homedir.HomeDir(); home != "" {
kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
} else {
kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
}
flag.Parse()

//定义函数内的变量
namespace := "default"
replicas := 2
deployname := "ku"
image := "nginx:1.17"

// 从本机加载kubeconfig配置文件,因此第一个参数为空字符串
config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
// kubeconfig加载失败就直接退出了
if err != nil {
panic(err)
}
// dynamic.NewForConfig实例化clientset对象
client, err := dynamic.NewForConfig(config)
if err != nil {
panic(err)
}
//使用schema的包带入gvr
deploymentRes := schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}
//定义结构化数据结构
deployment := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": map[string]interface{}{
"name": deployname,
},
"spec": map[string]interface{}{
"replicas": replicas,
"selector": map[string]interface{}{
"matchLabels": map[string]interface{}{
"app": "demo",
},
},
"template": map[string]interface{}{
"metadata": map[string]interface{}{
"labels": map[string]interface{}{
"app": "demo",
},
},

"spec": map[string]interface{}{
"containers": []map[string]interface{}{
{
"name": "web",
"image": image,
"ports": []map[string]interface{}{
{
"name": "http",
"protocol": "TCP",
"containerPort": 80,
},
},
},
},
},
},
},
},
}

// 创建 Deployment
fmt.Println("创建 deployment...")
result, err := client.Resource(deploymentRes).Namespace(namespace).Create(context.TODO(), deployment, metav1.CreateOptions{})
if err != nil {
panic(err)
}

fmt.Printf("创建 deployment %q.\n", result.GetName())

// 列出 Deployments
prompt()
fmt.Printf("在命名空间中列出deployment %q:\n", apiv1.NamespaceDefault)
list, err := client.Resource(deploymentRes).Namespace(namespace).List(context.TODO(), metav1.ListOptions{})
if err != nil {
panic(err)
}
for _, d := range list.Items {
replicas, found, err := unstructured.NestedInt64(d.Object, "spec", "replicas")
if err != nil || !found {
fmt.Printf("Replicas not found for deployment %s: error=%s", d.GetName(), err)
continue
}
fmt.Printf(" * %s (%d replicas)\n", d.GetName(), replicas)
}

}
func prompt() {
fmt.Printf("--------------> 按回车键继续 <--------------.")
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
break
}
if err := scanner.Err(); err != nil {
panic(err)
}
fmt.Println()
}

⾸先加载 kubeconfig 配置信息, dynamic.NewForConfig 通过 kubeconfig 配置信息实例化 dynamicClient 对象,该对象⽤于管理 Kubernetes 的所有 Resource 的客户端,例如对 Resource 执⾏ CreateUpdateDeleteGetListWatchPatch 等操作

DiscoveryClient客户端对象

DiscoveryClient 是发现客户端,它主要⽤于发现 Kubernetes API Server 所⽀持的资源组、资源版本、资源信息。 Kubernetes API Server ⽀持很多资源组、资源版本、资源信息,开发者在开发过程中很难记住所有信息,此时可以通过 DiscoveryClient 查看所⽀持的资源组、资源版本、资源信息。

kubectlapi-versionsapi-resources 命令输出也是通过 DiscoveryClient 实现的。另外, DiscoveryClient 同样在 RESTClient 的基础上进⾏了封装

DiscoveryClient 除了可以发现 Kubernetes API Server 所⽀持的资源组、资源版本、资源信息,还可以将这些信息存储到本地,⽤于本地缓存( Cache ),以减轻对 Kubernetes API Server 访问的压⼒。在运⾏ Kubernetes 组件的机器上,缓存信息默认存储于 〜/.kube/cache〜/.kube/http-cache

类似于 kubectl 命令,通过 DiscoveryClient 列出 Kubernetes API Server 所⽀持的资源组、资源版本、资源信息, DiscoveryClient Example 代码⽰例如下:

package main

import (
"flag"
"fmt"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/discovery"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/homedir"
"path/filepath"
)

func main() {

var kubeconfig *string

// home是家目录,如果能取得家目录的值,就可以用来做默认值
if home:=homedir.HomeDir(); home != "" {
// 如果输入了kubeconfig参数,该参数的值就是kubeconfig文件的绝对路径,
// 如果没有输入kubeconfig参数,就用默认路径~/.kube/config
kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
} else {
// 如果取不到当前用户的家目录,就没办法设置kubeconfig的默认目录了,只能从入参中取
kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
}

flag.Parse()

// 从本机加载kubeconfig配置文件,因此第一个参数为空字符串
config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)

// kubeconfig加载失败就直接退出了
if err != nil {
panic(err.Error())
}

// 新建discoveryClient实例
discoveryClient, err := discovery.NewDiscoveryClientForConfig(config)

if err != nil {
panic(err.Error())
}

// 获取所有分组和资源数据
APIGroup, APIResourceListSlice, err := discoveryClient.ServerGroupsAndResources()

if err != nil {
panic(err.Error())
}

// 先看Group信息
fmt.Printf("APIGroup :\n\n %v\n\n\n\n",APIGroup)

// APIResourceListSlice是个切片,里面的每个元素代表一个GroupVersion及其资源
for _, singleAPIResourceList := range APIResourceListSlice {

// GroupVersion是个字符串,例如"apps/v1"
groupVerionStr := singleAPIResourceList.GroupVersion

// ParseGroupVersion方法将字符串转成数据结构
gv, err := schema.ParseGroupVersion(groupVerionStr)

if err != nil {
panic(err.Error())
}

fmt.Println("*****************************************************************")
fmt.Printf("GV string [%v]\nGV struct [%#v]\nresources :\n\n", groupVerionStr, gv)

// APIResources字段是个切片,里面是当前GroupVersion下的所有资源
for _, singleAPIResource := range singleAPIResourceList.APIResources {
fmt.Printf("%v\n", singleAPIResource.Name)
}
}
}

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

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

获取 Kubernetes API Server 所⽀持的资源组、资源版本、资源信息 Kubernetes API Server 暴露出 /api/apis 接⼝。 DiscoveryClient 通过 RESTClient 分别请求 /api/apis 接⼝,从⽽获取 Kubernetes API Server 所⽀持的资源组、资源版本、资源信息。其核⼼实现位于 ServerGroupsAndResources→ServerGroups 中,代码⽰例如下:

代码路径: vendor/k8s.io/client-go/discovery/discovery_client.go

// ServerGroups返回⽀持的组,以及⽀持的版本和⾸选版本等信息
func (d *DiscoveryClient) ServerGroups() (apiGroupList *metav1.APIGroupList, err error) {
// 获取在/api公开的groupVersions
v := &metav1.APIVersions{}
err = d.restClient.Get().AbsPath(d.LegacyPrefix).Do(context.TODO()).Into(v)
apiGroup := metav1.APIGroup{}
if err == nil && len(v.Versions) != 0 {
apiGroup = apiVersionsToAPIGroup(v)
}
if err != nil && !errors.IsNotFound(err) && !errors.IsForbidden(err) {
return nil, err
}

// 获取在/api公开的groupVersions
apiGroupList = &metav1.APIGroupList{}
err = d.restClient.Get().AbsPath("/apis").Do(context.TODO()).Into(apiGroupList)
if err != nil && !errors.IsNotFound(err) && !errors.IsForbidden(err) {
return nil, err
}
// 为了与v1.0服务器兼容,如果它是403或404,忽略并返回我们从/api得到的内容
if err != nil && (errors.IsNotFound(err) || errors.IsForbidden(err)) {
apiGroupList = &metav1.APIGroupList{}
}

// 如果不是空的,将从/api中检索到的组前置到列表中
if len(v.Versions) != 0 {
apiGroupList.Groups = append([]metav1.APIGroup{apiGroup}, apiGroupList.Groups...)
}
return apiGroupList, nil
}

⾸先, DiscoveryClient 通过 RESTClient 请求 /api 接⼝,将请求结果存放于 metav1.APIVersions 结构体中。然后,再次通过 RESTClient 请求 /apis 接⼝,将请求结果存放于 metav1.APIGroupList 结构体中。最后,将 /api 接⼝中检索到的资源组信息合并到 apiGroupList 列表中并返回

本地缓存的 DiscoveryClient

DiscoveryClient 可以将资源相关信息存储于本地,默认存储位置为 〜/.kube/cache〜/.kube/http-cache 。缓存可以减轻 client-goKubernetes API Server 的访问压⼒。默认每 10 分钟Kubernetes API Server 同步⼀次,同步周期较⻓,因为资源组、源版本、资源信息⼀般很少变动。本地缓存的 DiscoveryClient

DiscoveryClient 第⼀次获取资源组、资源版本、资源信息时,⾸先会查询本地缓存,如果数据不存在(没有命中)则请求 Kubernetes API Server 接⼝(回源), CacheKubernetes API Server 响应的数据存储在本地⼀份并返回给 DiscoveryClient 。当下⼀次 DiscoveryClient 再次获取资源信息时,会将数据直接从本地缓存返回(命中)给 DiscoveryClient 。本地缓存的默认存储周期为 10 分钟。代码⽰例如下

![DiscoveryClient.png](/img/Client-go 四种客户端交互对象/DiscoveryClient.png)

代码路径: vendor/k8s.io/client-go/discovery/cached/disk/cached_discovery.go

Client-go 源码结构及 kubeconfig 配置管理对象

· 阅读需 4 分钟

Client-go 源码结构及 kubeconfig 配置管理对象

Github: https://github.com/kubernetes/client-go

目录结构:

目录结构含义
.githubpull请求的模版
applyconfigurationsapplyconfigurations 提供了⽤于构造服务器端应⽤请求的应⽤配置的类型安全的 go
discovery提供DiscoveryClient发现客户端
dynamic提供DynamicClient动态客户端
examples此⽬录包含涵盖 client-go 的各种⽤例和功能的⽰例。
informers每种kubernetes资源的informer实现
kubernetes提供ClientSet客户端
kubernetes_test提供ClientSet客户端的测试
listers为每⼀个kubernetes资源提供Listers功能,该功能对Get和List请求提供只读的缓存数据
metadata包含关于client-go的元数据信息
pkg客户端认证及版本的包
plugin/pkg/client/auth认证插件包auth
rest提供RestClient客户端,对kubernetes API Server执⾏Rest API操作
restmapper映射器的⼀些⽅法
scale提供scaleclient客户端,⽤于扩容和缩容deployment、replicaset、replication controller等资源对象
testing⼀些测试⽂件
third_party/forked/golang⼀些第三⽅的库
tools提供常⽤⼯具,例如sharedinformer、Reflector、DealtFIFO及Indexers。提供Client查询和缓存机制,以减少向kube-apiserv
transport提供安全的TCP连接,⽀持HTTP STREAM,某些操作需要在客户端和容器之间传输⼆进制流,例如exec、attach等操作
util提供常⽤⽅法,例如workqueue⼯作队列,certificate证书管理等

kubeconfig 配置管理对象

kubeconfig ⽤于管理访问 kube-apiserver 的配置信息,同时也⽀持访问多 kube-apiserver 的配置管理,可以在不同的环境下管理不 同的 kube-apiserver 集群配置,不同的业务线也可以拥有不同的集群。 Kubernetes 的其他组件都使⽤ kubeconfig 配置信息来连接 kube-apiserver 组件,例如当 kubectl 访问 kube-apiserver 时,会默认加载 kubeconfig 配置信息。

kubeconfig 中存储了集群、⽤户、命名空间和⾝份验证等信息,在默认的情况下, kubeconfig 存放在 $HOME/.kube/config 路径下。

Kubeconfig 配置信息如下:

apiVersion: v1 
clusters:
- cluster:
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM1ekNDQWMrZ0F3... - 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: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURJVENDQWdtZ0F3SUJBZ...
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVBL1VB...

kubeconfig 配置信息通常包含 3 个部分,分别介绍如下。

  • clusters :定义 Kubernetes 集群信息,例如 kube-apiserver 的服务地址及集群的证书信息等。

  • users :定义 Kubernetes 集群⽤户⾝份验证的客户端凭据,例如 client-certificate、client-key、token 及 username/password 等。

  • contexts :定义 Kubernetes 集群⽤户信息和命名空间等,⽤于将请求发送到指定的集群。 client-go 会读取 kubeconfig 配置信息并⽣成 config 对象,⽤于与 kube-apiserver 通信,代码⽰例如下:

package main 
import ( "
k8s.io/client-go/tools/clientcmd"
)
func main() {
config, err := clientcmd.BuildConfigFromFlags("", "/root/.kube/config")
if err != nil {
panic (err)
...

在上述代码中, clientcmd.BuildConfigFromFlags 函数会读取 kubeconfig 配置信息并实例化 rest.Config 对象。其中 kubeconfig 最核⼼ 的功能是管理多个访问 kube-apiserver 集群的配置信息,将多个配置信息合并( merge )成⼀份,在合并的过程中会解决多个配置 ⽂件字段冲突的问题。

该过程由 Load 函数完成,可分为两步:

  • 第1步,加载 kubeconfig 配置信息;

  • 第2步,合并多个 kubeconfig 配置信息。代码⽰例如下。

一、加载 kubeconfig 配置信息

vendor/k8s.io/client-go/tools/clientcmd/loader.go

⼆、合并多个 kubeconfig 配置信息

有两份 kubeconfig 配置信息,集群分别为 cow-clusterpig-cluster ,经过合并后,最终得到⼀份多集群的配置信息

vendor/k8s.io/client-go/tools/clientcmd/loader.go

//由于值会被覆盖,但映射值不会被覆盖,所以我们可以将⾮映射配置合并到映射配置之上,并获得我们所期望的值。 config := clientcmdapi.NewConfig()
mergo.Merge(config, mapConfig, mergo.WithOverride)
mergo.Merge(config, nonMapConfig, mergo.WithOverride)

mergo.MergeWithOverwrite 函数将 src 字段填充到 dst 结构中,私有字段除外,⾮空的 dst 字段将被覆盖。另外, dstsrc 必须拥 有有效的相同类型结构。合并过程举例如下:

src 结构: T {X: "two", Z: Z{A: "three", B: 4}} 
dst 结构: T {X: "One", Y:5, Z{A: "four", B: 6}}

merge后的结构: T {X: "two", Y:5, Z{A: "three", B: 4}}