验证Kubernetes YAML的最佳实践和策略

本文来自Rancher Labs

Kubernetes工作负载最常见的定义是YAML格式的文件。使用YAML所面临的挑战之一是,它相当难以表达manifest文件之间的约束或关系。

如果你想检查所有部署到集群中的镜像是否从受信任的镜像仓库中提取应该怎么做?如何防止没有PodDisruptionBudgets的部署被提交到集群?

集成静态检查可以在接近开发生命周期的时候发现错误和策略违规。而且由于围绕资源定义的有效性和安全性的保证得到了改善,你可以相信生产工作负载是遵循最佳实践的。

Kubernetes YAML文件静态检查的生态系统可以分为以下几类:

  • API验证器:这一类工具可以针对Kubernetes API服务器验证给定的YAML manifest。

  • 内置检查器:这一类工具捆绑了安全、最佳实践等方面的意见检查。

  • 自定义验证器:这一类工具允许用几种语言编写自定义检查,如Rego和Javascript。

在本文中,你将学习并比较六种不同的工具:

  • Kubeval

  • Kube-score

  • Config-lint

  • Copper

  • Conftest

  • Polaris

让我们开始吧!

验证Deployment

在开始比较工具之前,你应该设置一个基准。以下manifest并没有遵循最佳实践,可能存在一些问题,你能发现几个问题呢?

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

我们将会使用这个YAML文件来对比不同的工具。

你可以在这个git仓库中找到上面的YAML清单、文件 base-valid.yaml以及文章中提到的其他manifest:

https://github.com/amitsaha/kubernetes-static-checkers-demo

manifest描述了一个总是在5678端口回复“Hello World”消息的web应用程序。

你可以通过以下方式部署该应用程序:

kubectl apply -f hello-world.yaml

你可以使用以下命令测试它:

kubectl port-forward svc/http-echo 8080:5678

你可以访问http://localhost:8080 并确认该应用程序能否按照预期运行。但是它是否遵循了最佳实践呢?

让我们往下看。

Kubeval

主页:https://www.kubeval.com/

Kubeval的前提是,与Kubernetes的任何交互都要通过它的REST API。因此,你可以使用API模式来验证一个给定的YAML输入是否符合该模式。我们来看看一个例子。

你可以按照项目网站上的说明来安装kubeval,撰写此文时最新版本 是0.15.0。安装完成之后,让我们用前文讨论的manifest来运行它:

kubeval base-valid.yaml
PASS - base-valid.yaml contains a valid Deployment (http-echo)
PASS - base-valid.yaml contains a valid Service (http-echo)

当成功之后,kubeval退出时代码为0。你可以使用以下代码验证退出代码:

echo $?
0

现在,让我们使用另一个manifest来测试kubeval:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: http-echo
spec:
  replicas: 2
  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

你能发现问题吗?

让我们运行kubeval:

kubeval kubeval-invalid.yaml
WARN - kubeval-invalid.yaml contains an invalid Deployment (http-echo) - selector: selector is required
PASS - kubeval-invalid.yaml contains a valid Service (http-echo)

# let's check the return value
echo $?
1

资源并没有通过验证。使用app/v1 API版本的Deployment必须包含一个匹配Pod标签的selector。上面的manifest没有包含selector,针对manifest运行kubeval报告了一个错误和一个非零的退出代码。

你可能想知道,当你用上面的manifest运行kubectl apply -f时会发生什么?

让我们试一试:

kubectl apply -f kubeval-invalid.yaml
error: error validating "kubeval-invalid.yaml": error validating data: ValidationError(Deployment.spec):
missing required field "selector" in io.k8s.api.apps.v1.DeploymentSpec; if you choose to ignore these errors,
turn validation off with --validate=false

这正是kubeval警告你的错误。你可以通过添加像这样的selector来修复资源。

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

像kubeval这样的工具的好处是,你可以在部署周期的早期发现这样的错误。此外,你不需要访问集群来运行检查——它们可以离线运行。默认情况下,kubeval会根据最新的未发布的Kubernetes API模式验证资源。然而,在大多数情况下,你可能希望根据特定的Kubernetes版本运行验证。你可以使用标志--kubernetes-version来测试特定的API版本:

kubeval --kubernetes-version 1.16.1 base-valid.yaml

请注意,版本应该是Major.Minor.Patch.的形式。要查看可用于验证的版本,请查看GitHub上的JSON schema,kubeval使用它来执行验证。

如果你需要离线运行kubeval,你可以下载schemas,然后使用--schema-location标志来使用本地目录。除了单个YAML文件,你还可以针对目录以及标准输入运行kubeval。你还应该知道,Kubeval易于与你的持续集成流水线集成。如果你想在提交你的manifest到集群之前包含检查,那么kubeval支持三种输出格式也许能够对你有所帮助。

  • 纯文本

  • JSON

  • 测试任何东西协议(TAP)

而且你可以使用其中一种格式来进一步解析输出,以创建一个自定义的结果摘要。但是,kubeval存在一个局限性,就是它目前还不能对自定义资源定义(CRD)进行验证。不过kubeval可以忽略它们。

尽管Kubeval是检查和验证资源的绝佳选择,但请注意,通过测试的资源并不能保证符合最佳实践。举个例子,在容器镜像中使用最新的标签被认为不是最佳实践。然而,Kubeval并不会将其作为错误报告,它会在没有警告的情况下验证YAML。

如果你想对YAML进行打分,并抓住诸如使用最新的标签这样的违规行为怎么办?如何根据最佳实践检查你的YAML文件?

Kube-score

主页:https://github.com/zegl/kube-score

Kube-score分析YAML清单,并根据内置的检查进行评分。这些检查是根据安全建议和最佳实践而选择的,例如:

  • 以非root用户身份运行容器。

  • 为pods指定健康检查。

  • 定义资源请求和限制。

  • 检查的结果可以是OK、WARNING或CRITICAL。

你可以在线试用kube-score,也可以在本地安装。在写这篇文章时,最新的版本是1.7.0让我们试着用之前的manifest base-valid.yaml来运行它:

apps/v1/Deployment http-echo
[CRITICAL] Container Image Tag
  · http-echo -> Image with latest tag
      Using a fixed tag is recommended to avoid accidental upgrades
[CRITICAL] Pod NetworkPolicy
  · The pod does not have a matching network policy
      Create a NetworkPolicy that targets this pod
[CRITICAL] Pod Probes
  · Container is missing a readinessProbe
      A readinessProbe should be used to indicate when the service is ready to receive traffic.
      Without it, the Pod is risking to receive traffic before it has booted. It is also used during
      rollouts, and can prevent downtime if a new version of the application is failing.
      More information: https://github.com/zegl/kube-score/blob/master/README_PROBES.md
[CRITICAL] Container Security Context
  · http-echo -> Container has no configured security context
      Set securityContext to run the container in a more secure context.
[CRITICAL] Container Resources
  · http-echo -> CPU limit is not set
      Resource limits are recommended to avoid resource DDOS. Set resources.limits.cpu
  · http-echo -> Memory limit is not set
      Resource limits are recommended to avoid resource DDOS. Set resources.limits.memory
  · http-echo -> CPU request is not set
      Resource requests are recommended to make sure that the application can start and run without
      crashing. Set resources.requests.cpu
  · http-echo -> Memory request is not set
      Resource requests are recommended to make sure that the application can start and run without crashing.
      Set resources.requests.memory
[CRITICAL] Deployment has PodDisruptionBudget
  · No matching PodDisruptionBudget was found
      It is recommended to define a PodDisruptionBudget to avoid unexpected downtime during Kubernetes
      maintenance operations, such as when draining a node.
[WARNING] Deployment has host PodAntiAffinity
  · Deployment does not have a host podAntiAffinity set
      It is recommended to set a podAntiAffinity that stops multiple pods from a deployment from
      being scheduled on the same node. This increases availability in case the node becomes unavailable.

YAML文件通过了kubeval检查,但kube-score指出了几个不足之处。

  • 缺少了readiness probe

  • 缺少内存和CPU请求和限制。

  • 缺少Poddisruptionbudgets

  • 缺少反亲和规则以最大化可用性。

  • 容器以root身份运行。

这些都是你应该解决的有效点,以使你的部署更加健壮和可靠。kube-score命令会输出一个可读性高的结果,包含所有的WARNING和CRITICAL违规行为,这在开发过程中是非常好的。如果你打算把它作为持续集成流水线的一部分,你可以用--output-format ci这个标志来使用更简洁的输出,它还可以打印级别为OK的检查:

kube-score score base-valid.yaml --output-format ci
[OK] http-echo apps/v1/Deployment
[OK] http-echo apps/v1/Deployment
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) CPU limit is not set
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) Memory limit is not set
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) CPU request is not set
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) Memory request is not set
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) Image with latest tag
[OK] http-echo apps/v1/Deployment
[CRITICAL] http-echo apps/v1/Deployment: The pod does not have a matching network policy
[CRITICAL] http-echo apps/v1/Deployment: Container is missing a readinessProbe
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) Container has no configured security context
[CRITICAL] http-echo apps/v1/Deployment: No matching PodDisruptionBudget was found
[WARNING] http-echo apps/v1/Deployment: Deployment does not have a host podAntiAffinity set
[OK] http-echo v1/Service
[OK] http-echo v1/Service
[OK] http-echo v1/Service
[OK] http-echo v1/Service

与kubeval类似,当有一个CRITICAL检查失败时,kube-score会返回一个非零的退出代码,但你配置它在WARNINGs时也会失败。还有一个内置的检查来验证不同API版本的资源,类似于kubeval。然而,这些信息是硬编码在kube-score本身,你不能选择不同的Kubernetes版本。因此,如果你升级你的集群或你有几个不同的集群运行不同的版本,这可能会限制你使用这一工具。

请注意,有一个open issue可以实现这个功能。你可以在官方网站上了解更多关于kube-score的信息:https://github.com/zegl/kube-score

Kube-score检查是执行最佳实践的优秀工具,但如果你想自定义,或者添加自己的规则呢?暂时不可以,Kube-score的设计不是可扩展的,你不能添加或调整政策。如果你想写自定义检查来遵守你的组织政策,你可以使用接下来的四个选项之——config-lint、copper、conftest或polaris。

Config-lint

Config-lint是一个旨在验证以YAML、JSON、Terraform、CSV和Kubernetes manifest编写的配置文件的工具。你可以使用项目网站上的说明安装它:

https://stelligent.github.io/config-lint/#/install

在撰写本文时,最新的版本是1.5.0。

Config-lint没有内置对Kubernetes manifest的检查。你必须编写自己的规则来执行验证。这些规则被写成YAML文件,称为规则集,具有以下结构:

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

让我们来详细看看。type字段表示你将用config-lint检查什么类型的配置——一般是Kubernetes manifest。

files字段除了接受单个文件外,还接受一个目录作为输入。

rules字段是你可以定义自定义检查的地方。比方说,你希望检查Deployment中的镜像是否总是从受信任的镜像仓库(如my-company.com/myapp: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: "my-company.com/"

每条规则必须具有以下属性。

  • id——这是对规则的唯一标识。

  • severity——它必须是FAILURE、WARNING和NON_COMPLIANT中的一个。

  • message——如果违反了一个规则,这个字符串的内容会被显示出来。

  • resource——你希望这个规则被应用到的资源种类。

  • assertions——将对指定资源进行评估的条件列表。

在上面的规则中,every assertion检查每个容器中的Deployment(key:spec.templates.spec.contains)是否使用受信任的镜像(即以"my-company.com/"开头的镜像)。

完整的规则集看起来如下:

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: "my-company.com/"

如果你想要测试检查,你可以将规则集保存为check_image_repo.yaml

现在,让我们对base-valid.yaml文件进行验证。


config-lint -rules check_image_repo.yaml base-valid.yaml
[
  {
  "AssertionMessage": "Every expression fails: And expression fails: image does not start with my-company.com/",
  "Category": "",
  "CreatedAt": "2020-06-04T01:29:25Z",
  "Filename": "test-data/base-valid.yaml",
  "LineNumber": 0,
  "ResourceID": "http-echo",
  "ResourceType": "Deployment",
  "RuleID": "DEPLOYMENT_IMAGE_REPOSITORY",
  "RuleMessage": "Deployment must use a valid image repository",
  "Status": "FAILURE"
  }
]

它失败了。现在,让我们考虑以下manifest和有效的镜像仓库:

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: my-company.com/http-echo:1.0
        args: ["-text", "hello-world"]
        ports:
        - containerPort: 5678

使用以上manifest运行相同的检查并且将不会报告违规:

config-lint -rules check_image_repo.yaml image-valid-mycompany.yaml
[]

Config-lint是一个很有前途的框架,它可以让你使用YAML DSL为Kubernetes YAML manifest编写自定义检查。但如果你想表达更复杂的逻辑和检查呢?是不是YAML的限制性太大?如果你能用真正的编程语言来表达这些检查呢?

Copper

主页:https://github.com/cloud66-oss/copper

Copper V2是一个使用自定义检查来验证清单的框架——就像config-lint一样。然而,Copper并没有使用YAML来定义检查。取而代之的是,测试是用JavaScript编写的,Copper提供了一个库,里面有一些基本的帮助程序来协助读取Kubernetes对象和报告错误。

你可以按照官方文档来安装Copper。在写这篇文章的时候,最新的版本是2.0.1:

https://github.com/cloud66-oss/copper#installation

与config-lint类似,Copper没有内置检查。让我们写一个检查,以确保部署只能从受信任的仓库(如my-company.com)拉取容器镜像。创建一个新文件check_image_repo.js,内容如下:

$$.forEach(function($){
    if ($.kind === 'Deployment') {
        $.spec.template.spec.containers.forEach(function(container) {
            var image = new DockerImage(container.image);
            if (image.registry.lastIndexOf('my-company.com/') != 0) {
                errors.add_error('no_company_repo',"Image " + $.metadata.name + " is not from my-company.com repo", 1)
            }
        });
    }
});

现在,要根据我们的base-valid.yaml manifest运行这项检查,可以使用copper validate命令:

copper validate --in=base-valid.yaml --validator=check_image_tag.js
Check no_company_repo failed with severity 1 due to Image http-echo is not from my-company.com repo
Validation failed

正如你所想的,你可以编写更复杂的检查,比如验证Ingress manifest的域名,或者拒绝任何作为特权运行的Pod。Copper有一些内置的助手:

DockerImage函数读取指定的输入文件并创建一个包含以下属性的对象:

  • name-包含镜像名称

  • tag-包含镜像tag

  • registry-镜像仓库

  • registry_url-包含协议和镜像仓库

  • fqin代表整个完全合格的镜像位置。

  • findByName函数可以帮助从输入文件中找到给定kind和name的资源。
  • findByLabels函数可以帮助查找资源提供的kindlabels

你可以在这里看到所有可用的帮助程序:

https://github.com/cloud66-oss/copper/tree/master/libjs

默认情况下,它将整个输入的YAML文件加载到$$变量中,并使其在你的脚本中可用(如果你过去使用jQuery,你可能会发现这个模式很熟悉)。

除了不用学习自定义语言外,你还可以使用整个JavaScript语言来编写你的检查,如字符串插值、函数等。值得注意的是,目前的copper版本嵌入了ES5版本的JavaScript引擎,而不是ES6。想要了解更多,可以访问项目官网:

https://github.com/cloud66-oss/copper

如果Javascript不是你的首选语言,或者你更喜欢用于查询和描述策略的语言,你应该看看conftest。

Conftest

Conftest是一个配置数据的测试框架,可用于检查和验证Kubernetes manifest。测试使用专门构建的查询语言Rego编写。

你可以按照项目网站上的说明安装conftest,在撰写本文时,最新的版本是0.18.2:

https://www.conftest.dev/install/

与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, "my-company.com/")
  msg := sprintf("image '%v' doesn't come from my-company.com repository", [image])
}

现在让我们运行conftest来验证manifest base-valid.yaml

conftest test --policy ./conftest-checks base-valid.yaml
FAIL - base-valid.yaml - image 'hashicorp/http-echo' doesn't come from my-company.com repository
1 tests, 1 passed, 0 warnings, 1 failure

当然,它是失败的,因为镜像不受信任。上面的Rego文件指定了一个deny块,当为true时就会评估为违规。当你有多个deny块时,conftest会独立检查它们,总体结果是任何一个块的违规都会导致整体违规。

除了默认的输出格式外,conftest还支持JSON、TAP和通过--output标志的表格格式,如果你希望将报告与现有的持续集成流水线集成,那么这些格式将会很有帮助。为了帮助调试策略,conftest有一个方便的--trace标志,它可以打印conftest如何解析指定策略文件的跟踪。

Conftest策略可以作为artefacts在OCI(Open Container Initiative)仓库中发布和共享。命令push和pull允许发布一个工件和从远程仓库中提取一个现有的artefact。

让我们看看使用conftest push将上述策略发布到本地docker仓库的演示。使用以下命令启动本地docker仓库:

docker run -it --rm -p 5000:5000 registry

从另一个终端,导航到上面创建的conftest-checks目录,并运行以下命令:

conftest push 127.0.0.1:5000/amitsaha/opa-bundle-example:latest

该命令应成功完成,并显示以下信息:

2020/06/10 14:25:43 pushed bundle with digest: sha256:e9765f201364c1a8a182ca637bc88201db3417bacc091e7ef8211f6c2fd2609c

现在,创建一个临时目录,运行conftest pull命令,将上述bundle下载到临时目录中:

cd $(mktemp -d)
conftest pull 127.0.0.1:5000/amitsaha/opa-bundle-example:latest

你会看到,在包含之前push的策略文件的临时目录中,有一个新的子目录策略:

tree
.
└── policy
  └── check_image_registry.rego

你甚至可以直接从仓库中运行测试:

conftest test --update 127.0.0.1:5000/amitsaha/opa-bundle-example:latest base-valid.yaml
..
FAIL - base-valid.yaml - image 'hashicorp/http-echo' doesn't come from my-company.com repository
2 tests, 1 passed, 0 warnings, 1 failure

不幸的是,DockerHub还不是支持的镜像仓库之一。然而,如果你正在使用Azure容器仓库(ACR)或运行你的容器仓库,可能会通过测试。

artefact格式与开放策略代理 (OPA) 绑定使用的格式相同,这使得使用 conftest 从现有的 OPA 绑定中运行测试成为可能。

你可以在官方网站上了解更多关于共享策略和conftest的其他功能:

https://www.conftest.dev/

Polaris

主页:https://github.com/FairwindsOps/polaris

本文将探讨的最后一个工具是polaris。Polaris既可以安装在集群内部,也可以作为命令行工具静态地分析Kubernetes manifest。当作为命令行工具运行时,它包括几个内置的检查,涵盖安全和最佳实践等领域,类似于kube-score。此外,你还可以用它来编写类似config-lint、copper和conftest的自定义检查。换句话说,polaris结合了两个类别中最好的:内置和自定义检查器。

你可以按照项目网站上的说明安装polaris命令行工具。在写这篇文章的时候,最新的版本是1.0.3:

https://github.com/FairwindsOps/polaris/blob/master/docs/usage.md#cli

安装完成后,你可以使用以下命令针对base-valid.yaml manifest运行polaris:

polaris audit --audit-path base-valid.yam

上述命令将打印一个JSON格式的字符串,详细说明运行的检查和每个测试的结果。输出结果的结构如下:

{
  "PolarisOutputVersion": "1.0",
  "AuditTime": "0001-01-01T00:00:00Z",
  "SourceType": "Path",
  "SourceName": "test-data/base-valid.yaml",
  "DisplayName": "test-data/base-valid.yaml",
  "ClusterInfo": {
    "Version": "unknown",
    "Nodes": 0,
    "Pods": 2,
    "Namespaces": 0,
    "Controllers": 2
  },
  "Results": [
    /* long list */
  ]
}

你可以在下方链接中获取完整的输出:

https://github.com/amitsaha/kubernetes-static-checkers-demo/blob/master/base-valid-polaris-result.json

与kube-score类似,polaris也发现了一些manifest未达到建议的最佳实践的情况,其中包括:

  • 缺少健康检查的pod。

  • 容器镜像没有指定标签。

  • 容器以root身份运行。

  • 没有设置CPU和内存请求和限制。

  • 每项检查都被划分为警告或危险的严重程度。

要了解有关当前内置检查的更多信息,请参阅文档:

https://github.com/FairwindsOps/polaris/blob/master/docs/usage.md#checks

如果你对详细的结果不感兴趣,传递标志--format score会打印一个范围为1-100的数字,polaris将其称为分数(score):

polaris audit --audit-path test-data/base-valid.yaml --format score
68

分数越接近100,符合度越高。如果你检查polaris audit命令的退出代码,你会发现它是0。要使polaris审计退出时的代码是非0,可以利用另外两个标志。

--set-exit-code-below-score标志接受范围为1-100的阈值分数,当分数低于阈值时,将以4的退出代码退出。当你的基线分数是75分,而你想在分数低于75分时发出警报时,这非常有用。

当任何危险检查失败时,--set-exit-code-on-danger标志将以3的退出代码退出。

现在让我们看看如何为polaris定义一个自定义检查,以测试Deployment中的容器镜像是否来自可信任的镜像仓库。自定义检查以YAML格式定义,测试本身使用JSON Schema描述。下面的YAML代码段定义了一个新的检查checkImageRepo:

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: ^my-company.com/.+$

让我们仔细看看:

  • successMessage是检查成功时显示的字符串。

  • failureMessage是指当测试不成功时显示的信息。

  • category指的是其中一个类别—镜像、健康检查、安全、网络和资源。

  • target是一个字符串,用于确定检查所针对的规范对象,应该是Container、Pod或Controller中的一个。

  • 测试本身是在schema对象中使用JSON模式定义的。这里的检查使用模式关键字来匹配镜像是否来自允许的仓库。

要运行上面定义的检查,你需要创建一个Polaris配置文件,如下所示:

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: ^my-company.com/.+$

让我们来分析一下这个文件。

  • check字段指定了检查和它们的严重性。由于你想在镜像不受信任时发出警报,所以checkImageRepo被分配了一个danger严重程度。

  • 然后在customChecks对象中定义checkImageRepo检查本身。

你可以将上面的文件保存为custom_check.yaml,然后用你想要验证的YAML manifest运行polaris audit

你可以用base-valid.yaml manifest进行测试:

polaris audit --config custom_check.yaml --audit-path base-valid.yaml

你会发现,polaris audit只运行了上面定义的自定义检查,但没有成功。如果你将容器镜像修改为my-company.com/http-echo:1.0,polaris将报告成功。Github仓库中包含了修改后的manifest,所以你可以根据image-valid-mycompany.yaml manifest测试前面的命令。

但是如何同时运行内置和自定义检查呢?上面的配置文件应该更新所有内置的检查标识符,看起来应该如下:

checks:
  cpuRequestsMissing: warning
  cpuLimitsMissing: warning
  # Other inbuilt checks..
  # ..
  # custom 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: ^my-company.com/.+$

你可以在这里看到一个完整的配置文件的例子:

https://github.com/amitsaha/kubernetes-static-checkers-demo/blob/master/polaris-configs/config_with_custom_check.yaml

你可以用自定义和内置检查来测试base-valid.yaml manifest:

polaris audit --config config_with_custom_check.yaml --audit-path base-valid.yaml

Polaris用你的自定义检查增强了内置检查,从而结合了两种方式的最佳状态。然而,如果不能使用更强大的语言,如Rego或JavaScript,可能会限制编写更复杂的检查。

要了解更多关于polaris的信息,请查看项目网站:

https://github.com/FairwindsOps/polaris

总 结

虽然有很多工具可以验证、打分和精简Kubernetes YAML文件,但重要的是要有一个心理模型来了解你将如何设计和执行检查。举个例子,如果你想让Kubernetes manifest通过一个流水线,kubeval可以是这样一个流水线的第一步,因为它验证对象定义是否符合Kubernetes API模式。一旦这项检查成功,也许你可以继续进行更详细的测试,比如标准最佳实践和自定义策略。Kube-score和polaris在这里是最优秀的选择。

如果你有复杂的需求,并且想要自定义检查的细节,你应该考虑copper、config-lint和conftest。虽然conftest和config-lint都使用了更多的YAML来定义自定义验证规则,但copper给你提供了一个真正的编程语言,使其相当有吸引力。但是,你应该使用其中的一个,从头开始写所有的检查,还是应该使用Polaris,只写额外的自定义检查?这要根据情况而定。

你可能感兴趣的:(验证Kubernetes YAML的最佳实践和策略)