TL;DR:本文比较了六种静态工具来验证和评分Kubernetes YAML文件的最佳实践和遵从性。
YAML的挑战之一是很难表达清单文件之间的约束或关系。
如果您希望检查部署到集群中的所有映像是否都是从受信任的注册表中提取的,该怎么办?
如何防止没有PodDisruptionBudgets的部署提交到群集?
而且,由于围绕资源定义的有效性和安全性的保证得到了改进,因此您可以相信生产工作负载遵循最佳实践。
Kubernetes YAML文件的静态检查生态系统可以分为以下几类:
API验证器-此类别中的工具根据Kubernetes API服务器验证给定的YAML清单。
内置检查器-此类别中的工具捆绑了针对安全性、最佳实践等的自以为是的检查。
自定义验证器-此类别的工具允许使用几种语言(如Rego和Javascript)编写自定义检查。
以下清单有几个问题,并且没有遵循最佳实践-您能发现多少个问题?
ApiVersion:app/v1种类:部署元数据:名称:HTTP-ECHO规范:副本:2选择器:MatchLabels:APP:HTTP-ECHO模板:元数据:标签:APP:HTTP-ECHO规范:容器:-名称:HTTP-ECHO图像:hashicorp/http-ECHO参数:[";-text";,";hello-world";]端口:-tainerPort:5678-apiVersion:V1 Kind:服务元数据:名称:HTTP-ECHO规范:端口:-PORT:5678协议:TCP目标端口:5678选择器:APP:HTTP-ECHO。
您可以找到上面的YAML清单文件base-valid.yaml以及本文中引用的其他清单。
该清单描述了始终在端口5678上回复Hello World消息的Web应用程序。
因此,您可以使用API模式来验证给定的YAML输入是否符合该模式。
Kubeval base-valid.yaml pass-base-valid.yaml包含有效部署(http-echo)pass-base-valid.yaml包含有效服务(http-echo)。
ApiVersion:app/v1种类:部署元数据:名称:HTTP-ECHO规范:副本:2模板:元数据:标签:APP:HTTP-ECHO规范:容器:-名称:HTTP-ECHO图像:hashicorp/http-ECHO参数:[";-text";,";hello-world";]端口:-tainerPort:5678-apiVersion:V1 Kind:服务元数据:名称:HTTP-ECHO规范:端口:-PORT:5678协议:TCP目标端口:5678选择器:APP:HTTP-ECHO
Kubeval kubeval-valid.yaml warn-kubeval-valid.yaml包含无效的部署(http-echo)-选择器:需要选择器PASS-kubeval-valid.yaml包含有效的服务(http-echo)#let';s检查返回值ECHO$?1。
使用APP/v1 API版本的部署必须包括与Pod标签匹配的选择器。
上述清单不包括选择器和针对报告的清单运行kubeval的错误和非零退出代码。
您可能想知道,使用上面的清单运行kubectl application-f时会发生什么?
Kubectl Apply-f kubeval-valid.yaml错误:验证";kubeval-valid.yaml";时出错:验证数据时出错:ValidationError(Deployment.spec):io.k8s.api.apps.v1.DeploymentSpec;中缺少必填字段";选择器";如果选择忽略这些错误,请使用--VALIDATE=FALSE关闭验证。
ApiVersion:app/v1种类:部署元数据:名称:HTTP-ECHO规范:副本:2选择器:MatchLabels:APP:HTTP-ECHO模板:元数据:标签:APP:HTTP-ECHO规范:容器:-名称:HTTP-ECHO图像:hashicorp/http-ECHO参数:[";-text";,";hello-world";]端口:-tainerPort:5678-apiVersion:V1 Kind:服务元数据:名称:HTTP-ECHO规范:端口:-PORT:5678协议:TCP目标端口:5678选择器:APP:HTTP-ECHO。
像kubeval这样的工具的优势在于,您可以在部署周期的早期捕获此类错误。
此外,您不需要访问群集即可运行检查-它们可以脱机运行。
然而,在大多数情况下,您可能希望针对特定的Kubernetes版本运行验证。
要查看可用于验证的版本,请查看kubeval用于执行验证的版本。
如果需要脱机运行kubeval,可以下载模式,然后使用--schema-location标志使用本地目录。
除了单独的YAML文件之外,您还可以对目录和标准输入运行kubeval。
您还应该知道Kubeval可以轻松地与您的持续集成管道集成。
如果您希望在将清单提交到群集之前包括检查,您将很高兴知道kubeval支持三种输出格式:
您还可以使用其中一种格式进一步解析输出,以创建结果的自定义摘要。
Kubeval的一个限制是目前不能根据自定义资源定义(CRD)进行验证。
Kubeval是检查和验证资源的最佳选择,但请注意,通过测试的资源不能保证符合最佳实践。
例如,在容器图像中使用最新的标签不被认为是最佳实践。
但是,Kubeval不会将其报告为错误,并且会在没有警告的情况下验证YAML。
如果您想要给YAML评分并捕捉违规行为(如最新的标签),该怎么办?
Kube-Score base-valid.yaml apps/v1/Deployment http-echo[Critical]容器映像标记·http-echo->;建议使用固定标记使用最新标记的映像以避免意外升级[Critical]Pod NetworkPolicy·Pod没有匹配的网络策略创建针对此Pod的网络策略[Critical]Pod Probe·容器缺少可读性Probe A readinessProbe应用于指示服务何时可以接收。如果没有它,Pod将冒着在启动之前接收流量的风险。它还可以在部署期间使用,并且可以在新版本的应用程序出现故障时防止停机。详细信息:https://github.com/zegl/kube-score/blob/master/README_PROBES.md[关键]容器安全上下文·http-ECHO->;容器没有配置安全上下文集securityContext以在更安全的上下文中运行容器。[关键]容器资源·http-echo->;未设置CPU限制建议避免资源DDOS的资源限制。未设置resources.limits.cpu·http-echo->;内存限制建议使用资源限制以避免资源DDOS。不设置resource ces.limits.memory·http-echo->;CPU请求建议使用资源请求,以确保应用程序可以在不崩溃的情况下启动和运行。未设置资源请求。未设置内存请求。建议使用资源请求,以确保应用程序可以在不崩溃的情况下启动和运行。Set resource ces.request.memory[关键]部署有PodDisruptionBudget·找不到匹配的PodDisruptionBudget建议定义PodDisruptionBudget,以避免Kubernetes维护操作期间意外停机,例如清空节点。[警告]部署具有主机PodAntiAffity·部署没有设置主机PodAntiAffity,建议设置阻止在同一节点上计划部署中的多个Pod的podAntiAffity。这提高了在节点变得不可用的情况下的可用性。
这些都是您应该解决的有效问题,以使您的部署更加健壮和可靠。
Kube-SCORE命令打印一个友好的输出,其中包含所有警告和严重违规,这在开发过程中非常有用。
如果您计划将其用作持续集成管道的一部分,则可以使用带有标志--output-format ci的更简明的输出,它还会打印级别为OK的检查:
Kube-Score base-valid.yaml--output-format ci[OK]http-echo apps/v1/Deployment[OK]http-echo apps/v1/ployment[Critical]http-echo apps/v1/Deployment:(http-echo)未设置CPU限制[Critical]http-echo apps/v1/Deployment:(http-echo)未设置内存限制[Critical]http-echo apps/v1/Deployment:(http-echo)CPU请求未设置[Critical]http-ECHO APPS/v1/Deployment:(http-echo)CPU请求未设置[Critical]http-echo apps/v1/Deployment:(http-echo)CPU请求未设置[Critical]http-echo apps/v1/Deployment:(HTTP-ECHO)内存请求未设置[关键]http-ECHO APPS/v1/Deployment:(HTTP-ECHO)具有最新标记的映像[OK]http-ECHO应用程序/v1/Deployment[关键]http-ECHO应用程序/v1/Deployment:Pod没有匹配的网络策略[关键]http-ECHO应用程序/v1/Deployment:容器缺少读取可能[关键]http-ECHO应用程序/v1/Deployment:(HTTP-ECHO)容器没有配置安全上下文[关键]http-ECHO应用程序/v1/Deployment:(HTTP-ECHO)容器没有配置安全上下文[关键]http-ECHO应用程序/v1/Deployment:找到[警告]http-echo应用程序/v1/Deployment:部署没有主机podAntiAffinity设置[OK]http-echo v1/Service[OK]http-echo v1/Service。
与kubeval类似,当关键检查失败时,Kube-Score返回非零退出代码,但您将其配置为即使在出现警告时也失败。
还有一个内置的检查,可以针对不同的API版本验证资源-类似于kubeval。
然而,这个信息是硬编码在Kube-Score本身中的,您不能选择不同的Kubernetes版本。
因此,如果您升级集群,或者您有几个不同的集群运行不同的版本,这可能会被证明是一个严重的限制。
Kube-Score检查是实施最佳实践的优秀工具,但是如果您想要定制一个或添加您自己的规则怎么办?
如果您想要编写自定义检查以符合您的组织策略,您可以使用以下四个选项之一-config-lint、铜缆、conftest或Polaris。
Config-lint是一个设计用于验证用YAML、JSON、Terraform、CSV和Kubernetes清单编写的配置文件的工具。
这些规则以YAML文件的形式编写,称为规则集,具有以下结构:
Type字段指示您将使用config-lint检查的配置类型-对于Kubernetes清单,它始终是Kubernetes。
假设您希望检查部署中的映像是否总是从受信任的存储库(如my-company.com/myapp:1.0)提取的。
-id:my_Deployment_image_tag严重性:失败消息:部署必须使用有效的映像标记资源:部署断言:-Every:key:spec.template.spec.tainers表达式:-key:image op:start-with value:";my-company.com/";
在上述规则中,检查部署中的每个容器(key:spec.templates.spec.tainers)是否使用可信镜像(即镜像以";my-company.com/";开头)。
版本:1描述:Kubernetes规范文件规则类型:Kubernetes文件类型:Kubernetes文件:-";*.yaml";规则:-id:Deployment_Image_Repository严重性:失败消息:部署必须使用有效的映像存储库资源:部署断言:-Every:key:spec.template.spec.tainers表达式:-key:image op:start-with value:";my-company.com/";
Config-lint-ules check_image_repo.yaml base-valid.yaml[{";AssertionMessage";:";每个表达式都失败:表达式失败:镜像不以my-company.com/";,";类别";:";";,";CreatedAt";:";2020-06-04T01:29:25.。Test-data/base-valid.yaml";,";LineNumber";:0,";ResourceID";:";http-echo";,";ResourceType";:";Deployment";,";RuleID";:";Deployment_Image_pository";,";RuleMessage";:&。,";状态";:";故障";}]。
ApiVersion:app/v1 Kind:Deployment Metadata:Name:HTTP-ECHO SPEC:Replicas:2选择器:matchLabels:app:HTTP-ECHO模板:METADATA:Labels:App:HTTP-ECHO规范:Containers:-Name:HTTP-ECHO image:my-company.com/http-ECHO:1.0 args:[";-text";,";hello-world";]ports:-tainerPort:5678
对上述清单运行相同的检查,将不会报告任何违规行为:
Config-lint是一个很有前途的框架,它允许您使用YAML DSL为Kubernetes YAML清单编写自定义检查。
相反,测试是用JavaScript编写的,并且Cu提供了一个具有几个基本帮助器的库来帮助读取Kubernetes对象和报告错误。
让我们写一个检查,以确保部署只能从受信任的存储库(如my-company.com)提取容器映像。
$$。ForEach(function($){if($.Kind=';Deployment';){$.spec.template.spec.tainers。ForEach(函数(容器){var image=new(容器.image);if(image.regiation.。LastIndexOf(';my-company.com/';)!=0){错误。Add_error(';no_company_repo';,";Image";+$.adata.name+";不是来自my-company.com repo";,1)}});
现在,要对我们的base-valid.yaml清单运行此检查,您可以使用铜质验证命令:
铜缆验证--in=base-valid.yaml--validator=check_image_tag.js检查no_company_repo失败,严重程度为1,原因是映像http-echo不是来自my-company.com repoValidation失败。
正如您可以想象的那样,您可以编写更复杂的检查,比如验证Inress清单的域名,或者拒绝任何以特权身份运行的Pod。
DockerImage函数读取指定的输入文件并创建包含以下属性的对象:
FindByName函数可帮助从输入文件中查找给定种类和名称的资源。
默认情况下,它将整个输入YAML文件加载到$$变量中,并使其在脚本中可用(如果您过去使用jQuery,您可能会发现这种模式很熟悉)。
除了不必学习自定义语言之外,您还可以访问用于编写检查的整个JavaScript语言,如字符串插值、函数等。
值得注意的是,当前的铜版嵌入了ES5版本的JavaScript引擎,而不是ES6。
如果Javascript不是您的首选语言,而您更喜欢一种用于查询和描述策略的语言,则您应该查看conftest。
Conftest是一个配置数据测试框架,可用于检查和验证Kubernetes清单。
与前面的示例一样,您将检查容器是否来自受信任的来源。
软件包mainden[msg]{input.Kind==";Deployment";image:=input.spec.template.spe.tainers[_].image不是以(image,";my-company.com/";)msg:=sprintf(";image';%v';不是来自my-company.com存储库";,[image])}开头。
Conftest--policy./conftest-checksbase-valid.yaml FAIL-base-valid.yaml-image';hashicorp/http-echo';不是来自my-company.com存储库1个测试,1个通过,0个警告,1个失败。
上面的rego文件指定了一个拒绝块,当为true时,该块的计算结果为违规。
当您有多个拒绝块时,conftest会单独检查它们,总体结果是任何块的违规都会导致违规。
除了默认的输出格式之外,conftest还通过--output标志支持JSON、TAP和表格格式,如果您希望将报告与现有的持续集成管道集成,这是非常棒的。
为了帮助调试策略,conftest有一个方便的--trace标志,它打印有关conftest如何解析指定的策略文件的跟踪信息。
Conftest策略可以在OCI(Open Container Initiative,开放容器倡议)注册表中作为工件发布和共享。
命令PUSH和PULL允许发布人工产物并从远程注册表中拉出现有的人工产物。
让我们看一个使用confltest推送将上述策略发布到本地docker注册表的演示。
从另一个终端导航到上面创建的conftest-check目录,然后运行以下命令:
现在,创建一个临时目录并运行conftest Pull命令,该命令会将上面的捆绑包下载到临时目录:
您将看到临时目录中有一个新的子目录策略,其中包含前面推送的策略文件:
Confltest--更新127.0.0.1:5000/amitsaha/opa-bundle-example:latest库-VALID.yaml..失败-BASE-VALID.yaml-Image';hashicorp/Http-ECHO';不是来自my-company.com存储库2个测试,1个通过,0个警告,1个失败。
但是,如果您正在使用或运行容器注册表,那么您可能会走运。
,这使得使用Confltest从现有的OPA捆绑包运行测试成为可能。
Polaris可以安装在集群中,也可以作为命令行工具静态分析Kubernetes清单。
作为命令行工具运行时,它包括几个内置检查,涵盖安全和最佳实践等领域-类似于Kube-SCORE。
此外,您还可以使用它编写与config-lint、cub和conftest类似的自定义检查。
换句话说,北极星结合了两个类别中的最好:内置和自定义检查器。
上面的命令将打印一个JSON格式的字符串,详细说明运行的检查和每个测试的结果。
{";PolarisOutputVersion";:";1.0";,";AuditTime";:";0001-01-01T00:00:00Z";,";SourceType";:";路径";,";SourceName";:";test-data/base-valid.yaml";,&。Test-data/base-valid.yaml";,";ClusterInfo";:{";版本";:";未知";,";节点";:0,";Pods";:2,";命名空间";:0,";控制器";:2},";结果";:[/*长列表*/]}。
与Kube-Score类似,Polaris确定了清单不符合推荐最佳做法的几种情况,其中包括:
如果您对详细结果不感兴趣,传递标志--格式分数将打印1-100范围内的数字,北极星将其称为分数:
如果检查Polaris AUDIT命令的退出代码,您会发现它是0。
要使用非零代码使Polaris审计退出,您可以使用另外两个标志。
SET-EXIT-CODE-BREST-SCORE标志接受1-100范围内的阈值分数,当分数低于阈值时将退出,退出代码为4。
这在基线分数为75分的情况下非常有用,并且您希望在分数降低时得到警告。
当任何危险检查失败时,--set-exit-code-on-anger标志将退出,退出代码为3。
现在让我们看看如何为Polaris定义自定义检查,以测试部署中的容器映像是否来自受信任的注册表。
自定义检查以YAML格式定义,并使用JSON Schema描述测试本身。
CheckImageRepo:成功消息:图像注册表有效失败消息:图像注册表无效类别:图像目标:容器架构:';$schema';:http://json-schema.org/draft-07/schema type:对象属性:image:type:string pattern:^my-company.com/.+$。
类别是指其中一个类别-映像、运行状况检查、安全、网络和资源。
Target是确定对哪个规范对象应用检查的字符串,应该是Container、Pod或。
.