client-go实战之四:dynamicClient

本篇概览

  • 本文是《client-go实战》系列的第四篇,前文咱们学习了Clientset客户端,发现Clientset在deployment、service这些kubernetes内置资源的时候是很方便的,每个资源都有其专属的方法,配合官方API文档和数据结构定义,开发起来比Restclient高效;

  • 但如果要处理的不是kubernetes的内置资源呢?比如CRD,Clientset的代码中可没有用户自定义的东西,显然就用不上Clientset了,此时本篇的主角dynamicClient就要登场啦!

相关知识储备

  • 在正式学习dynamicClient之前,有两个重要的知识点需要了解:Object.runtime和Unstructured,对于整个kubernetes来说它们都是非常重要的;

Object.runtime

  • 聊Object.runtime之前先要明确两个概念:资源和资源对象,关于资源大家都很熟悉了,pod、deployment这些不都是资源嘛,个人的理解是资源更像一个严格的定义,当您在kubernetes中创建了一个deployment之后,这个新建的deployment实例就是资源对象了;

  • 在kubernetes的代码世界中,资源对象对应着具体的数据结构,这些数据结构都实现了同一个接口,名为Object.runtime,源码位置是staging/src/k8s.io/apimachinery/pkg/runtime/interfaces.go,定义如下:

type Object interface {

GetObjectKind() schema.ObjectKind

DeepCopyObject() Object

}

  • DeepCopyObject方法顾名思义,就是深拷贝,也就是将内存中的对象克隆出一个新的对象;

  • 至于GetObjectKind方法的作用,相信聪明的您也猜到了:处理Object.runtime类型的变量时,只要调用其GetObjectKind方法就知道它的具体身份了(如deployment,service等);

  • 最后再次强调:资源对象都是Object.runtime的实现;

Unstructured

  • 在聊Unstructured之前,先看一个简单的JSON字符串:

{

“id”: 101,

“name”: “Tom”

}

  • 上述JSON的字段名称和字段值类型都是固定的,因此可以针对性编写一个数据结构来处理它:

type Person struct {

ID int

Name String

}

  • 对于上面的JSON字符串就是结构化数据(Structured Data),这个应该好理解;

  • 与结构化数据相对的就是非结构化数据了(Unstructured Data),在实际的kubernetes环境中,可能会遇到一些无法预知结构的数据,例如前面的JSON字符串中还有第三个字段,字段值的具体内容和类型在编码时并不知晓,而是在真正运行的时候才知道,那么在编码时如何处理呢?相信您会想到用interface{}来表示,实际上client-go也是这么做的,来看Unstructured数据结构的源码,路径是staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured.go:

type Unstructured struct {

// Object is a JSON compatible map with string, float, int, bool, []interface{}, or

// map[string]interface{}

// children.

Object map[string]interface{}

}

  • 显然,上述数据结构定义并不能发挥什么作用,真正重要的是关联的方法,如下图,可见client-go已经为Unstructured准备了丰富的方法,借助这些方法可以灵活的处理非结构化数据:

在这里插入图片描述

重要知识点:Unstructured与资源对象的相互转换

  • 另外还有一个非常重要的知识点:可以用Unstructured实例生成资源对象,也可以用资源对象生成Unstructured实例,这个神奇的能力是unstructuredConverter的FromUnstructured和ToUnstructured方法分别实现的,下面的代码片段展示了如何将Unstructured实例转为PodList实例:

// 实例化一个PodList数据结构,用于接收从unstructObj转换后的结果

podList := &apiv1.PodList{}

// unstructObj

err = runtime.DefaultUnstructuredConverter.FromUnstructured(unstructObj.UnstructuredContent(), podList)

  • 您可能会好奇上述FromUnstructured方法究竟是如何实现转换的,咱们去看下此方法的内部实现,如下图所示,其实也没啥悬念了,通过反射可以得到podList的字段信息:

在这里插入图片描述

  • 至此,Unstructured的分析就结束了吗?没有,强烈推荐您进入上图红框2中的fromUnstructured方法去看细节,这里面是非常精彩的,以podList为例,这是个数据结构,而fromUnstructured只处理原始类型,对于数据结构会调用structFromUnstructured方法处理,在structFromUnstructured方法中

处理数据结构的每个字段,又会调用fromUnstructured,这是相互迭代的过程,最终,不论podList中有多少数据结构的嵌套都会被处理掉,篇幅所限就不展开相信分析了,下图是一部分关键代码:

在这里插入图片描述

  • 小结:Unstructured转为资源对象的套路并不神秘,无非是用反射取得资源对象的字段类型,然后按照字段名去Unstructured的map中取得原始数据,再用反射设置到资源对象的字段中即可;

  • 做完了准备工作,接下来该回到本篇文章的主题了:dynamicClient客户端

关于dynamicClient

  • deployment、pod这些资源,其数据结构是明确的固定的,可以精确对应到Clientset中的数据结构和方法,但是对于CRD(用户自定义资源),Clientset客户端就无能为力了,此时需要有一种数据结构来承载资源对象的数据,也要有对应的方法来处理这些数据;

  • 此刻,前面提到的Unstructured可以登场了,没错,把Clientset不支持的资源对象交给Unstructured来承载,接下来看看dynamicClient和Unstructured的关系:

  • 先看数据结构定义,和clientset没啥区别,只有个restClient字段:

type dynamicClient struct {

client *rest.RESTClient

}

  • 这个数据结构只有一个关联方法Resource,入参为GVR,返回的是另一个数据结构dynamicResourceClient:

func (c *dynamicClient) Resource(resource schema.GroupVersionResource) NamespaceableResourceInterface {

return &dynamicResourceClient{client: c, resource: resource}

}

  • 通过上述代码可知,dynamicClient的关键是数据结构dynamicResourceClient及其关联方法,来看看这个dynamicResourceClient,如下图,果然,dynamicClient所有和资源相关的操作都是dynamicResourceClient在做(代理模式?),选了create方法细看,序列化和反序列化都交给unstructured的UnstructuredJSONScheme,与kubernetes的交互交给Restclient:

在这里插入图片描述

  • 小结:
  1. 与Clientset不同,dynamicClient为各种类型的资源都提供统一的操作API,资源需要包装为Unstructured数据结构;

  2. 内部使用了Restclient与kubernetes交互;

  • 对dynamicClient的介绍分析就这些吧,可以开始实战了;

需求确认

  • 本次编码实战的需求很简单:查询指定namespace下的所有pod,然后在控制台打印出来,要求用dynamicClient实现;

  • 您可能会问:pod是kubernetes的内置资源,更适合Clientset来操作,而dynamicClient更适合处理CRD不是么?—您说得没错,这里用pod是因为折腾CRD太麻烦了,定义好了还要在kubernetes上发布,于是干脆用pod来代替CRD,反正dynamicClient都能处理,咱们通过实战掌握dynamicClient的用法就行了,以后遇到各种资源都能处理之;

源码下载

  • 本篇实战中的源码可在GitHub下载到,地址和链接信息如下表所示(https://github.com/zq2599/blog_demos):

| 名称 | 链接 | 备注 |

| :-- | :-- | :-- |

| 项目主页 | https://github.com/zq2599/blog_demos | 该项目在GitHub上的主页 |

| git仓库地址(https) | https://github.com/zq2599/blog_demos.git | 该项目源码的仓库地址,https协议 |

| git仓库地址(ssh) | git@github.com:zq2599/blog_demos.git | 该项目源码的仓库地址,ssh协议 |

  • 这个git项目中有多个文件夹,client-go相关的应用在client-go-tutorials文件夹下,如下图红框所示:

在这里插入图片描述

  • client-go-tutorials文件夹下有多个子文件夹,本篇对应的源码在dynamicclientdemo目录下,如下图红框所示:

在这里插入图片描述

编码

  • 新建文件夹dynamicclientdemo,在里面执行以下命令,新建module:

go mod init dynamicclientdemo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值