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\t status\t\t name\n")

	// 每個pod都打印namespace、status.Phase、name三個字段
	for _, d := range result.Items {
		fmt.Printf("%v\t %v\t %v\n",
			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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM