26

自己动手写一个 K8S YAML 模板化工具

 3 years ago
source link: https://mp.weixin.qq.com/s?__biz=MzU4MjQ0MTU4Ng%3D%3D&%3Bmid=2247487250&%3Bidx=1&%3Bsn=aac987b4cb9dfc8cd65c6840383f4606
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

7JFFvi6.png!mobile

我们在使用 Kubernetes 编写资源清单文件的时候,往往会使用类似于 Helm 或者 Kustomize 这样的工具来进行模板化处理,一来是提高了资源清单的灵活性,另一方面也确实降低了我们安装复杂的 Kubernetes 应用的门槛。本文我们尝试自己使用 Golang 来实现一个 YAML 资源清单文件模板化的方工具。

Golang 的模板化

Golang 中有一个支持模板文本文件的标准库 text/template ,这个库允许我们运行函数、赋值等操作,并可以执行一些逻辑来替换一些源文本中的模板值,我们可以从文件中读取这些文本,也可以从一个字符串去进行解析。由于我们想要模板化 YAML 文件,所以会从文件中去读取,这样我们就可以用如下所示的代码来进行处理:

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
}

上面的代码读取一个位于 filePath 的文件,并将其作为模板,使用 availableFunctions 中的函数和 availableData 中的数据来填充所有的模板值。比如我们读取的是一个 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 函数调用后的输出结果如下所示。

apiVersion: v1
kind: ConfigMap
metadata:
name: my-configmap
namespace: my-namespace
labels:
app: myapp
data:
USER: admin
PASSWORD: s0m3p455w0rd # 依赖你的 GeneratePassword 函数

在程序中使用 YAML

当我们使用 kubectl 这样的 CLI 工具的时候,在 Kubernetes 中使用 YAML 非常简单:

kubectl create -f myfile.yaml

但是如果要我们自己去编写代码来应用 YAML 文件的话,一般情况下会去使用 client-go 这个客户端工具包,但是 client-go 是针对静态类型的,而 YAML 文件中是没有对应的信息的,但是我们还可以通过下面两种方案来解决这个问题。

  • 使用 YAML 中的 KindVersion 反序列化为静态类型,然后使用它的类型化 REST 客户端进行通信。

  • 使用 Discovery 功能,Discovery 允许我们动态地查找给定类型的 REST 客户端,而不是通过静态类型去访问,下面我们就使用这种方式来进行演示。

首先我们需要像往常一样与 APIServer 通信创建一个 ClientSet 对象,如果我们从一个可以使用 kubectl 的系统执行代码,就意味着有一个可用的 kubeconfig 文件可以使用,通常这个文件为 $HOME/.kube/config 文件,如下所示:

import (
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/kubernetes"
)
...
// 使用本地 ~/.kube/config 创建配置
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 相当于和 K8S 集群通信的网关,使用它我们可以获取对象来给我们提供发现接口。对于我们想要实现的功能,需要能够查询给定资源的类型,并与该类型的 REST 客户端进行通信,所以我们分别需要一个 Discovery REST mapper 和一个 动态的 REST 接口 ,代码如下所示:

import (
"k8s.io/client-go/restmapper"
"k8s.io/client-go/dynamic"
)
...
// 获取支持的资源类型列表
resources, err := restmapper.GetAPIGroupResources(c.Discovery())
if err != nil {
log.Fatal(err)
}
// 创建 'Discovery REST Mapper',获取查询的资源的类型
mapper:= restmapper.NewDiscoveryRESTMapper(resourcesAvailable)
// 获取 'Dynamic REST Interface',获取一个指定资源类型的 REST 接口
dynamicREST, err := dynamic.NewForConfig(config)
if err != nil {
log.Fatal(err)
}

接下来我们去查找 YAML 文件中所代表的对象类型,并得到一个支持它的 REST 客户端是不是就可以去操作这个资源对象了?

首先调用前面的 Read 函数读取并执行一个模板:

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

为了使用我们的 Discovery REST mapper动态 REST 接口 ,我们需要将 YAML 文件的内容 decode 成一个 runtime.Objects 对象。

首先将 YAML 文件内容根据 --- 进行分割(一个 YAML 文件中可能有多个资源对象):

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

然后在每个片段上使用 k8s.io 的反序列化功能输出得到 runtime.Object 对象,以及一个持有 GroupVersionKind 信息的结构体。

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)
}
...

现在我们可以回头去使用我们的 RESTMapper,通过上面得到的 GVK 来获取一个映射:

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

有了资源类型,我们就可以使用前面的动态 REST 接口获取特定资源对象的客户端了:

unstructuredObj := runtimeObject.(*unstructured.Unstructured)
var resourceREST dynamic.ResourceInterface
// 需要为 namespace 范围内的资源提供不同的接口
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 对象来执行创建删除等操作了!

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// 创建对象
_, err = resourceREST.Create(unstructuredObj, metav1.CreateOptions{})
if err != nil {
log.Fatal(err)
}
// 删除对象
prop := metav1.DeletePropagationForeground
err = resourceREST.Delete(unstructuredObj.GetName(),
&metav1.DeleteOptions{
PropagationPolicy: &prop,
})
if err != nil {
log.Fatal(err)
}

到这里我们就使用 Golang 完成了一个轻量级的 YAML 模板处理工具了。


双11课程优惠活动~

zMFvee.png!mobile

点击屏末    |   即刻学习


Recommend

  • 28
    • 微信 mp.weixin.qq.com 3 years ago
    • Cache

    自己动手写一个 strace

    这次主要分享一下一个动手的东西,就是自己动手写一个 strace 工具。 用过 strace 的同学都知道, strace 是用来跟踪进程调用的  系统调用 ,还可以统计进程对  系统调用...

  • 5

    Grafana Tanka 是 Kubernetes 集群的配置工具,由 Jsonnet 数据模板语言实现。 使用它比使用 Yaml 来定义 Kubernetes 资源更简洁。Jsonnet 高度可重用,使你能通过组合现成的库来实现...

  • 6
    • 微信 mp.weixin.qq.com 3 years ago
    • Cache

    自己上手写性能工具(三)

  • 2

    学院的操作系统实验全都参照于渊的《自己动手写操作系统》。这本书虽然确实是好书,但却是 2005 年成书的,距离现在已经十几年了。其中使用的 bochs 虚拟机已经是时代的眼泪。更常见的是 gcc 工具链和 qemu。比如打开项目目录发现一堆神秘的 *.S

  • 6
    • zhuanlan.zhihu.com 2 years ago
    • Cache

    自己动手写一个 Golang ORM 框架

    自己动手写一个 Golang ORM 框架作者:smallyang,腾讯 IEG 运营开发工程师 当我深入的学习和了解了 GORM,XORM 后,我还是觉得它们不够简洁和优雅,有些笨重,有很大的学习成本。本着学习和探索的目的,于是我自己实现了一个简单且...

  • 4

    自己动手写一个 Kubernetes YAML 模板化工具 我们在使用 Kubernetes 编写资源清单文件的时候,往往...

  • 9

    Go语言动手写Web框架 - Gee第六天 模板(HTML Template) 源代码/数据集已上传到 Github - 7days-golang

  • 3

    前文虽然以spring配置文件的样式,根据配置文件搞了一个类出来,但离工具化的应用还很远,本文再进一步。 处理类之间的关系 前文的例子只创建了较为简单的beanB类,现在我们创建beanA类。从模仿spring beanFactory的getBean方法开始。...

  • 4

    原生拖拽太拉跨了,纯JS自己手写一个拖拽效果,纵享丝滑 推荐 原创 提到元素拖拽,通常都会先想到用 HTML5 的...

  • 6

    V2EX  ›  Kubernetes 多环境下的 k8s 的 yaml 配置管理  

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK