Go Client

概述

参考:

Go Client 就是 Kubernetes 针对 Go 编程语言 而言的 Client Library。Go Client 项目名称为 client-go,是用来与 Kubernetes 对话的 Go 编程语言 的第三方库。

安装 client-go

版本控制策略:k8s 版本 1.18.8 对应 client-go 版本 0.18.8,其他版本以此类推。

使用前注意事项: 使用 client-go 之前,需要手动获取对应版本的的 client-go 库。

如果使用的 Kubernetes 版本> = v1.17.0,请使用相应的 v0.x.y标签。例如,k8s.io/client-go@v0.17.0对应于 Kubernetes v1.17.0

根据版本控制策略,使用如下命令进行初始化:

# 初始化项目
go mod init github.com/DesistDaydream/kubernetes-development
# 为 go.mod 文件添加 require k8s.io/client-go v0.19.2 // indirect 信息
go get k8s.io/client-go@v0.19.2
# 整理 go.mod 与 go.sum 文件
go mod tidy

至此,才可以正常使用,否则会产生很多依赖问题

Hello World 示例

参考:阳明的文章

package main

import (
 "context"
 "fmt"
 "os"

 "k8s.io/client-go/kubernetes"
 "k8s.io/client-go/rest"
 "k8s.io/client-go/tools/clientcmd"

 v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// InOrOut 判断当前环境是在集群内部,还是集群外部
func InOrOut() string {
 // 如果容器内具有环境变量 KUBERNETES_SERVICE_HOST 且不为空,则当前代码是在容器内运行,否则是在集群外部运行
 if h := os.Getenv("KUBERNETES_SERVICE_HOST"); h != "" {
  return "inCluster"
 }
 return "outCluster"
}

// Deployment 获取指定 namespace 下所有的 deployment 对象
func get(clientset *kubernetes.Clientset, namespace string) {
 // 获取指定 名称空间 下所有的 deployment 对象
 deployments, _ := clientset.AppsV1().Deployments(namespace).List(context.TODO(), v1.ListOptions{})
 for i, deploy := range deployments.Items {
  fmt.Printf("%d -> %s\n", i+1, deploy.Name)
 }
}

func main() {
 var config *rest.Config
 // 根据代码所在环境,决定如何创建一个连接集群所需的配置。
 switch InOrOut() {
 case "inCluster":
  // 根据容器内的 /var/run/secrets/kubernetes.io/serviceaccount/ 目录下的 token 与 ca.crt 文件创建一个用于连接集群的配置。
  config, _ = rest.InClusterConfig()
 case "outCluster":
  // 根据指定的 kubeconfig 文件创建一个用于连接集群的配置,/root/.kube/config 为 kubectl 命令所用的 config 文件
  config, _ = clientcmd.BuildConfigFromFlags("", "/root/.kube/config")
  // 注意,clientcmd.BuildConfigFromFlags() 内部实际上也是有调用 rest.InClusterConfig() 的逻辑,只要满足条件即可。条件如下:
  // 若第二个参数为空的话,则会直接调用 rest.InClusterConfig()
 }

 // 根据 BuildConfigFromFlags 创建的配置,返回一个可以连接集群的指针
 clientset, _ := kubernetes.NewForConfig(config)

 // 获取指定 namespace 下所有的 deployment 对象
 get(clientset, "kube-system")
}

示例详解

从上面的示例中可以看到,通过 API 访问 Kubernetes 集群大体分为两类

  • 从集群内部访问
  • 从集群外部访问

所谓的从集群内部访问,其实就是代码运行在 pod 中。不过,不管是集群内还是集群外,至少需要一个配置信息来连接集群,而两种访问方式的唯一区别也在于此,不同的访问方式,获取到的配置信息是不同的。

  • 但是,不管如何获得,这个配置信息实际上就是 k8s.io/client-go/rest 包中的 *rest.Config 结构体。这个结构体中包含了待连接到 Kubernetes 集群的各种信息。
  • 然后,使用 *rest.Config 来实例化 *kubernetes.Clientset,这个 Clientset 就是用来通过 API 来操作 Kubernetes 的客户端集。这个 Clientset 实际上是 Kubernetes API 的集合。
type Clientset struct {
 *discovery.DiscoveryClient
 admissionregistrationV1      *admissionregistrationv1.AdmissionregistrationV1Client
 admissionregistrationV1beta1 *admissionregistrationv1beta1.AdmissionregistrationV1beta1Client
 appsV1                       *appsv1.AppsV1Client
 appsV1beta1                  *appsv1beta1.AppsV1beta1Client
 appsV1beta2                  *appsv1beta2.AppsV1beta2Client
 authenticationV1             *authenticationv1.AuthenticationV1Client
 authenticationV1beta1        *authenticationv1beta1.AuthenticationV1beta1Client
 authorizationV1              *authorizationv1.AuthorizationV1Client
 authorizationV1beta1         *authorizationv1beta1.AuthorizationV1beta1Client
 autoscalingV1                *autoscalingv1.AutoscalingV1Client
 autoscalingV2beta1           *autoscalingv2beta1.AutoscalingV2beta1Client
 autoscalingV2beta2           *autoscalingv2beta2.AutoscalingV2beta2Client
 batchV1                      *batchv1.BatchV1Client
 batchV1beta1                 *batchv1beta1.BatchV1beta1Client
 batchV2alpha1                *batchv2alpha1.BatchV2alpha1Client
 certificatesV1               *certificatesv1.CertificatesV1Client
 certificatesV1beta1          *certificatesv1beta1.CertificatesV1beta1Client
 coordinationV1beta1          *coordinationv1beta1.CoordinationV1beta1Client
 coordinationV1               *coordinationv1.CoordinationV1Client
 coreV1                       *corev1.CoreV1Client
 discoveryV1alpha1            *discoveryv1alpha1.DiscoveryV1alpha1Client
 discoveryV1beta1             *discoveryv1beta1.DiscoveryV1beta1Client
 eventsV1                     *eventsv1.EventsV1Client
 eventsV1beta1                *eventsv1beta1.EventsV1beta1Client
 extensionsV1beta1            *extensionsv1beta1.ExtensionsV1beta1Client
 flowcontrolV1alpha1          *flowcontrolv1alpha1.FlowcontrolV1alpha1Client
 networkingV1                 *networkingv1.NetworkingV1Client
 networkingV1beta1            *networkingv1beta1.NetworkingV1beta1Client
 nodeV1alpha1                 *nodev1alpha1.NodeV1alpha1Client
 nodeV1beta1                  *nodev1beta1.NodeV1beta1Client
 policyV1beta1                *policyv1beta1.PolicyV1beta1Client
 rbacV1                       *rbacv1.RbacV1Client
 rbacV1beta1                  *rbacv1beta1.RbacV1beta1Client
 rbacV1alpha1                 *rbacv1alpha1.RbacV1alpha1Client
 schedulingV1alpha1           *schedulingv1alpha1.SchedulingV1alpha1Client
 schedulingV1beta1            *schedulingv1beta1.SchedulingV1beta1Client
 schedulingV1                 *schedulingv1.SchedulingV1Client
 settingsV1alpha1             *settingsv1alpha1.SettingsV1alpha1Client
 storageV1beta1               *storagev1beta1.StorageV1beta1Client
 storageV1                    *storagev1.StorageV1Client
 storageV1alpha1              *storagev1alpha1.StorageV1alpha1Client
}

在集群内部生成配置信息

使用 rest.InClusterConfig() 方法生成 *rest.Config。使用该方法时,一般通过 /var/run/secrets/kubernetes.io/serviceaccount/ 目录下的 token 与 ca.crt 文件生成认证信息。通过容器内的环境变量生成待连接的 Server 端信息。

func InClusterConfig() (*Config, error) {
 const (
  tokenFile  = "/var/run/secrets/kubernetes.io/serviceaccount/token"
  rootCAFile = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
 )
 host, port := os.Getenv("KUBERNETES_SERVICE_HOST"), os.Getenv("KUBERNETES_SERVICE_PORT")
 token, err := ioutil.ReadFile(tokenFile)
 tlsClientConfig := TLSClientConfig{}
 if _, err := certutil.NewPool(rootCAFile); err != nil {
  klog.Errorf("Expected to load root CA config from %s, but got err: %v", rootCAFile, err)
 } else {
  tlsClientConfig.CAFile = rootCAFile
 }
 return &Config{
  // TODO: switch to using cluster DNS.
  Host:            "https://" + net.JoinHostPort(host, port),
  TLSClientConfig: tlsClientConfig,
  BearerToken:     string(token),
  BearerTokenFile: tokenFile,
 }, nil
}

在集群外部生成配置信息

使用 clientcmd.BuildConfigFromFlags() 方法生成 *rest.Config。使用该方法时,直接将 kubectl 的配置文件,当作参数传递进去即可。

只不过有一点需要注意,就是当参数都为空时,该方法还是会调用的 rest.InClusterConfig() 方法。

func BuildConfigFromFlags(masterUrl, kubeconfigPath string) (*restclient.Config, error) {
 if kubeconfigPath == "" && masterUrl == "" {
  kubeconfig, err := restclient.InClusterConfig()
 }
 return NewNonInteractiveDeferredLoadingClientConfig(
  &ClientConfigLoadingRules{ExplicitPath: kubeconfigPath},
  &ConfigOverrides{ClusterInfo: clientcmdapi.Cluster{Server: masterUrl}}).ClientConfig()
}