我调查了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 の作者のプレゼン資料