我调查了Kubernetes清单测试工具

总结

Kubernetes的配置文件通常使用YAML编写,并经常通过git等进行提交和管理。
在此过程中,我们考虑进行预先测试,以以下方式为依据。

    • マニフェストがYAMLとして正しいか

 

    • マニフェストがチームで定めたルールにしたがっているか?

特定のラベルがついているか?またその値が正しいか?

工具调查

我调查了一些可能用于满足要求的开源软件工具。

这是由Cloud 66公司开发的测试工具,用于测试所创建的清单。

品尝

我参考了《入门指南》,试着进行了一次实际操作。

文件中写着参数-rules之类的,但正确的应该是–rules。

规则可以这样写。通过使用 jsonpath 来过滤感兴趣的资源,并编写断言等操作。

rule ApiV1Only ensure {
    fetch("$.apiVersion").first == "v1" // we only allow the use of v1 API functions
}
$ copper check --rules ../rules/v10only.cop --file kube-state-metrics.yaml
check --rules ../rules/v10only.cop --file kube-state-metrics.yaml
Validating part 0
    ApiV1Only - PASS
Validating part 1
    ApiV1Only - FAIL
Validating part 2
    ApiV1Only - FAIL
Validating part 3
    ApiV1Only - PASS
Validating part 4
    ApiV1Only - FAIL

当一个文件中包含多个资源时,会按照上述的方式进行输出。我希望能够提供资源的名称。
如果没有被fetch捕捉到,似乎之后的assert也不会被执行。

可以通过限制资源来进行测试。在部署中,还可以指定apiVersion。

rule always ensure{
  fetch("$[?(@['kind'] == 'Deployment')].spec.template.spec.containers..imagePullPolicy").first == "IfNotPresent"
}

我想比较所有的imagePullPolicy选项,而不仅仅是比较第一个选项。但是我没有找到类似于forEach的方法,并且不知道如何实现。

通过检查标签来确认是否具有特定的标签?
看起来可以通过检查数量来判断是否存在。

rule existLabel ensure {
   fetch("$.metadata.labels['addonmanager.kubernetes.io/mode']")
        .count == 1
    }

如果试图验证其中的内容

rule labelContents ensure {
    fetch("$.metadata.labels['addonmanager.kubernetes.io/mode']").first == "Reconcile"
    }

在处理不存在的资源时,产生了运行时错误。

$ copper check --rules ./rules/nolatest.cop --file kube-state-metrics.yaml
check --rules ./rules/nolatest.cop --file kube-state-metrics.yaml
Validating part 0
    existLabel - PASS
    labelContents - PASS
Validating part 1
    existLabel - PASS
    labelContents - PASS
Validating part 2
    existLabel - PASS
    labelContents - PASS
Validating part 3
    existLabel - PASS
    labelContents - PASS
Validating part 4
Runtime error: cannot call first on an empty array

然而,考试本身似乎被认为是成功的。需要对错误处理进行改进。

其他让我感到在意的事项

    • && が使えると書いてあるが実際にはsyntax errorする

 

    • 論理演算にかっこが使えない

 

    • andが短絡評価にならないのでガードとして使えない

 

    fetchを変数に入れられない

我看到他们使用Ruby制作了DSL,并以此为由想着Ruby是否可以完全使用,但查看了源代码后发现copper/copper.treetop at master · cloud66/copper · GitHub他们似乎是在自己构建专门的语法解析器,没有给我一种可以编写灵活处理逻辑的感觉。(可能是因为他们并不希望它作为测试描述语言来使用。)

整理

因为我只能以本土语言(中文)为你提供一个选项,所以无法提供其他选项。

以独一无二的语言,并且尚未达到完善的程度,试图做出一些努力是很艰难的印象。

由于这个产品仍处于发展阶段,通过积极进行宣传和处理问题,或许可以让它变得更有用途。

Kubeval – 可助核(Kè zhù hé)

这个工具可以检查Kubernetes清单YAML文件是否是合法的YAML格式并且符合Kubernetes API的规范。

品尝

GitHub – garethr/kubeval:验证您的Kubernetes配置文件,支持多个Kubernetes版本。

wget https://github.com/garethr/kubeval/releases/download/0.6.0/kubeval-darwin-amd64.tar.gz
tar xf kubeval-darwin-amd64.tar.gz
cp kubeval /usr/local/bin

我按照指示使用了curl。我认为你也可以通过go get进行安装。

获取OpenAPI的Schema的源地址

        --schema-location string      Base URL used to download schemas. Can also be specified with the environment variable KUBEVAL_SCHEMA_LOCATION (default "https://raw.githubusercontent.com/garethr")

默认情况下,似乎会去GitHub查看模式。

GitHub – garethr/kubernetes-json-schema:一套从OpenAPI定义中提取的各种Kubernetes版本的JSON模式。

然而,更新停留在v1.9.3版本之前。
由于模式可以在本地通过file://进行引用,因此可以将所需的Kubernetes版本的模式放置在本地进行执行。

据作者所述,使用这个工具(GitHub – garethr/openapi2jsonschema: Convert OpenAPI definitions into JSON schemas for all types in the API)可以将swagger.json转换成jsonschema。

只需選擇一個選項:按照下述方式執行,可以從Kubernetes存儲庫中創建模式。

openapi2jsonschema https://raw.githubusercontent.com/kubernetes/kubernetes/master/api/openapi-spec/swagger.json

有可用的 Docker 镜像。 https://hub.docker.com/r/garethr/openapi2jsonschema/

openapi2jsonschema有一些选项可供选择。

    • standalone

定義を参照せずに全て展開するオプション
指定するとすごく遅くなりました

strict

存在しないプロパティがあった場合にエラーとなるようなschemaを出力するオプション
指定すると終わらなくなった・・・

我原本以为默认情况下是参考的独立的jsonschema,但后来确认也能够运行非独立的jsonschema。

动作的样子 zuò de

我尝试使用上述方法生成的schema来执行命令。

$ kubeval manifests/* --schema-location file:///Users/inajob/work/_tmp/schema
The document manifests/kube-state-metrics.yaml contains a valid ServiceAccount
The document manifests/kube-state-metrics.yaml contains a valid ClusterRole
The document manifests/kube-state-metrics.yaml contains a valid ClusterRoleBinding
The document manifests/kube-state-metrics.yaml contains a valid Service
The document manifests/kube-state-metrics.yaml contains an invalid Deployment
---> spec.template.spec.containers.0.readinessProbe.httpGet.port: Invalid type. Expected: string, given: integer
The document manifests/kubernetes-dashboard.yaml contains a valid ServiceAccount
The document manifests/kubernetes-dashboard.yaml contains a valid ClusterRoleBinding
The document manifests/kubernetes-dashboard.yaml contains a valid Role
The document manifests/kubernetes-dashboard.yaml contains a valid RoleBinding
The document manifests/kubernetes-dashboard.yaml contains an invalid Service
---> spec.ports.0.targetPort: Invalid type. Expected: string, given: integer
The document manifests/kubernetes-dashboard.yaml contains an invalid Deployment
---> spec.template.spec.containers.0.livenessProbe.httpGet.port: Invalid type. Expected: string, given: integer
The document manifests/prometheus.yaml contains a valid ConfigMap
The document manifests/prometheus.yaml contains a valid ConfigMap
The document manifests/prometheus.yaml contains a valid ServiceAccount
The document manifests/prometheus.yaml contains a valid ClusterRole
The document manifests/prometheus.yaml contains a valid ClusterRoleBinding
The document manifests/prometheus.yaml contains an invalid Service
---> spec.ports.0.targetPort: Invalid type. Expected: string, given: integer
The document manifests/prometheus.yaml contains an invalid Deployment
---> spec.template.spec.containers.0.livenessProbe.httpGet.port: Invalid type. Expected: string, given: integer
---> spec.template.spec.containers.0.readinessProbe.httpGet.port: Invalid type. Expected: string, given: integer
The document _output/prometheus.yaml contains an invalid Ingress
---> spec.rules.0.http.paths.0.backend.servicePort: Invalid type. Expected: string, given: integer
make: *** [kubeval] Error 1

得到了一个类似的输出。可以看出,出现了端口应该是字符串而不是数字的错误。

总结

使用kubeval可以检查YAML是否正确,并且是否符合Kubernetes定义的OpenAPI规范的清单。

我們在驗證中使用了經由openapi2jsonschema轉換的jsonschema。
默認情況下,我們會使用GitHub – garethr/kubernetes-json-schema的JSON模式,其中包含從OpenAPI定義中提取的各種Kubernetes版本。但是,更新在1.9.3版本停止了。因此,為了在更高版本上進行驗證,您需要自己準備jsonschema。

Kubetest (K8S测试套件)

这是一个用于执行Kubernetes清单文件的单元测试的工具。

尝试着品尝

安装
因为是 Go 语言,所以很容易。

$ go get github.com/garethr/kubetest
# github.com/garethr/skyhook
../../go-workspace/src/github.com/garethr/skyhook/skyhook.go:30:28: multiple-value skylark.ExecFile() in single-value context
../../go-workspace/src/github.com/garethr/skyhook/skyhook.go:46:24: not enough arguments in call to syntax.Parse
    have (string, []byte)
    want (string, interface {}, syntax.Mode)
../../go-workspace/src/github.com/garethr/skyhook/skyhook.go:62:14: thread.Push undefined (type *skylark.Thread has no field or method Push)
../../go-workspace/src/github.com/garethr/skyhook/skyhook.go:63:14: thread.Pop undefined (type *skylark.Thread has no field or method Pop)

没有成功。。skyhook是有问题的吗?
根据阅读来看,函数的接口似乎不正确。
使用附带的Makefile进行构建。

$ make darwin
(略)
vendor/github.com/garethr/skyhook/skyhook.go:30:28: multiple-value skylark.ExecFile() in single-value context
vendor/github.com/garethr/skyhook/skyhook.go:46:24: not enough arguments in call to syntax.Parse
    have (string, []byte)
    want (string, interface {}, syntax.Mode)
vendor/github.com/garethr/skyhook/skyhook.go:62:14: thread.Push undefined (type *skylark.Thread has no field or method Push)
vendor/github.com/garethr/skyhook/skyhook.go:63:14: thread.Pop undefined (type *skylark.Thread has no field or method Pop)
make: *** [darwin] Error 2

无论如何,仍然存在错误。
由于官方的travis也出现了错误,所以似乎无法在当前构建中进行HEAD。

根据我的理解,这段话的意思是:使用Make来进行glide update的操作似乎会安装最新的软件包,但这会导致安装了最新的软件包后,由于函数接口的不同导致无法编译。

以下是对该段话的汉语翻译:
使用Make进行glide update操作时,会导致安装最新的软件包,而这会使函数接口发生变化而无法进行编译。

需要使用 glide.lock 文件中所列出的包来进行构建。
可以按照以下步骤进行安装。

$ go get github.com/garethr/kubetest
(失敗する)
$ glide install
(略)
$ go get github.com/garethr/kubetest
(うまくいく)

也有编译好的二进制文件可用。使用这个也是一个不错的选择。

发布 · garethr/kubetest · GitHub (在 GitHub 上)

试试看

当将测试放入./tests目录中时,它会自动加载(你也可以指定)。

规则可以这样描述。

def test_for_latest_image():
    if spec["kind"] == "ReplicationController":
        for container in spec["spec"]["template"]["spec"]["containers"]:
            tag = container["image"].split(":")[-1]
            assert_not_equal(tag, "latest", "should not use latest images")

test_for_latest_image()
$ kubetest _output/kube-state-metrics.yaml
FATA .latest.sky.swp:1:10: got float literal, want newline

据说可以读取tests文件而无需考虑文件扩展名…正在尝试操作vim的临时文件…删除并重新开始。

$ kubetest _output/kube-state-metrics.yaml
WARN _output/kube-state-metrics.yaml "map[k8s-app:"kube-state-metrics"]" does not contain "addonmanager.kubernetes.io/mode"

它看起来在做样子。
但是它没有告诉我它处理的是哪个清单文件…

为了确定正在处理的资源,可以进行以下操作:

– 确认正在处理的资源是哪一个。
– 确保了解正在处理的资源。
– 确定当前正在处理的资源。
– 为了了解正在处理的资源,需要进行如下操作。

当一个文件中定义了多个资源时,很难知道出了什么错误,因此我们要输出信息。

def test_for_latest_image():
    print(spec["kind"] + " " + spec["metadata"]["name"])
    if spec["kind"] == "ReplicationController":
        for container in spec["spec"]["template"]["spec"]["containers"]:
            tag = container["image"].split(":")[-1]
            assert_not_equal(tag, "latest", "should not use latest images")

test_for_latest_image()

尝试测试崩溃的YAML

$ kubetest manifest/kube-state-metrics.yaml && echo "ok"
WARN The document manifest/kube-state-metrics.yaml does not contain any content
ClusterRole kube-state-metrics
ClusterRoleBinding kube-state-metrics
Service kube-state-metrics
Deployment kube-state-metrics
ok

尽管出现了奇怪的警告,但命令返回OK。这个工具似乎无法确定YAML是否有效。

关于测试定义的领域特定语言(DSL)

    • Pythonのサブセットであるskylarkという言語で記述する。(https://github.com/google/skylark)

Pythonとにているがフルセットではないので戸惑うことがある。マニュアルはしっかりしている。skylark/spec.md at master · google/skylark · GitHub

テスト用の関数を定義した後,最後に実行するのが少しダサい。 Issueもできているのでそのうちよくなりそう Autodetect functions starting with test_ · Issue #2 · garethr/kubetest · GitHub

总结

我对一些地方有所关注,但我觉得manifest是一个可以灵活验证是否符合特定规则的好工具。
我担心HEAD无法构建。

整体总结

    • Copper

 

    • kubeval

 

    kubetest

进行了对每个选项的调查。虽然没有一个是完美的,但通过进行事前测试可以提高清单的质量。

请参考

    • kubetest, kubeval の作者のプレゼン資料

广告
将在 10 秒后关闭
bannerAds