client-go實(shí)戰(zhàn)之四:dynamicClient

歡迎訪問我的GitHub

https://github.com/zq2599/blog_demos

內(nèi)容:所有原創(chuàng)文章分類匯總及配套源碼,涉及Java哼凯、Docker考榨、Kubernetes吟逝、DevOPS等;

系列文章鏈接

  1. client-go實(shí)戰(zhàn)之一:準(zhǔn)備工作
  2. client-go實(shí)戰(zhàn)之二:RESTClient
  3. client-go實(shí)戰(zhàn)之三:Clientset
  4. client-go實(shí)戰(zhàn)之四:dynamicClient
  5. client-go實(shí)戰(zhàn)之五:DiscoveryClient

本篇概覽

  • 本文是《client-go實(shí)戰(zhàn)》系列的第四篇,前文咱們學(xué)習(xí)了Clientset客戶端役衡,發(fā)現(xiàn)Clientset在deployment、service這些kubernetes內(nèi)置資源的時(shí)候是很方便的薪棒,每個(gè)資源都有其專屬的方法,配合官方API文檔和數(shù)據(jù)結(jié)構(gòu)定義棵介,開發(fā)起來(lái)比Restclient高效吧史;
  • 但如果要處理的不是kubernetes的內(nèi)置資源呢?比如CRD吨述,Clientset的代碼中可沒有用戶自定義的東西,顯然就用不上Clientset了冰啃,此時(shí)本篇的主角dynamicClient就要登場(chǎng)啦阎毅!

相關(guān)知識(shí)儲(chǔ)備

  • 在正式學(xué)習(xí)dynamicClient之前扇调,有兩個(gè)重要的知識(shí)點(diǎn)需要了解:<font color="blue">Object.runtime</font>和<font color="blue">Unstructured</font>,對(duì)于整個(gè)kubernetes來(lái)說(shuō)它們都是非常重要的雌团;

Object.runtime

  • 聊Object.runtime之前先要明確兩個(gè)概念:資源和資源對(duì)象锦援,關(guān)于資源大家都很熟悉了灵寺,pod略板、deployment這些不都是資源嘛慈缔,個(gè)人的理解是資源更像一個(gè)嚴(yán)格的定義瓤檐,當(dāng)您在kubernetes中創(chuàng)建了一個(gè)deployment之后挠蛉,這個(gè)新建的deployment實(shí)例就是資源對(duì)象了谴古;
  • 在kubernetes的代碼世界中掰担,資源對(duì)象對(duì)應(yīng)著具體的數(shù)據(jù)結(jié)構(gòu)带饱,這些數(shù)據(jù)結(jié)構(gòu)都實(shí)現(xiàn)了同一個(gè)接口纠炮,名為<font color="red">Object.runtime</font>,源碼位置是<font color="blue">staging/src/k8s.io/apimachinery/pkg/runtime/interfaces.go</font>穷躁,定義如下:
type Object interface {
    GetObjectKind() schema.ObjectKind
    DeepCopyObject() Object
}
  • DeepCopyObject方法顧名思義猿诸,就是深拷貝梳虽,也就是將內(nèi)存中的對(duì)象克隆出一個(gè)新的對(duì)象窜觉;
  • 至于GetObjectKind方法的作用禀挫,相信聰明的您也猜到了:處理Object.runtime類型的變量時(shí)语婴,只要調(diào)用其GetObjectKind方法就知道它的具體身份了(如deployment砰左,service等)菜职;
  • 最后再次強(qiáng)調(diào):<font color="blue">資源對(duì)象都是Object.runtime的實(shí)現(xiàn)</font>酬核;

Unstructured

  • 在聊Unstructured之前嫡意,先看一個(gè)簡(jiǎn)單的JSON字符串:
{
    "id": 101,
    "name": "Tom"
}
  • 上述JSON的字段名稱和字段值類型都是固定的,因此可以針對(duì)性編寫一個(gè)數(shù)據(jù)結(jié)構(gòu)來(lái)處理它:
type Person struct {
    ID int
    Name String
}
  • 對(duì)于上面的JSON字符串就是結(jié)構(gòu)化數(shù)據(jù)(Structured Data)旧巾,這個(gè)應(yīng)該好理解鲁猩;
  • 與結(jié)構(gòu)化數(shù)據(jù)相對(duì)的就是非結(jié)構(gòu)化數(shù)據(jù)了(Unstructured Data),在實(shí)際的kubernetes環(huán)境中隙券,可能會(huì)遇到一些無(wú)法預(yù)知結(jié)構(gòu)的數(shù)據(jù)娱仔,例如前面的JSON字符串中還有第三個(gè)字段薪铜,字段值的具體內(nèi)容和類型在編碼時(shí)并不知曉隔箍,而是在真正運(yùn)行的時(shí)候才知道蜒滩,那么在編碼時(shí)如何處理呢俯艰?相信您會(huì)想到用<font color="blue">interface{}</font>來(lái)表示,實(shí)際上client-go也是這么做的辆飘,來(lái)看Unstructured數(shù)據(jù)結(jié)構(gòu)的源碼芹关,路徑是<font color="blue">staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured.go</font>:
type Unstructured struct {
    // Object is a JSON compatible map with string, float, int, bool, []interface{}, or
    // map[string]interface{}
    // children.
    Object map[string]interface{}
}
  • 顯然,上述數(shù)據(jù)結(jié)構(gòu)定義并不能發(fā)揮什么作用轴总,真正重要的是關(guān)聯(lián)的方法怀樟,如下圖,可見client-go已經(jīng)為Unstructured準(zhǔn)備了豐富的方法投蝉,借助這些方法可以靈活的處理非結(jié)構(gòu)化數(shù)據(jù):
在這里插入圖片描述

重要知識(shí)點(diǎn):Unstructured與資源對(duì)象的相互轉(zhuǎn)換

  • 另外還有一個(gè)非常重要的知識(shí)點(diǎn):可以用Unstructured實(shí)例生成資源對(duì)象,也可以用資源對(duì)象生成Unstructured實(shí)例庸娱,這個(gè)神奇的能力是unstructuredConverter的FromUnstructured和ToUnstructured方法分別實(shí)現(xiàn)的,下面的代碼片段展示了如何將Unstructured實(shí)例轉(zhuǎn)為PodList實(shí)例:
// 實(shí)例化一個(gè)PodList數(shù)據(jù)結(jié)構(gòu)斤儿,用于接收從unstructObj轉(zhuǎn)換后的結(jié)果
podList := &apiv1.PodList{}

// unstructObj
err = runtime.DefaultUnstructuredConverter.FromUnstructured(unstructObj.UnstructuredContent(), podList)
  • 您可能會(huì)好奇上述FromUnstructured方法究竟是如何實(shí)現(xiàn)轉(zhuǎn)換的,咱們?nèi)タ聪麓朔椒ǖ膬?nèi)部實(shí)現(xiàn)一铅,如下圖所示肮之,其實(shí)也沒啥懸念了局骤,通過(guò)反射可以得到podList的字段信息:
在這里插入圖片描述
  • 至此,Unstructured的分析就結(jié)束了嗎暴凑?沒有峦甩,強(qiáng)烈推薦您進(jìn)入上圖紅框2中的fromUnstructured方法去看細(xì)節(jié),這里面是非常精彩的现喳,以podList為例凯傲,這是個(gè)數(shù)據(jù)結(jié)構(gòu),而fromUnstructured只處理原始類型嗦篱,對(duì)于數(shù)據(jù)結(jié)構(gòu)會(huì)調(diào)用structFromUnstructured方法處理冰单,在structFromUnstructured方法中
    處理數(shù)據(jù)結(jié)構(gòu)的每個(gè)字段,又會(huì)調(diào)用fromUnstructured诫欠,這是相互迭代的過(guò)程被廓,最終蜓斧,不論podList中有多少數(shù)據(jù)結(jié)構(gòu)的嵌套都會(huì)被處理掉狼荞,篇幅所限就不展開相信分析了丰涉,下圖是一部分關(guān)鍵代碼:
在這里插入圖片描述
  • 小結(jié):Unstructured轉(zhuǎn)為資源對(duì)象的套路并不神秘伪煤,無(wú)非是用反射取得資源對(duì)象的字段類型防泵,然后按照字段名去Unstructured的map中取得原始數(shù)據(jù),再用反射設(shè)置到資源對(duì)象的字段中即可;
  • 做完了準(zhǔn)備工作惠桃,接下來(lái)該回到本篇文章的主題了:dynamicClient客戶端

關(guān)于dynamicClient

  • deployment、pod這些資源续膳,其數(shù)據(jù)結(jié)構(gòu)是明確的固定的,可以精確對(duì)應(yīng)到Clientset中的數(shù)據(jù)結(jié)構(gòu)和方法,但是對(duì)于CRD(用戶自定義資源)熙兔,Clientset客戶端就無(wú)能為力了花沉,此時(shí)需要有一種數(shù)據(jù)結(jié)構(gòu)來(lái)承載資源對(duì)象的數(shù)據(jù),也要有對(duì)應(yīng)的方法來(lái)處理這些數(shù)據(jù)碰声;
  • 此刻覆旱,前面提到的Unstructured可以登場(chǎng)了辐马,沒錯(cuò),把Clientset不支持的資源對(duì)象交給Unstructured來(lái)承載诅挑,接下來(lái)看看dynamicClient和Unstructured的關(guān)系:
  • 先看數(shù)據(jù)結(jié)構(gòu)定義,和clientset沒啥區(qū)別泛源,只有個(gè)restClient字段:
type dynamicClient struct {
    client *rest.RESTClient
}
  • 這個(gè)數(shù)據(jù)結(jié)構(gòu)只有一個(gè)關(guān)聯(lián)方法Resource努释,入?yún)镚VR,返回的是另一個(gè)數(shù)據(jù)結(jié)構(gòu)dynamicResourceClient:
func (c *dynamicClient) Resource(resource schema.GroupVersionResource) NamespaceableResourceInterface {
    return &dynamicResourceClient{client: c, resource: resource}
}
  • 通過(guò)上述代碼可知部逮,dynamicClient的關(guān)鍵是數(shù)據(jù)結(jié)構(gòu)dynamicResourceClient及其關(guān)聯(lián)方法享完,來(lái)看看這個(gè)dynamicResourceClient,如下圖有额,果然般又,dynamicClient所有和資源相關(guān)的操作都是dynamicResourceClient在做(代理模式?)巍佑,選了create方法細(xì)看茴迁,序列化和反序列化都交給unstructured的UnstructuredJSONScheme,與kubernetes的交互交給Restclient:
在這里插入圖片描述
  • 小結(jié):
  1. 與Clientset不同萤衰,dynamicClient為各種類型的資源都提供統(tǒng)一的操作API堕义,資源需要包裝為Unstructured數(shù)據(jù)結(jié)構(gòu);
  2. 內(nèi)部使用了Restclient與kubernetes交互脆栋;
  • 對(duì)dynamicClient的介紹分析就這些吧倦卖,可以開始實(shí)戰(zhàn)了;

需求確認(rèn)

  • 本次編碼實(shí)戰(zhàn)的需求很簡(jiǎn)單:查詢指定namespace下的所有pod椿争,然后在控制臺(tái)打印出來(lái)怕膛,要求用dynamicClient實(shí)現(xiàn);
  • 您可能會(huì)問:pod是kubernetes的內(nèi)置資源秦踪,更適合Clientset來(lái)操作褐捻,而dynamicClient更適合處理CRD不是么?---您說(shuō)得沒錯(cuò)椅邓,這里用pod是因?yàn)檎垓vCRD太麻煩了柠逞,定義好了還要在kubernetes上發(fā)布,于是干脆用pod來(lái)代替CRD景馁,反正dynamicClient都能處理板壮,咱們通過(guò)實(shí)戰(zhàn)掌握dynamicClient的用法就行了,以后遇到各種資源都能處理之裁僧;

源碼下載

名稱 鏈接 備注
項(xiàng)目主頁(yè) https://github.com/zq2599/blog_demos 該項(xiàng)目在GitHub上的主頁(yè)
git倉(cāng)庫(kù)地址(https) https://github.com/zq2599/blog_demos.git 該項(xiàng)目源碼的倉(cāng)庫(kù)地址,https協(xié)議
git倉(cāng)庫(kù)地址(ssh) git@github.com:zq2599/blog_demos.git 該項(xiàng)目源碼的倉(cāng)庫(kù)地址聊疲,ssh協(xié)議
  • 這個(gè)git項(xiàng)目中有多個(gè)文件夾茬底,client-go相關(guān)的應(yīng)用在<font color="blue">client-go-tutorials</font>文件夾下,如下圖紅框所示:
在這里插入圖片描述
  • client-go-tutorials文件夾下有多個(gè)子文件夾获洲,本篇對(duì)應(yīng)的源碼在<font color="blue">dynamicclientdemo</font>目錄下阱表,如下圖紅框所示:
在這里插入圖片描述

編碼

  • 新建文件夾dynamicclientdemo,在里面執(zhí)行以下命令,新建module:
go mod init dynamicclientdemo
  • 添加k8s.io/api和k8s.io/client-go這兩個(gè)依賴最爬,注意版本要匹配kubernetes環(huán)境:
go get k8s.io/api@v0.20.0
go get k8s.io/client-go@v0.20.0
  • 新建main.go涉馁,內(nèi)容如下,稍后會(huì)說(shuō)一下要注意的重點(diǎn):
package main

import (
    "context"
    "flag"
    "fmt"
    apiv1 "k8s.io/api/core/v1"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/apimachinery/pkg/runtime"
    "k8s.io/apimachinery/pkg/runtime/schema"
    "k8s.io/client-go/dynamic"
    "k8s.io/client-go/tools/clientcmd"
    "k8s.io/client-go/util/homedir"
    "path/filepath"
)

func main() {

    var kubeconfig *string

    // home是家目錄爱致,如果能取得家目錄的值烤送,就可以用來(lái)做默認(rèn)值
    if home:=homedir.HomeDir(); home != "" {
        // 如果輸入了kubeconfig參數(shù),該參數(shù)的值就是kubeconfig文件的絕對(duì)路徑糠悯,
        // 如果沒有輸入kubeconfig參數(shù)帮坚,就用默認(rèn)路徑~/.kube/config
        kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
    } else {
        // 如果取不到當(dāng)前用戶的家目錄,就沒辦法設(shè)置kubeconfig的默認(rèn)目錄了互艾,只能從入?yún)⒅腥?        kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
    }

    flag.Parse()

    // 從本機(jī)加載kubeconfig配置文件试和,因此第一個(gè)參數(shù)為空字符串
    config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)

    // kubeconfig加載失敗就直接退出了
    if err != nil {
        panic(err.Error())
    }

    dynamicClient, err := dynamic.NewForConfig(config)

    if err != nil {
        panic(err.Error())
    }

    // dynamicClient的唯一關(guān)聯(lián)方法所需的入?yún)?    gvr := schema.GroupVersionResource{Version: "v1", Resource: "pods"}

    // 使用dynamicClient的查詢列表方法,查詢指定namespace下的所有pod纫普,
    // 注意此方法返回的數(shù)據(jù)結(jié)構(gòu)類型是UnstructuredList
    unstructObj, err := dynamicClient.
        Resource(gvr).
        Namespace("kube-system").
        List(context.TODO(), metav1.ListOptions{Limit: 100})

    if err != nil {
        panic(err.Error())
    }

    // 實(shí)例化一個(gè)PodList數(shù)據(jù)結(jié)構(gòu)阅悍,用于接收從unstructObj轉(zhuǎn)換后的結(jié)果
    podList := &apiv1.PodList{}

    // 轉(zhuǎn)換
    err = runtime.DefaultUnstructuredConverter.FromUnstructured(unstructObj.UnstructuredContent(), podList)

    if err != nil {
        panic(err.Error())
    }

    // 表頭
    fmt.Printf("namespace\t status\t\t name\n")

    // 每個(gè)pod都打印namespace、status.Phase昨稼、name三個(gè)字段
    for _, d := range podList.Items {
        fmt.Printf("%v\t %v\t %v\n",
            d.Namespace,
            d.Status.Phase,
            d.Name)
    }
}
  • 上述代碼中有三處重點(diǎn)需要注意:
  1. Resource方法指定了本次操作的資源類型节视;
  2. List方法向kubernetes發(fā)起請(qǐng)求;
  3. FromUnstructured將Unstructured數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)成PodList悦昵,其原理前面已經(jīng)分析過(guò)肴茄;
  • 執(zhí)行<font color="blue">go run main.go</font>晌畅,如下但指,可以從kubernetes取得數(shù)據(jù),并且轉(zhuǎn)換成PodList也正常:
zhaoqin@zhaoqindeMBP-2 dynamicclientdemo % 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
  • 至此抗楔,dynamicClient的學(xué)習(xí)和實(shí)戰(zhàn)就完成了棋凳,它是名副其實(shí)的動(dòng)態(tài)客戶端工具,用一套API處理所有資源连躏,除了突破Clientset的內(nèi)置資源限制剩岳,還讓我們的業(yè)務(wù)代碼有了更大的靈活性,希望本文能給您一些參考入热,輔助您寫出與場(chǎng)景更加匹配的代碼拍棕;

你不孤單,欣宸原創(chuàng)一路相伴

  1. Java系列
  2. Spring系列
  3. Docker系列
  4. kubernetes系列
  5. 數(shù)據(jù)庫(kù)+中間件系列
  6. DevOps系列

歡迎關(guān)注公眾號(hào):程序員欣宸

微信搜索「程序員欣宸」勺良,我是欣宸绰播,期待與您一同暢游Java世界...
https://github.com/zq2599/blog_demos

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市尚困,隨后出現(xiàn)的幾起案子蠢箩,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,386評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谬泌,死亡現(xiàn)場(chǎng)離奇詭異滔韵,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)掌实,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門陪蜻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人贱鼻,你說(shuō)我怎么就攤上這事囱皿。” “怎么了忱嘹?”我有些...
    開封第一講書人閱讀 164,704評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵嘱腥,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我拘悦,道長(zhǎng)齿兔,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,702評(píng)論 1 294
  • 正文 為了忘掉前任础米,我火速辦了婚禮分苇,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘屁桑。我一直安慰自己医寿,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評(píng)論 6 392
  • 文/花漫 我一把揭開白布蘑斧。 她就那樣靜靜地躺著靖秩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪竖瘾。 梳的紋絲不亂的頭發(fā)上沟突,一...
    開封第一講書人閱讀 51,573評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音捕传,去河邊找鬼惠拭。 笑死,一個(gè)胖子當(dāng)著我的面吹牛庸论,可吹牛的內(nèi)容都是我干的职辅。 我是一名探鬼主播,決...
    沈念sama閱讀 40,314評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼聂示,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼域携!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起催什,我...
    開封第一講書人閱讀 39,230評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤涵亏,失蹤者是張志新(化名)和其女友劉穎宰睡,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體气筋,經(jīng)...
    沈念sama閱讀 45,680評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拆内,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了宠默。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片麸恍。...
    茶點(diǎn)故事閱讀 39,991評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖搀矫,靈堂內(nèi)的尸體忽然破棺而出抹沪,到底是詐尸還是另有隱情,我是刑警寧澤瓤球,帶...
    沈念sama閱讀 35,706評(píng)論 5 346
  • 正文 年R本政府宣布融欧,位于F島的核電站,受9級(jí)特大地震影響卦羡,放射性物質(zhì)發(fā)生泄漏噪馏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評(píng)論 3 330
  • 文/蒙蒙 一绿饵、第九天 我趴在偏房一處隱蔽的房頂上張望敞斋。 院中可真熱鬧话原,春花似錦、人聲如沸茶袒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)桶错。三九已至漫谷,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間欢搜,已是汗流浹背封豪。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工谴轮, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留炒瘟,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,158評(píng)論 3 370
  • 正文 我出身青樓第步,卻偏偏與公主長(zhǎng)得像疮装,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子粘都,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容