kubernetes提供了rest api供我们操作集群,针对go语言专门提供了client-go来操作,并且在很多controller中都使用到了client-go,本文将介绍client-go简单使用

kubernetes api介绍

kubernetes通过http rest协议向外暴露api,各组件间和kubectl都是通过rest api调用api-server,并在rest api中使用多个api版本进行版本控制,每个版本有不同的api路径,例如:/api/v1/api/extension/v1beta1

api-group

kubernetes 的api是按照api-group进行分组的,各api在不同的组下:

core: 核心组,kubernetes早期就存在或者必须的资源,其路径为 /api/v1,对应的yaml为:apiVersion: v1

其他组: 核心组意外的资源,路径为/apis/Group_Name/Version,yaml为apiVersion: GroupName/Version,例如Deployment其apiVersion: apps/v1,那么其Rest地址为/apis/apps/v1/namespaces/{namespace}/deployments

client-go

kubernetes为各主流语言提供了client方便了我们使用(不用直接使用rest api调用,并且封装了资源的建模序列化等过程),本文以go语言的client为例,简单说明调用方式.

1,创建项目,初始化依赖

#创建目录
$ mkdir client-go-test && cd client-go-test
#初始化项目
$ go mod init client-go-test
# 获取依赖
$ go get k8s.io/client-go/...
# 修正依赖
$ go get k8s.io/client-go@v7.0.0

在此处修正依赖关系是因为 go get到的是最新的包,但是client-go是需要根据k8s的版本来使用的,详细的版本关系请参照 client-go 兼容性矩阵 ,也可以直接使用 示例go.mod

2,读取连接配置

kubernetes是通过api-server的端口+证书/token进行连接的,所以我们需要先读取kubeconfig配置

//kubelet.kubeconfig  是文件对应地址
	kubeconfig := flag.String("kubeconfig", "/home/tangxu/.kube/config", "(optional) absolute path to the kubeconfig file")
	flag.Parse()

	// 解析到config
	config, err := clientcmd.BuildConfigFromFlags("10.30.21.238:6443", *kubeconfig)
	if err != nil {
		panic(err.Error())
	}

3,建立clientset

kubernetes的资源是按照api-group分割的,在client-go中体现的十分突出(就像腰间盘那样)

clientset, err := kubernetes.NewForConfig(config)

其中的返回值 clientSet的声明如下:

// Clientset contains the clients for groups. Each group has exactly one
// version included in a Clientset.
type Clientset struct {
	*discovery.DiscoveryClient
	admissionregistrationV1alpha1 *admissionregistrationv1alpha1.AdmissionregistrationV1alpha1Client
	admissionregistrationV1beta1  *admissionregistrationv1beta1.AdmissionregistrationV1beta1Client
	appsV1beta1                   *appsv1beta1.AppsV1beta1Client
	appsV1beta2                   *appsv1beta2.AppsV1beta2Client
	appsV1                        *appsv1.AppsV1Client
	authenticationV1              *authenticationv1.AuthenticationV1Client
	authenticationV1beta1         *authenticationv1beta1.AuthenticationV1beta1Client
	authorizationV1               *authorizationv1.AuthorizationV1Client
	authorizationV1beta1          *authorizationv1beta1.AuthorizationV1beta1Client
	autoscalingV1                 *autoscalingv1.AutoscalingV1Client
	autoscalingV2beta1            *autoscalingv2beta1.AutoscalingV2beta1Client
	batchV1                       *batchv1.BatchV1Client
	batchV1beta1                  *batchv1beta1.BatchV1beta1Client
	batchV2alpha1                 *batchv2alpha1.BatchV2alpha1Client
	certificatesV1beta1           *certificatesv1beta1.CertificatesV1beta1Client
	coreV1                        *corev1.CoreV1Client
	eventsV1beta1                 *eventsv1beta1.EventsV1beta1Client
	extensionsV1beta1             *extensionsv1beta1.ExtensionsV1beta1Client
	networkingV1                  *networkingv1.NetworkingV1Client
	policyV1beta1                 *policyv1beta1.PolicyV1beta1Client
	rbacV1                        *rbacv1.RbacV1Client
	rbacV1beta1                   *rbacv1beta1.RbacV1beta1Client
	rbacV1alpha1                  *rbacv1alpha1.RbacV1alpha1Client
	schedulingV1alpha1            *schedulingv1alpha1.SchedulingV1alpha1Client
	settingsV1alpha1              *settingsv1alpha1.SettingsV1alpha1Client
	storageV1beta1                *storagev1beta1.StorageV1beta1Client
	storageV1                     *storagev1.StorageV1Client
	storageV1alpha1               *storagev1alpha1.StorageV1alpha1Client
}

可以看到clientset其实就是原生的资源的集合,通过group来进行区分,简单使用如下:

deploymentsClient := clientset.AppsV1beta1().Deployments("kube-system")
	deploymentList, err := deploymentsClient.List(v1.ListOptions{})
	if err != nil {
		panic(err.Error())
	}
	for key, value := range deploymentList.Items {
		fmt.Println("deployment", key)
		bytes, err := json.Marshal(value)
		if err != nil {
			println(err.Error())
			return
		}
		fmt.Println("内容为", string(bytes))
	}
	fmt.Println("============获取pod==============")

	pods := clientset.CoreV1().Pods("kube-system")
	podList, err := pods.List(v1.ListOptions{})
	if err != nil {
		println(err.Error())
		return
	}

	for key, value := range podList.Items {
		fmt.Println("第", key, "个pod.................")
		bytes, err := json.Marshal(value)
		if err != nil {
			return
		}
		fmt.Println(string(bytes))
	}

从上面的代码可以看到整体的调用非常简单,获取deployment也非常简单,不过写着写着就发现了一个问题,如何获取关联资源?

4,关联资源获取

我们都知道在kubernetes中deployment是依赖ReplicaSet的,ReplicaSet是依赖Pod的,那么怎么获取deployment中的Pod呢?(毕竟我们真的不怎么关心ReplicaSet!)

clientset.CoreV1().Pods(namespace.Name).List(v1.ListOptions{})

请注意上面代码的v1.ListOptions{}这个参数,其声明如下:

// ListOptions is the query options to a standard REST list call.
type ListOptions struct {
	TypeMeta `json:",inline"`

	// A selector to restrict the list of returned objects by their labels.
	// Defaults to everything.
	// +optional
	LabelSelector string `json:"labelSelector,omitempty" protobuf:"bytes,1,opt,name=labelSelector"`
	// A selector to restrict the list of returned objects by their fields.
	// Defaults to everything.
	// +optional
	FieldSelector string `json:"fieldSelector,omitempty" protobuf:"bytes,2,opt,name=fieldSelector"`
... 其他字段省略

deployment的selector字段中的label和pod中的label是统一的,那么我们可以通过此机制来直接获取pod,并且ListOptions也提供了这个参数.

podInterface := client.CoreV1().Pods("kube-system")
	podList, e := podInterface.List(metav1.ListOptions{
		LabelSelector: "app=helm,component=kube-controller-manager,tier=control-plane",
	})
	if e != nil {
		println(e.Error())
	}
	for _, value := range podList.Items {
		bytes, e := json.Marshal(value)
		if e != nil {
			println(e.Error())
			continue
		}
		fmt.Println("pod", value.Name, string(bytes))
	}

通过这样的方式就能直接获取关联的pod了..

参照

client-go文档

kubernetes client library

示例代码

clientcmd package 介绍

集群内通过service account的token和ca.crt连接api-server

client-go示例