Vis/skjul meny Yingchi Blog

client-go 初步认识与实践

最近本人的一个容器应用管理平台项目需要实现对接 Kubernetes 平台并进行一些相关资源的操作,查阅了官方文档、GitHub 以及相关技术文章,发现有个叫做 client-go 的 go 语言库是非常适合做 Kubernetes 二次开发的,于是就边实践,边学习,对 client-go 这个库有了一定程度的了解。对于其中比较复杂的设计,如 informer 部分,之后有时间的话会结合 kube-controller-manager 相关机制的研究学习过程加以介绍分享。

client-go 是 Kubernetes 项目所采用的编程式交互客户端库,官方从2016年8月份开始,资源交互操作相关的核心源码,也就是 client-go 抽取出来,独立出来作为一个项目。也就是现在所用到的 Kubernetes 内部都是集成有 client-go 的,因此对于这个库的编码质量应该是值得放心的。

client-go 所谓编程式交互客户端库说白了就是可以通过写一些 Go 代码实现对kubernetes集群中资源对象(包括deployment、service、ingress、replicaSet、pod、namespace、node等)的增删改查操作。

源码简介

源码目录简述

  • discovery:通过Kubernetes API 进行服务发现;
  • kubernetes:提供 ClientSet 客户端,可以对 Kubernetes 内置资源对象进行操作;
  • dynamic:提供 DynamicClient 客户端,可以实现对任意 Kubernetes 资源对象操作;
  • rest:提供 RESTClient 客户端,可以实现对 kube-apiserver 执行 REST 请求实现资源操作;
  • scale:提供 ScaleClient 客户端,主要用于 Deployment 等资源的扩缩容;
  • listers:为 Kubernetes 资源提供 Lister 功能,对 Get / List 请求提供只读的缓存数据;
  • informers:提供每种 Kubernetes 资源的 Informer 实现;
  • transport:用于提供安全的 TCP 连接;
  • tools/cache:提供常用工具;提供 Client 查询和缓存机制,以缓解 kube-apiserver 压力;
  • util:提供常用方法;

Client 对象

学习 client-go 进行 kubernetes 二次开发的很大一部分工作是学会熟练使用它的几种 client,client-go 有如下 4 种 client 客户端对象,通过 kubeconfig 配置信息连接到指定集群的 kube-apiserver 从而实现对于资源的相关操作。

  • RESTClientclient-go 中最基础的客户端,其它 client 都基于 RESTClient 实现,RESTClient 实现了 RESTful 风格的 API 请求封装,可以实现对任意 Kubernetes 资源(包括内置资源及 CRDs)的 RESTful 风格交互,如 Post() / Delete() / Put() / Get(),同时支持 Json 和 protobuf;
  • ClientSet与 Kubernetes 内置资源对象交互最常用的 Client,强调,只能处理 Kubernetes 内置资源,不包括 CRD 自定义资源,使用时需要指定 Group、指定 Version,然后根据 Resource 获取。ClientSet 的操作代码是通过 client-gen 代码生成器自动生成的;
  • DynamicClientDynamicClient 能处理包括 CRD 自定义资源在内的任意 kubernetes 资源。但是要注意,DynamicClient 返回的对象是一个 map[string]interface{},如果一个 controller 中需要控制所有的 API,可以使用dynamic client,DynamicClient 只支持JSON;
  • DiscoveryClient:用于发现 kube-apiserver 支持的 Group / Version / Resource 信息;

client-go 客户端初始化

kubeconfig 配置信息

client-go 想要访问 kube-apiserver 进行交互操作,首先要配置相关的连接及身份认证等信息,配置信息由 kubeconfig 文件提供,默认情况下集群的 kubeconfig 文件存放路径在:

$HOME/.kube/config

以本人的测试集群的 kubeconfig 文件为例:

apiVersion: v1
kind: Config
clusters:
  - cluster:
      name: kubernetes
      server: https://node01:6443
      certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJ...
contexts:
  - context:
      cluster: kubernetes
      user: kubernetes-admin
      name: kubernetes-admin@kubernetes
      current-context: kubernetes-admin@kubernetes
users:
  - name: kubernetes-admin
    user:
      client-certificate-data: LS0tLS1CRUdJTiBDM4akND...
      client-key-data: LS0tLS1CFURSBLRVktLS0tLQpNS...

其中主要包含了如下信息:

  • apiVersion:配置文件资源的版本
  • kind:配置文件资源的种类,即 Config
  • clusters:定义 Kubernetes 集群相关信息
    • cluster:定义每一个集群的名称、kube-apiserver 地址、证书信息;
  • contexts:集群上下文环境
    • context:定义具体每个集群的命令空间及用户信息,用于将请求发送到指定的集群;
  • users:定义用户身份验证信息,客户端凭据;

client-go 读取 kubeconfig 配置信息生成 config 对象:

...
config, err := clientcmd.BuildConfigFromFlags("","./configs/kubeconfig.conf")
...
kubeconfig, _ = ioutil.ReadFile("./configs/kubeconfig.conf")
restConf, _ = clientcmd.RESTConfigFromKubeConfig(kubeconfig)

由 config 对象进一步生成需要的 client 对象,之后才能与 kube-apiserver 通信进行资源的交互操作,每种 client 对象都有自己的生成方式。

常用的 Client 初始化及资源操作示例

介绍一下 RESTClient / ClientSet / DynamicClient 三种 Client 的用法,DiscoveryClient 暂不做介绍

RESTClient

RESTClient 初始化

config, err := clientcmd.BuildConfigFromFlags("","./configs/kubeconfig.conf")
if err != nil{
  panic(err)
}

config.APIPath = "api"
config.GroupVersion = &corev1.SchemeGroupVersion
config.NegotiatedSerializer = scheme.Codecs

restClient, err := rest.RESTClient(config)
if err != nil{
  panic(err)
}

RESTClient 资源操作示例

pods := &corev1.PodList{}
err = restClient.Get()
                .Namespace("yingchi")
                .Resource("pods")
								.VersionedParams(&metav1.ListOptions{}, scheme.ParameterCodec)
                .Do()
                .Into(pods)
if err != nil {
  panic(err)
}

ClientSet

client-go 中最常用的 client 对象,原因就是 ClientSet 相比 RESTClient 封装的更加易用,尤其是仅仅是去操作 Kubernetes 内置资源时,ClientSet 应该是首选 Client 对象。ClientSet 在 RESTClient 基础上封装了对 Version 和 Resource 的管理方法,每个 Resource 都可以理解为一个 client,这也是 ClientSet 名称的由来。

ClientSet 初始化

config, err := clientcmd.BuildConfigFromFlags("","./configs/kubeconfig.conf")
if err != nil{
  panic(err)
}

clientSet, err := kubernetes.NewForConfig(config)
if err != nil{
  panic(err)
}

ClientSet 资源操作示例

pods, err := clientSet.CoreV1()
                      .Pods("yingchi")
                      .List(metav1.ListOptions{})
if err != nil{
  panic(err)
}

DynamicClient

DynamicClient 也是基于 RESTClient 封装的一种动态客户端,与 ClientSet 不同的是,DynamicClient 能处理包括 CRD 自定义资源在内的任意 kubernetes 资源

能处理 CRD 的原因是 DynamicClient 内部实现了 Unstructured,用于处理非结构化的或者说未知结构的数据结构,这是处理 CRD 类型资源的关键,而 ClientSet 内部的数据都是结构化的,即知道每种 Resource 和 Version 对应的具体资源数据类型。

需要注意的是,DynamicClient 不是类型安全的,使用 DynamicClient 对资源进行交互时尤其要注意指针问题,操作不当可能会导致程序崩溃。

DynamicClient 初始化

config, err := clientcmd.BuildConfigFromFlags("", "./configs/kubeconfig.conf")
if err != nil{
  panic(err)
}

dynamicClient, err := dynamic.NewForConfig(config)
if err != nil{
  panic(err)
}

DynamicClient 资源操作示例

resource := schema.GroupVersionResource{
  Version: "v1",
  Resource: "pods"
}

obj, err := dynamicClient.Resource(resource)
                        .Namespace("yingchi")
                        .List(metav1.ListOptions{})
if err != nil{
  panic(err)
}

pods := &corev1.PodList{}
err = runtime.DefaultUnstructuredConverter
							.FromUnstructured(obj.UnstructuredContent(), pods)

Informer 机制

Client-go 包中一个相对较为高端的设计在于 Informer 的设计,如果想要资源交互更优雅的模式,就要用到 Informer 的机制。

通过之前的例子我们知道可以直接通过 Kubernetes API 交互,但是考虑一点就是交互的形式,Informer设计为List/Watch 的方式。Informer 在初始化的时先通过 List 去从 Kubernetes API 中取出资源的全部 object 对象,并同时缓存,然后后面通过 Watch 的机制去监控资源,这样的话,通过 Informer 及其缓存,我们就可以直接和Informer 交互而不是每次都和 Kubernetes API 交互,避免了轮询 kube-apiserver 等方式对 apiserver 带来的访问压力。

Informer 提供了事件 handler 机制,并会触发回调,这样上层应用如 Controller 就可以基于异步回调的方式进行具体业务逻辑的处理。因为 Informer 通过 List、Watch 机制可以监控到所有资源的所有事件,因此只要给Informe r添加 ResourceEventHandler 实例的回调函数实例取实现 OnAdd(obj interface{}) OnUpdate(oldObj, newObj interface{})OnDelete(obj interface{})这三个方法,就可以处理好资源的创建、更新和删除操作。Kubernetes 中各种controller都会用到Informer。

Informer 是 client-go 中比较精髓的

参考