
使用 Clientset 获取 Kubernetes 资源对象
作者 |阳明
来源 | k8s技术圈(ID:kube100)
本节主要讲解 Kubernetes 核心的资源类型 Scheme 的定义以及如何使用 Clientset 来获取集群资源对象。
介绍
当我们操作资源和 apiserver 进行通信的时候,需要根据资源对象类型的 Group、Version、Kind 以及规范定义、编解码等内容构成 Scheme 类型,然后 Clientset 对象就可以来访问和操作这些资源类型了,Scheme 的定义主要在 api 子项目之中,源码仓库地址: https://github.com/kubernetes/api ,被同步到 Kubernetes 源码的 staging/src/k8s.io/api 之下。
主要就是各种资源对象的原始结构体定义,比如查看 apps/v1 目录下面的定义:
types.go 文件
其中 types.go 文件里面就是 apps/v1 这个 GroupVersion 下面所有的资源对象的定义,有 Deployment、DaemonSet、StatefulSet、ReplicaSet 等几个资源对象,比如 Deployment 的结构体定义如下所示:
由 TypeMeta、ObjectMeta、DeploymentSpec 以及 DeploymentStatus 4个属性组成,和我们使用 YAML 文件定义的 Deployment 资源对象也是对应的。
其中 apiVersion 与 kind 就是 TypeMeta 属性,metadata 属性就是 ObjectMeta,spec 属性就是 DeploymentSpec,当资源部署过后也会包含一个 status 的属性,也就是 DeploymentStatus ,这样就完整的描述了一个资源对象的模型。
zz_generated.deepcopy.go 文件
上面定义的规范在 Kubernetes 中称为资源类型 Scheme,此外zz_generated.deepcopy.go 文件是由 deepcopy-gen 工具创建的定义各资源类型 DeepCopyObject() 方法的文件,所有注册到 Scheme 的资源类型都要实现 runtime.Object 接口:
而所有的资源类型都包含一个 TypeMeta 类型,而该类型实现了 GetObjectKind() 方法,所以各资源类型只需要实现 DeepCopyObject() 方法即可:
各个资源类型的 DeepCopyObject() 方法也不是手动定义,而是使用 deepcopy-gen 工具命令统一自动生成的,该工具会读取 types.go 文件中的 +k8s:deepcopy-gen 注释,以 Deployment 为例:
然后将自动生成的代码保存到 zz_generated.deepcopy.go 文件中。
register.go 文件
register.go 文件的主要作用是定义 AddToScheme 函数,将各种资源类型注册到 Clientset 使用的 Scheme 对象中去,由于每个资源自动生成了 DeepCopyObject() 方法,这样资源就实现了 runtime.Object 接口,所以可以注册到 Scheme 中去了。
将各类资源类型注册到全局的 Scheme 对象中,这样 Clientset 就可以识别和使用它们了,那么我们应该如何使用 Clientset 呢?
示例
首先我们来看下如何通过 Clientset 来获取资源对象,我们这里来创建一个 Clientset 对象,然后通过该对象来获取默认命名空间之下的 Deployments 列表,代码如下所示:
上面的代码运行可以获得 default 命名空间之下的 Deployments:
这是一个非常典型的访问 Kubernetes 集群资源的方式,通过 client-go 提供的 Clientset 对象来获取资源数据,主要有以下三个步骤:
- 使用 kubeconfig 文件或者 ServiceAccount(InCluster 模式)来创建访问 Kubernetes API 的 Restful 配置参数,也就是代码中的 rest.Config 对象
- 使用 rest.Config 参数创建 Clientset 对象,这一步非常简单,直接调用 kubernetes.NewForConfig(config) 即可初始化
- 然后是 Clientset 对象的方法去获取各个 Group 下面的对应资源对象进行 CRUD 操作
Clientset 对象
上面我们了解了如何使用 Clientset 对象来获取集群资源,接下来我们来分析下 Clientset 对象的实现。
上面我们使用的 Clientset 实际上是对各种资源类型的 Clientset 的一次封装:
上面的 NewForConfig 函数里面就是将其他的各种资源的 RESTClient 封装到了全局的 Clientset 中,这样当我们需要访问某个资源的时候只需要使用 Clientset 里面包装的属性即可,比如 clientset.CoreV1() 就是访问 Core 这个 Group 下面 v1 这个版本的 RESTClient。这些局部的 RESTClient 都定义在 staging/src/k8s.io/client-go/typed///_client.go 文件中,比如 staging/src/k8s.io/client-go/kubernetes/typed/apps/v1/apps_client.go 这个文件中就是定义的 apps 这个 Group 下面的 v1 版本的 RESTClient,这里同样以 Deployment 为例:
通过上面代码我们就可以很清晰的知道可以通过 clientset.AppsV1().Deployments("default")来获取一个 deployments 对象,然后该对象下面定义了 deployments 对象的 CRUD 操作,比如我们调用的 List() 函数:
从上面代码可以看出最终是通过 c.client 去发起的请求,也就是局部的 restClient 初始化的函数中通过 rest.RESTClientFor(&config) 创建的对象,也就是将 rest.Config 对象转换成一个 Restful 的 Client 对象用于网络操作:
到这里我们就知道了 Clientset 是基于 RESTClient 的,RESTClient 是底层的用于网络请求的对象,可以直接通过 RESTClient 提供的 RESTful 方法如 Get()、Put()、Post()、Delete() 等和 APIServer 进行交互:
- 同时支持 JSON 和 protobuf 两种序列化方式
- 支持所有原生资源
但实际上除了常用的 CRUD 操作之外,我们还可以进行 Watch 操作,可以监听资源对象的增、删、改、查操作,这样我们就可以根据自己的业务逻辑去处理这些数据了,但是实际上也并不建议这样使用,因为往往由于集群中的资源较多,我们需要自己在客户端去维护一套缓存,而这个维护成本也是非常大的,为此 client-go 也提供了自己的实现机制,那就是 Informers。Informers 是这个事件接口和带索引查找功能的内存缓存的组合,这样也是目前最常用的用法。Informers 第一次被调用的时候会首先在客户端调用 List 来获取全量的对象集合,然后通过 Watch 来获取增量的对象更新缓存,这个我们后续在讲解。
