• client-go实战之二:RESTClient


    欢迎访问我的GitHub

    https://github.com/zq2599/blog_demos

    内容:所有原创文章分类汇总及配套源码,涉及Java、Docker、Kubernetes、DevOPS等;

    系列文章链接

    1. client-go实战之一:准备工作
    2. client-go实战之二:RESTClient
    3. client-go实战之三:Clientset
    4. client-go实战之四:dynamicClient
    5. client-go实战之五:DiscoveryClient

    本篇概览

    • 本文是《client-go实战》系列的第二篇,前文咱们提到过client-go一共有四种客户端:RESTClient、ClientSet、DynamicClient、DiscoveryClient,而RESTClient是最基础的版本,其他三种都是基于RESTClient封装的,今天咱们通过实战编码来学习RESTClient,熟悉最基础的远程操作步骤;
    • 本篇由以下几部分组成:
    1. 简介RESTClient
    2. 每次编码前的准备工作
    3. 正式编码
    4. 验证
    5. 关键源码分析

    RESTClient简介

    • RESTClient是client-go最基础的客户端,主要是对HTTP Reqeust进行了封装,对外提供RESTful风格的API,并且提供丰富的API用于各种设置,相比其他几种客户端虽然更复杂,但是也更为灵活;
    • 使用RESTClient对kubernetes的资源进行增删改查的基本步骤如下:
    1. 确定要操作的资源类型(例如查找deployment列表),去官方API文档中找到对于的path、数据结构等信息,后面会用到;
    2. 加载配置kubernetes配置文件(和kubectl使用的那种kubeconfig完全相同);
    3. 根据配置文件生成配置对象,并且通过API对配置对象就行设置(例如请求的path、Group、Version、序列化反序列化工具等);
    4. 创建RESTClient实例,入参是配置对象;
    5. 调用RESTClient实例的方法向kubernetes的API Server发起请求,编码用fluent风格将各种参数传入(例如指定namespace、资源等),如果是查询类请求,还要传入数据结构实例的指针,改数据结构用于接受kubernetes返回的查询结果;
    • 接下来的编码实战也是按照上述流程进行的;

    实战内容

    • 本次实战内容很简单:查询kube-system这个namespace下的所有pod,然后在控制台打印每个pod的几个关键字段;
    • 感谢您耐心听我啰嗦了一大堆,接下来开始实战吧;

    源码下载

    名称 链接 备注
    项目主页 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文件夹下有多个子文件夹,本篇对应的源码在restclientdemo目录下,如下图红框所示:

    在这里插入图片描述

    查看官方文档,获取编码所需内容

    在这里插入图片描述

    • 然后还要关注响应的数据结构,如下图红框,返回的是:

    在这里插入图片描述

    • 点击上图红框中的内容,可见PodList的详情,这就是我们编码时所需的数据结构:

    在这里插入图片描述

    • 掌握了请求和响应的详细信息,可以开始编码了;

    编码

    • 新建文件夹restclientdemo,在里面执行以下命令,新建module:
    go mod init restclientdemo
    
    • 添加k8s.io/api和k8s.io/client-go这两个依赖,注意版本要匹配kubernetes环境:
    go get k8s.io/api@v0.20.0
    go get k8s.io/client-go@v0.20.0
    
    • 新建main.go,内容如下,已经都添加了详细的注释,就不赘述了:
    package main
    
    import (
    	"context"
    	"flag"
    	"fmt"
    	"k8s.io/client-go/kubernetes/scheme"
    	"k8s.io/client-go/rest"
    	"k8s.io/client-go/tools/clientcmd"
    	"k8s.io/client-go/util/homedir"
    	corev1 "k8s.io/api/core/v1"
    	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    	"path/filepath"
    )
    
    func main() {
    	var kubeconfig *string
    
    	// home是家目录,如果能取得家目录的值,就可以用来做默认值
    	if home:=homedir.HomeDir(); home != "" {
    		// 如果输入了kubeconfig参数,该参数的值就是kubeconfig文件的绝对路径,
    		// 如果没有输入kubeconfig参数,就用默认路径~/.kube/config
    		kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
    	} else {
    		// 如果取不到当前用户的家目录,就没办法设置kubeconfig的默认目录了,只能从入参中取
    		kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
    	}
    
    	flag.Parse()
    
    	// 从本机加载kubeconfig配置文件,因此第一个参数为空字符串
    	config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
    
    	// kubeconfig加载失败就直接退出了
    	if err != nil {
    		panic(err.Error())
    	}
    
    	// 参考path : /api/v1/namespaces/{namespace}/pods
    	config.APIPath = "api"
    	// pod的group是空字符串
    	config.GroupVersion = &corev1.SchemeGroupVersion
    	// 指定序列化工具
    	config.NegotiatedSerializer = scheme.Codecs
    
    	// 根据配置信息构建restClient实例
    	restClient, err := rest.RESTClientFor(config)
    
    	if err!=nil {
    		panic(err.Error())
    	}
    
    	// 保存pod结果的数据结构实例
    	result := &corev1.PodList{}
    
    	//  指定namespace
    	namespace := "kube-system"
    	// 设置请求参数,然后发起请求
    	// GET请求
    	err = restClient.Get().
    		//  指定namespace,参考path : /api/v1/namespaces/{namespace}/pods
    		Namespace(namespace).
    		// 查找多个pod,参考path : /api/v1/namespaces/{namespace}/pods
    		Resource("pods").
    		// 指定大小限制和序列化工具
    		VersionedParams(&metav1.ListOptions{Limit:100}, scheme.ParameterCodec).
    		// 请求
    		Do(context.TODO()).
    		// 结果存入result
    		Into(result)
    
    	if err != nil {
    		panic(err.Error())
    	}
    
    	// 表头
    	fmt.Printf("namespace	 status		 name
    ")
    
    	// 每个pod都打印namespace、status.Phase、name三个字段
    	for _, d := range result.Items {
    		fmt.Printf("%v	 %v	 %v
    ",
    			d.Namespace,
    			d.Status.Phase,
    			d.Name)
    	}
    }
    
    
    • 编码完成,执行go run main.go,即可获取指定namespace下所有pod的信息,控制台输出如下:
    (base) zhaoqindeMBP:restclientdemo zhaoqin$ go run main.go
    namespace	 status		 name
    kube-system	 Running	 coredns-7f89b7bc75-5pdwc
    kube-system	 Running	 coredns-7f89b7bc75-nvbvm
    kube-system	 Running	 etcd-hedy
    kube-system	 Running	 kube-apiserver-hedy
    kube-system	 Running	 kube-controller-manager-hedy
    kube-system	 Running	 kube-flannel-ds-v84vc
    kube-system	 Running	 kube-proxy-hlppx
    kube-system	 Running	 kube-scheduler-hedy
    
    • 至此,RESTClient客户端从编码到验证都完成了;

    如何将收到的数据反序列化为PodList对象?

    • 前面的代码比较简单,但是有一处引起了我的兴趣,如下图红框所示,result是corev1.PodList类型的结构体指针,restClient收到kubernetes返回的数据后,如何知道要将数据反序列化成corev1.PodList类型呢(Into方法入参类型为runtime.Object)?

    在这里插入图片描述

    • 之前的代码中有一行设置了编解码工具:config.NegotiatedSerializer = scheme.Codecs,展开这个scheme.Codecs,可见设置的时候确定了序列化工具为runtime.Serializer:

    在这里插入图片描述

    • Serializer的typer字段类型是runtime.ObjectTyper,这里实际上是runtime.Scheme,因此ObjectTyper.ObjectKinds方法,实际上就是Scheme.ObjectKinds方法,在里面根据s.typeToGVK[t]拿到了GVK,也就是v1.PodList:

    在这里插入图片描述

    • 有了这个GVK就确定的返回数据的类型,最终调用caseSensitiveJSONIterator.Unmarshal(data, obj)完成byte数组到对象的反序列化操作:

    在这里插入图片描述

    • 最后还有一行关键代码,将data的内容写到最外层的Into方法的入参中:

    在这里插入图片描述

    • 源码分析完成,简单来说除了利用反射获取实际类型,还有就是Scheme内部维护的数据类型和GVK的关系映射表;
    • 至此,RESTClient的实战就完成了,希望本篇能帮助您打好基础,这样后面在体验其他三种客户端时已对其底层的实现原理了然于胸;

    你不孤单,欣宸原创一路相伴

    1. Java系列
    2. Spring系列
    3. Docker系列
    4. kubernetes系列
    5. 数据库+中间件系列
    6. DevOps系列

    你不孤单,欣宸原创一路相伴

    1. Java系列
    2. Spring系列
    3. Docker系列
    4. kubernetes系列
    5. 数据库+中间件系列
    6. DevOps系列

    欢迎关注公众号:程序员欣宸

    微信搜索「程序员欣宸」,我是欣宸,期待与您一同畅游Java世界...
    https://github.com/zq2599/blog_demos

  • 相关阅读:
    Object.assign()方法
    JavaScript对象(二)
    JavaScript对象(一)
    vue开发中遇到的问题集锦(2)
    Python:str.ljust()、str.rjust()、str.center()函数
    Python:如何将多个小字符串拼接成一个大的字符串
    Python:.join()函数
    Python:生成器表达式
    VS Code:快捷方式
    Python:如何调整字符串中文本的格式
  • 原文地址:https://www.cnblogs.com/bolingcavalry/p/15236581.html
Copyright © 2020-2023  润新知