<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          自己動(dòng)手寫(xiě)一個(gè) K8S YAML 模板化工具

          共 5057字,需瀏覽 11分鐘

           ·

          2020-11-08 21:55

          我們?cè)谑褂?Kubernetes 編寫(xiě)資源清單文件的時(shí)候,往往會(huì)使用類似于 Helm 或者 Kustomize 這樣的工具來(lái)進(jìn)行模板化處理,一來(lái)是提高了資源清單的靈活性,另一方面也確實(shí)降低了我們安裝復(fù)雜的 Kubernetes 應(yīng)用的門(mén)檻。本文我們嘗試自己使用 Golang 來(lái)實(shí)現(xiàn)一個(gè) YAML 資源清單文件模板化的方工具。

          Golang 的模板化

          Golang 中有一個(gè)支持模板文本文件的標(biāo)準(zhǔn)庫(kù) text/template,這個(gè)庫(kù)允許我們運(yùn)行函數(shù)、賦值等操作,并可以執(zhí)行一些邏輯來(lái)替換一些源文本中的模板值,我們可以從文件中讀取這些文本,也可以從一個(gè)字符串去進(jìn)行解析。由于我們想要模板化 YAML 文件,所以會(huì)從文件中去讀取,這樣我們就可以用如下所示的代碼來(lái)進(jìn)行處理:

          package?templates

          import?(
          ????"bytes"
          ????"path/filepath"
          ????"text/template"
          ????...
          )

          func?Read(filePath?string)?([]byte,?error)?{
          ????tmpl,?err?:=?template.New(filepath.Base(filePath)).
          ????Funcs(availableFunctions).
          ????ParseFiles(filePath)
          ????if?err?!=?nil?{
          ????????return?nil,?err
          ????}
          ????var?buf?bytes.Buffer
          ????if?err?:=?tmpl.Execute(&buf,?availableData);?err?!=?nil?{
          ????????return?nil,?err
          ????}
          ????return?buf.Bytes(),?nil
          }

          上面的代碼讀取一個(gè)位于 filePath 的文件,并將其作為模板,使用 availableFunctions 中的函數(shù)和 availableData 中的數(shù)據(jù)來(lái)填充所有的模板值。比如我們讀取的是一個(gè) ConfigMap 的 YAML 文件。

          apiVersion:?v1
          kind:?ConfigMap
          metadata:
          ??name:?my-configmap
          ??namespace:?{{?.Namespace?}}
          ??labels:
          ????app:?myapp
          data:
          ??USER:?admin
          ??PASSWORD:?{{?GeneratePassword?}}

          然后我們把 availableDataavailableFunctions 定義成如下所示的代碼。

          var?availableData?=?map[string]string{
          ????"Namespace":?"my-namespace",
          }
          var?availableFunctions?=?template.FuncMap{
          ????"GeneratePassword":?GeneratePasswordFunc,
          }

          func?GeneratePasswordFunc()?(string,?error)?{
          ...
          }

          這樣上面定義的 Read 函數(shù)調(diào)用后的輸出結(jié)果如下所示。

          apiVersion:?v1
          kind:?ConfigMap
          metadata:
          ??name:?my-configmap
          ??namespace:?my-namespace
          ??labels:
          ????app:?myapp
          data:
          ??USER:?admin
          ??PASSWORD:?s0m3p455w0rd?#?依賴你的?GeneratePassword?函數(shù)

          在程序中使用 YAML

          當(dāng)我們使用 kubectl 這樣的 CLI 工具的時(shí)候,在 Kubernetes 中使用 YAML 非常簡(jiǎn)單:

          kubectl?create?-f?myfile.yaml

          但是如果要我們自己去編寫(xiě)代碼來(lái)應(yīng)用 YAML 文件的話,一般情況下會(huì)去使用 client-go 這個(gè)客戶端工具包,但是 client-go 是針對(duì)靜態(tài)類型的,而 YAML 文件中是沒(méi)有對(duì)應(yīng)的信息的,但是我們還可以通過(guò)下面兩種方案來(lái)解決這個(gè)問(wèn)題。

          • 使用 YAML 中的 KindVersion 反序列化為靜態(tài)類型,然后使用它的類型化 REST 客戶端進(jìn)行通信。
          • 使用 Discovery 功能,Discovery 允許我們動(dòng)態(tài)地查找給定類型的 REST 客戶端,而不是通過(guò)靜態(tài)類型去訪問(wèn),下面我們就使用這種方式來(lái)進(jìn)行演示。

          首先我們需要像往常一樣與 APIServer 通信創(chuàng)建一個(gè) ClientSet 對(duì)象,如果我們從一個(gè)可以使用 kubectl 的系統(tǒng)執(zhí)行代碼,就意味著有一個(gè)可用的 kubeconfig 文件可以使用,通常這個(gè)文件為 $HOME/.kube/config 文件,如下所示:

          import?(
          ????"k8s.io/client-go/tools/clientcmd"
          ????"k8s.io/client-go/kubernetes"
          )
          ...
          //?使用本地?~/.kube/config?創(chuàng)建配置
          kubeConfigPath?:=?os.ExpandEnv("$HOME/.kube/config")
          config,?err?:=?clientcmd.BuildConfigFromFlags("",?kubeConfigPath)
          if?err?!=?nil?{
          ????log.Fatal(err)
          }
          //?使用上面的配置獲取連接
          c,?err?:=?kubernetes.NewForConfig(config)
          if?err?!=?nil?{
          ????log.Fatal(err)
          }

          ClientSet 相當(dāng)于和 K8S 集群通信的網(wǎng)關(guān),使用它我們可以獲取對(duì)象來(lái)給我們提供發(fā)現(xiàn)接口。對(duì)于我們想要實(shí)現(xiàn)的功能,需要能夠查詢給定資源的類型,并與該類型的 REST 客戶端進(jìn)行通信,所以我們分別需要一個(gè) Discovery REST mapper 和一個(gè)動(dòng)態(tài)的 REST 接口,代碼如下所示:

          import?(
          ????"k8s.io/client-go/restmapper"
          ????"k8s.io/client-go/dynamic"
          )
          ...
          //?獲取支持的資源類型列表
          resources,?err?:=?restmapper.GetAPIGroupResources(c.Discovery())
          if?err?!=?nil?{
          ????log.Fatal(err)
          }
          //?創(chuàng)建?'Discovery?REST?Mapper',獲取查詢的資源的類型
          mapper:=?restmapper.NewDiscoveryRESTMapper(resourcesAvailable)
          //?獲取?'Dynamic?REST?Interface',獲取一個(gè)指定資源類型的?REST?接口
          dynamicREST,?err?:=?dynamic.NewForConfig(config)
          if?err?!=?nil?{
          ????log.Fatal(err)
          }

          接下來(lái)我們?nèi)ゲ檎?YAML 文件中所代表的對(duì)象類型,并得到一個(gè)支持它的 REST 客戶端是不是就可以去操作這個(gè)資源對(duì)象了?

          首先調(diào)用前面的 Read 函數(shù)讀取并執(zhí)行一個(gè)模板:

          finalYAML,?err?:=?templates.Read(myFilePath)
          if?err?!=?nil?{
          ????log.Fatal(err)
          }

          為了使用我們的 Discovery REST mapper動(dòng)態(tài) REST 接口,我們需要將 YAML 文件的內(nèi)容 decode 成一個(gè) runtime.Objects 對(duì)象。

          首先將 YAML 文件內(nèi)容根據(jù) --- 進(jìn)行分割(一個(gè) YAML 文件中可能有多個(gè)資源對(duì)象):

          objectsInYAML?:=?bytes.Split(yamlBytes,?[]byte("---"))
          if?len(objectsInYAML)?==?0?{
          ????return?nil,?nil
          }

          然后在每個(gè)片段上使用 k8s.io 的反序列化功能輸出得到 runtime.Object 對(duì)象,以及一個(gè)持有 Group、VersionKind 信息的結(jié)構(gòu)體。

          import(
          ????"k8s.io/apimachinery/pkg/runtime/serializer/yaml"
          )
          ...
          for?_,?objectInYAML?:=?range?objectsInYAML?{
          ????runtimeObject,?groupVersionAndKind,?err?:=?
          ????yaml.
          ????????NewDecodingSerializer(unstructured.UnstructuredJSONScheme).
          ????????Decode(objectInYAML.Raw,?nil,?nil)
          ????if?err?!=?nil?{
          ????????log.Fatal(err)
          ????}
          ...

          現(xiàn)在我們可以回頭去使用我們的 RESTMapper,通過(guò)上面得到的 GVK 來(lái)獲取一個(gè)映射:

          //?查找?Group/Version/Kind?的?REST?映射
          mapping,?err?:=?d.mapper.RESTMapping(groupVersionAndKind.GroupKind(),?groupVersionAndKind.Version)
          if?err?!=?nil?{
          ????log.Fatal(err)
          }

          有了資源類型,我們就可以使用前面的動(dòng)態(tài) REST 接口獲取特定資源對(duì)象的客戶端了:

          unstructuredObj?:=?runtimeObject.(*unstructured.Unstructured)
          var?resourceREST?dynamic.ResourceInterface
          //?需要為?namespace?范圍內(nèi)的資源提供不同的接口
          if?mapping.Scope.Name()?==?meta.RESTScopeNameNamespace?{
          ????if?unstructuredObj.GetNamespace()?==?""?{
          ????????unstructuredObj.SetNamespace("default")
          ????}
          ????resourceREST?=?
          ????d.
          ??????dynamicREST.
          ??????Resource(mapping.Resource).
          ??????Namespace(unstructuredObj.GetNamespace())
          }?else?{
          ????resourceREST?=?d.dynamicREST.Resource(mapping.Resource)
          }

          到這里我們就可以在 Kubernetes 中使用得到的 client 對(duì)象來(lái)執(zhí)行創(chuàng)建刪除等操作了!

          import?(
          ????metav1?"k8s.io/apimachinery/pkg/apis/meta/v1"
          )
          //?創(chuàng)建對(duì)象
          _,?err?=?resourceREST.Create(unstructuredObj,?metav1.CreateOptions{})
          if?err?!=?nil?{
          ????log.Fatal(err)
          }
          //?刪除對(duì)象
          prop?:=?metav1.DeletePropagationForeground
          err?=?resourceREST.Delete(unstructuredObj.GetName(),
          ????&metav1.DeleteOptions{
          ???????PropagationPolicy:?&prop,
          ????})
          if?err?!=?nil?{
          ???log.Fatal(err)
          }

          到這里我們就使用 Golang 完成了一個(gè)輕量級(jí)的 YAML 模板處理工具了。




          雙11課程優(yōu)惠活動(dòng)~


          ?點(diǎn)擊屏末?|??|?即刻學(xué)習(xí)

          瀏覽 71
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久理论| 欧美精品乱码视频一二专区 | 精品av国产日韩一区二区 | 尹人大香蕉手机网 | 91狠狠综合久久 |