30、K8s实战:KubernetesYAML最佳实践和策略

前言

Kubernetesv工作负载最常用 YAML 格式的文件来定义。YAML 的问题之一就是很难描述清单文件之间的约束或关系。如果你希望检查是否已从受信任的注册表中提取部署到群集中的所有映像,该怎么办?如何防止没有 Pod 安全策略的工作负载提交到集群?集成静态检查可以在更接近开发生命周期的时间内捕获错误和违反策略的行为。并且由于改善了资源定义的有效性和安全性,因此你可以相信生产工作负载遵循最佳实践。Kubernetes YAML 文件的静态检查生态系统可以分为以下几类:

  • API验证程序:此类工具针对 Kubernetes API 服务器验证给定的 YAML 清单。
  • 内置检查器:此类工具捆绑了针对安全性,最佳实践等的自觉检查。
  • 自定义验证器:此类工具允许使用多种语言(例如 python 和 Javascript)编写自定义检查。

下文中我们将涉及到六个不同的工具:

Kubeval
Kube-score
Config-lint
Copper
Conftest
Polaris

基准服务

首先部署一个基准服务,以便后面测试对比

apiVersion:*apps/v1
kind:*Deployment
metadata:
**name:*http-echo
spec:
**replicas:*2
**selector:
****matchLabels:
******app:*http-echo
**template:
****metadata:
******labels:
********app:*http-echo
****spec:
******containers:
******-*name:*http-echo
********image:*hashicorp/http-echo
********args:*["-text",*"hello-world"]
********ports:
********-*containerPort:*5678
---
apiVersion:*v1
kind:*Service
metadata:
**name:*http-echo
spec:
**ports:
**-*port:*5678
****protocol:*TCP
****targetPort:*5678
**selector:
****app:*http-echo

部署完成并验证如下:

[root@k8s-node001*Test]#*kubectl**get*po
NAME****************************READY***STATUS****RESTARTS***AGE
http-echo-57dd74545-rtxzm*******1/1*****Running***0**********65s
http-echo-57dd74545-trst7*******1/1*****Running***0**********65s

[root@k8s-node001*Test]#*kubectl**get*svc
NAME*************TYPE********CLUSTER-IP******EXTERNAL-IP***PORT(S)**********AGE
http-echo********ClusterIP***10.102.221.64***<none>********5678/TCP*********70s

[root@k8s-node001*Test]#*curl**10.102.221.64:5678
hello-world

以上YAML 文件能部署成功,但是,它遵循最佳做法吗?

kubeval

kubeval 的前提是与 Kubernetes 的任何交互都通过其 REST API 进行。因此,可以使用 API 模式来验证给定的 YAML 输入是否符合该模式。安装 kubeval:

wget*https://github.com/instrumenta/kubeval/releases/latest/download/kubeval-linux-amd64.tar.gz
tar*xf*kubeval-linux-amd64.tar.gz
cp*kubeval*/usr/local/bin

现在我们来改下 base.yaml 删除

**selector:
****matchLabels:
******app:*http-echo

然后使用 kubeval 对 base.yaml 检查

[root@k8s-node001*Test]#*kubeval**base.yaml
WARN*-*base.yaml*contains*an*invalid*Deployment*(http-echo)*-*selector:*selector*is*required
PASS*-*base.yaml*contains*a*valid*Service*(http-echo)

输出看到一个 WARN,提示 selector 是必须的字段,然后恢复 selector,再次检查

[root@k8s-node001*Test]#*kubeval**base.yaml*
PASS*-*base.yaml*contains*a*valid*Deployment*(http-echo)
PASS*-*base.yaml*contains*a*valid*Service*(http-echo)

检查PASS

kubeval 之类的工具的优势在于,咱们可以在部署周期的早期发现此类错误。另外,您不需要访问集群即可运行检查-它们可以脱机运行。默认情况下,kubeval 会根据最新的未发布的 Kubernetes API 模式验证资源。更多用法详情请参见官网。

kube-score

kube-score 会对你提供的 YAML 清单进行分析,并针对集群的内置检查对其进行评分。kube-score 提供在线版和离线版,本文偷懒就用在线版了,首先打开 https://kube-score.com/ ,然后在输入框贴入写好的 YAML 清单,这里以上文 base.yaml 来分析。

*

解析结果如下

*

从如上可以看到针对这个文件给出的建议,比如资源限制、镜像 TAG、Pod 网络策略等。不错吧,非常好用的工具。当然,kube-score 并不可扩展,并且您不能添加或调整策略。如果要编写自定义检查以符合组织策略,则可以使用以下四个工具之一:config-lint,copper,conftest 或 Polaris。

Config-lint

Config-lint 是用于验证以 YAML,JSON,Terraform,CSV 和 Kubernetes 清单编写的配置文件的工具。安装 Config-lint:

wget*https://github.com/stelligent/config-lint/releases/download/v1.6.0/config-lint_Linux_x86_64.tar.gz
tar*-zxf*config-lint_Linux_x86_64.tar.gz
mv*config-lint*/usr/local/bin/

Config-lint 并没有对 Kubernetes 清单进行内置检查。你必须编写自己的规则才能执行任何验证。规则被写为 YAML 文件,称为规则集,并具有以下结构:

version:*1
description:*Rules*for*Kubernetes*spec*files
type:*Kubernetes
files:
**-*"*.yaml"
rules:
***#*list*of*rules

假设咱们希望检查部署中的镜像是否总是从可信任的仓库(例如 kubeops.net/app:1.0 )中提取。实施此类检查的 config-lint 规则如下所示:

-*id:*MY_DEPLOYMENT_IMAGE_TAG
**severity:*FAILURE
**message:*Deployment*must*use*a*valid*image*tag
**resource:*Deployment
**assertions:
****-*every:
********key:*spec.template.spec.containers
********expressions:
**********-*key:*image
************op:*starts-with
************value:*"kubeops.net/"

一个完整的规则集如下所示:

version:*1
description:*Rules*for*Kubernetes*spec*files
type:*Kubernetes
files:
**-*"*.yaml"
rules:
**-*id:*DEPLOYMENT_IMAGE_REPOSITORY
****severity:*FAILURE
****message:*Deployment*must*use*a*valid*image*repository
****resource:*Deployment
****assertions:
******-*every:
**********key:*spec.template.spec.containers
**********expressions:
************-*key:*image
**************op:*starts-with
**************value:*"kubeops.net/"

如果要测试检查,可以将规则集另存为 check_image_repo.yaml。然后使用 config-lint 执行检查:

[root@k8s-node001*Test]#*config-lint*-rules*check_image_repo.yaml*base.yaml
[
**{
****"AssertionMessage":*"Every*expression*fails:*And*expression*fails:*image*does*not*start*with*kubeops.net/",
****"Category":*"",
****"CreatedAt":*"2020-11-02T08:28:43Z",
****"Filename":*"base.yaml",
****"LineNumber":*0,
****"ResourceID":*"http-echo",
****"ResourceType":*"Deployment",
****"RuleID":*"DEPLOYMENT_IMAGE_REPOSITORY",
****"RuleMessage":*"Deployment*must*use*a*valid*image*repository",
****"Status":*"FAILURE"
**}
]

可以看到 Every expression fails,检测不通过。现在我们来改下 images 地址为 image: kubeops.net/http-echo 再来检查一次

[root@k8s-node001*Test]#*config-lint*-rules*check_image_repo.yaml*base.yaml
[]

输出不报错即为成功。Config-lint 是一个很有前途的框架,可以让你使用 YAML DSL 为Kubernetes YAML 清单编写自定义检查。但是如果您想表达更复杂的逻辑和检查该怎么办?YAML 对此是否也有限制?如果您可以使用真正的编程语言来表达这些检查,该怎么办?接下来看 Copper

Copper

Copper V2 是一个使用自定义检查来验证清单的框架,就像 config-lint 一样。但是,Copper 不使用 YAML 定义检查。相反,测试是用 JavaScript 编写的,而 Copper 提供了一个包含一些基本帮助程序的库,以帮助读取 Kubernetes 对象和报告错误。安装 Copper:

https://github.com/cloud66-oss/copper/releases/download/2.0.1/linux_amd64_2.0.1
mv*linux_amd64_2.0.1*copper
chmod*+*x*copper
mv*copper*/usr/local/bin/

与config-lint 相似,Copper 并没有提供内置检查。让我们自定义一个检查,以确保部署镜像 tag 必须非 latest。check_image_repo.js:

$$.forEach(function($){
****if*($.kind*===*'Deployment')*{
********$.spec.template.spec.containers.forEach(function(container)*{
************var*image*=*new*DockerImage(container.image);
************if*(image.tag*===*'latest')*{
****************errors.add_error('no_latest',"latest*is*used*in*"*+*$.metadata.name,*1)
************}
********});
****}
});

执行检查

[root@k8s-node001*Test]#*copper*validate*--in=base.yaml*--validator=check_image_tag.js
Check*no_latest*failed*with*severity*1*due*to*latest*is*used*in*http-echo
Validation*failed

现在修改为 image:kubeops.net/http-echo:v1.0.0

[root@k8s-node001*Test]#*copper*validate*--in=base.yaml*--validator=check_image_tag.js
Validation*successful

Conftest

Conftest 是用于配置数据的测试框架,可用于检查和验证 Kubernetes 清单。测试使用专用查询语言 Rego 编写的。安装 Conftest:

wget*https://github.com/open-policy-agent/conftest/releases/download/v0.21.0/conftest_0.21.0_Linux_x86_64.tar.gz
tar*-xzf*conftest_0.21.0_Linux_x86_64.tar.gz
mv*conftest*/usr/local/bin

与config-lint 和 copper 类似,conftest 没有任何内置检查。首先创建一个新目录 conftest-checks 和一个名为 check_image_registry.rego 的文件,其内容如下:

package*main

deny[msg]*{

**input.kind*==*"Deployment"
**image*:=*input.spec.template.spec.containers[_].image
**not*startswith(image,*"kubeops.net/")
**msg*:=*sprintf("image*'%v'*doesn't*come*from*kubeops.net*repository",*[image])
}

先修改base.yaml,image: docker.io/http-echo,使用 conftest 执行检测

[root@k8s-node001*Test]#*conftest*test*--policy*./conftest-checks*base.yaml*
FAIL*-*base.yaml*-*image*'docker.io/http-echo:v1.0.0'*doesn't*come*from*kubeops.net*repository

2*tests,*1*passed,*0*warnings,*1*failure,*0*exceptions

再次修改为 base.yaml,image: kubeops.net/http-echo

[root@k8s-node001*Test]#*conftest*test*--policy*./conftest-checks*base.yaml*

2*tests,*2*passed,*0*warnings,*0*failures,*0*exceptions

Polaris

Polaris 既可以安装在集群内部,也可以作为命令行工具来静态分析 Kubernetes 清单。作为命令行工具运行时,它包含多个内置检查,涉及诸如安全性和最佳实践等方面,类似于 kube-score。另外,你可以使用它来编写类似于 config-lint,copper 和 conftest 的自定义检查。换句话说,Polaris 结合了两类的优点:**内置和自定义检查器。**安装 Polaris,这里只安装命令行模式:

wget*https://github.com/FairwindsOps/polaris/releases/download/1.2.1/polaris_1.2.1_linux_amd64.tar.gz
tar*-zxf*polaris_1.2.1_linux_amd64.tar.gz
*mv*polaris*/usr/local/bin/

安装完成后,就可以使用 Polaris 对 base.yaml 进行检查。

[root@k8s-node001 Test]# polaris audit --audit-path base.yaml

结果如下,信息比较多,这里只截取部分信息,自己可以仔细看看分析出来的结果。

**"PolarisOutputVersion":*"1.0",
**"AuditTime":*"0001-01-01T00:00:00Z",
**"SourceType":*"Path",
**"SourceName":*"base.yaml",
**"DisplayName":*"base.yaml",
**"ClusterInfo":*{
****"Version":*"unknown",
****"Nodes":*0,
****"Pods":*1,
****"Namespaces":*0,
****"Controllers":*1
**},
**"Results":*[
****{
******"Name":*"http-echo",
******"Namespace":*"",
******"Kind":*"Deployment",
******"Results":*{},
******"PodResult":*{
********"Name":*"",
********"Results":*{
**********"hostIPCSet":*{
************"ID":*"hostIPCSet",
************"Message":*"Host*IPC*is*not*configured",
************"Success":*true,
************"Severity":*"danger",
************"Category":*"Security"
..............
**************"tagNotSpecified":*{
****************"ID":*"tagNotSpecified",
****************"Message":*"Image*tag*is*specified",
****************"Success":*true,
****************"Severity":*"danger",
****************"Category":*"Images"
**************}
************}
**********}
********]
******},
******"CreatedTime":*"0001-01-01T00:00:00Z"
****}
**]
}

另外,可以只输出评分

[root@k8s-node001*Test]#*polaris*audit*--audit-path*base.yaml*--format*score
66

下面使用 YAML 代码段定义了一个称为 checkImageRepo 的新检查:config_with_custom_check.yaml

checks:
**checkImageRepo:*danger

customChecks:
**checkImageRepo:
****successMessage:*Image*registry*is*valid
****failureMessage:*Image*registry*is*not*valid
****category:*Images
****target:*Container
****schema:
******'$schema':*http://json-schema.org/draft-07/schema
******type:*object
******properties:
********image:
**********type:*string
**********pattern:*^kubeops.net/.+$

现在base.yaml 的 image 为:image:docker.io/http-echo:v1.0.0,我们来使用自定义的规则执行检查

[root@k8s-node001*Test]#*polaris*audit*--config*config_with_custom_check.yaml*--audit-path*base.yaml
{
**"PolarisOutputVersion":*"1.0",
**"AuditTime":*"0001-01-01T00:00:00Z",
**"SourceType":*"Path",
**"SourceName":*"base.yaml",
**"DisplayName":*"base.yaml",
**"ClusterInfo":*{
****"Version":*"unknown",
****"Nodes":*0,
****"Pods":*1,
****"Namespaces":*0,
****"Controllers":*1
**},
**"Results":*[
****{
******"Name":*"http-echo",
******"Namespace":*"",
******"Kind":*"Deployment",
******"Results":*{},
******"PodResult":*{
********"Name":*"",
********"Results":*{},
********"ContainerResults":*[
**********{
************"Name":*"http-echo",
************"Results":*{
**************"checkImageRepo":*{
****************"ID":*"checkImageRepo",
****************"Message":*"Image*registry*is*not*valid",
****************"Success":*false,
****************"Severity":*"danger",
****************"Category":*"Images"
**************}
************}
**********}
********]
******},
******"CreatedTime":*"0001-01-01T00:00:00Z"
****}
**]
}

结果显示 "Message": "Image registry is not valid", "Success": false, 然后修改 base.yaml 的 image 为:image:kubeops.net/http-echo:v1.0.0,再次执行检查

[root@k8s-node001*Test]#*polaris*audit*--config*config_with_custom_check.yaml*--audit-path*base.yaml*
{
**"PolarisOutputVersion":*"1.0",
**"AuditTime":*"0001-01-01T00:00:00Z",
**"SourceType":*"Path",
**"SourceName":*"base.yaml",
**"DisplayName":*"base.yaml",
**"ClusterInfo":*{
****"Version":*"unknown",
****"Nodes":*0,
****"Pods":*1,
****"Namespaces":*0,
****"Controllers":*1
**},
**"Results":*[
****{
******"Name":*"http-echo",
******"Namespace":*"",
******"Kind":*"Deployment",
******"Results":*{},
******"PodResult":*{
********"Name":*"",
********"Results":*{},
********"ContainerResults":*[
**********{
************"Name":*"http-echo",
************"Results":*{
**************"checkImageRepo":*{
****************"ID":*"checkImageRepo",
****************"Message":*"Image*registry*is*valid",
****************"Success":*true,
****************"Severity":*"danger",
****************"Category":*"Images"
**************}
************}
**********}
********]
******},
******"CreatedTime":*"0001-01-01T00:00:00Z"
****}
**]
}

从输出看到 "Message": "Image registry is valid","Success": true,,检查通过。

总结

尽管有很多工具可以对 Kubernetes YAML 文件进行验证,评分和整理,但重要的是要有一个健康的模型来设计和执行检查。例如,如果你要考虑通过管道的 Kubernetes 清单,则 kubeval 可能是该管道中的第一步,因为它可以验证对象定义是否符合 Kubernetes API 模式。一旦此检查成功,你可以继续进行更详尽的测试,例如标准最佳实践和自定义策略。Kube-score 和 Polaris 是比较好的选择。如果你有复杂的要求,并且想要自定义检查的细节,则应考虑使用 copper ,config-lint 和 conftest。尽管 conftest 和 config-lint 都使用更多的 YAML 来定义自定义验证规则,但是 Copper 允许访问一种真正的编程语言,这使其颇具吸引力。

版权声明:本文不是「本站」原创文章,版权归原作者所有 | 原文地址: