驗(yàn)證Kubernetes YAML的最佳實(shí)踐和策略

本文來(lái)自Rancher Labs

Kubernetes工作負(fù)載最常見(jiàn)的定義是YAML格式的文件靖榕。使用YAML所面臨的挑戰(zhàn)之一是麸拄,它相當(dāng)難以表達(dá)manifest文件之間的約束或關(guān)系弯蚜。

如果你想檢查所有部署到集群中的鏡像是否從受信任的鏡像倉(cāng)庫(kù)中提取應(yīng)該怎么做?如何防止沒(méi)有PodDisruptionBudgets的部署被提交到集群?

集成靜態(tài)檢查可以在接近開(kāi)發(fā)生命周期的時(shí)候發(fā)現(xiàn)錯(cuò)誤和策略違規(guī)谨读。而且由于圍繞資源定義的有效性和安全性的保證得到了改善,你可以相信生產(chǎn)工作負(fù)載是遵循最佳實(shí)踐的晤锹。

Kubernetes YAML文件靜態(tài)檢查的生態(tài)系統(tǒng)可以分為以下幾類(lèi):

  • API驗(yàn)證器:這一類(lèi)工具可以針對(duì)Kubernetes API服務(wù)器驗(yàn)證給定的YAML manifest衔瓮。

  • 內(nèi)置檢查器:這一類(lèi)工具捆綁了安全、最佳實(shí)踐等方面的意見(jiàn)檢查抖甘。

  • 自定義驗(yàn)證器:這一類(lèi)工具允許用幾種語(yǔ)言編寫(xiě)自定義檢查热鞍,如Rego和Javascript。

在本文中衔彻,你將學(xué)習(xí)并比較六種不同的工具:

  • Kubeval

  • Kube-score

  • Config-lint

  • Copper

  • Conftest

  • Polaris

讓我們開(kāi)始吧薇宠!

驗(yàn)證Deployment

在開(kāi)始比較工具之前,你應(yīng)該設(shè)置一個(gè)基準(zhǔn)艰额。以下manifest并沒(méi)有遵循最佳實(shí)踐澄港,可能存在一些問(wèn)題,你能發(fā)現(xiàn)幾個(gè)問(wèn)題呢柄沮?

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

我們將會(huì)使用這個(gè)YAML文件來(lái)對(duì)比不同的工具回梧。

你可以在這個(gè)git倉(cāng)庫(kù)中找到上面的YAML清單、文件 base-valid.yaml以及文章中提到的其他manifest:

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

manifest描述了一個(gè)總是在5678端口回復(fù)“Hello World”消息的web應(yīng)用程序祖搓。

你可以通過(guò)以下方式部署該應(yīng)用程序:

kubectl apply -f hello-world.yaml

你可以使用以下命令測(cè)試它:

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

你可以訪問(wèn)http://localhost:8080 并確認(rèn)該應(yīng)用程序能否按照預(yù)期運(yùn)行狱意。但是它是否遵循了最佳實(shí)踐呢?

讓我們往下看拯欧。

Kubeval

主頁(yè):https://www.kubeval.com/

Kubeval的前提是详囤,與Kubernetes的任何交互都要通過(guò)它的REST API。因此镐作,你可以使用API模式來(lái)驗(yàn)證一個(gè)給定的YAML輸入是否符合該模式藏姐。我們來(lái)看看一個(gè)例子。

你可以按照項(xiàng)目網(wǎng)站上的說(shuō)明來(lái)安裝kubeval该贾,撰寫(xiě)此文時(shí)最新版本 是0.15.0羔杨。安裝完成之后,讓我們用前文討論的manifest來(lái)運(yùn)行它:

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

當(dāng)成功之后杨蛋,kubeval退出時(shí)代碼為0兜材。你可以使用以下代碼驗(yàn)證退出代碼:

echo $?
0

現(xiàn)在,讓我們使用另一個(gè)manifest來(lái)測(cè)試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

你能發(fā)現(xiàn)問(wèn)題嗎六荒?

讓我們運(yùn)行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

資源并沒(méi)有通過(guò)驗(yàn)證护姆。使用app/v1 API版本的Deployment必須包含一個(gè)匹配Pod標(biāo)簽的selector。上面的manifest沒(méi)有包含selector掏击,針對(duì)manifest運(yùn)行kubeval報(bào)告了一個(gè)錯(cuò)誤和一個(gè)非零的退出代碼卵皂。

你可能想知道,當(dāng)你用上面的manifest運(yùn)行kubectl apply -f時(shí)會(huì)發(fā)生什么砚亭?

讓我們?cè)囈辉嚕?/p>

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警告你的錯(cuò)誤灯变。你可以通過(guò)添加像這樣的selector來(lái)修復(fù)資源殴玛。

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這樣的工具的好處是,你可以在部署周期的早期發(fā)現(xiàn)這樣的錯(cuò)誤添祸。此外滚粟,你不需要訪問(wèn)集群來(lái)運(yùn)行檢查——它們可以離線運(yùn)行。默認(rèn)情況下刃泌,kubeval會(huì)根據(jù)最新的未發(fā)布的Kubernetes API模式驗(yàn)證資源凡壤。然而,在大多數(shù)情況下耙替,你可能希望根據(jù)特定的Kubernetes版本運(yùn)行驗(yàn)證亚侠。你可以使用標(biāo)志--kubernetes-version來(lái)測(cè)試特定的API版本:

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

請(qǐng)注意,版本應(yīng)該是Major.Minor.Patch.的形式俗扇。要查看可用于驗(yàn)證的版本硝烂,請(qǐng)查看GitHub上的JSON schema,kubeval使用它來(lái)執(zhí)行驗(yàn)證铜幽。

如果你需要離線運(yùn)行kubeval滞谢,你可以下載schemas,然后使用--schema-location標(biāo)志來(lái)使用本地目錄除抛。除了單個(gè)YAML文件狮杨,你還可以針對(duì)目錄以及標(biāo)準(zhǔn)輸入運(yùn)行kubeval。你還應(yīng)該知道镶殷,Kubeval易于與你的持續(xù)集成流水線集成禾酱。如果你想在提交你的manifest到集群之前包含檢查,那么kubeval支持三種輸出格式也許能夠?qū)δ阌兴鶐椭?/p>

  • 純文本

  • JSON

  • 測(cè)試任何東西協(xié)議(TAP)

而且你可以使用其中一種格式來(lái)進(jìn)一步解析輸出绘趋,以創(chuàng)建一個(gè)自定義的結(jié)果摘要。但是颗管,kubeval存在一個(gè)局限性陷遮,就是它目前還不能對(duì)自定義資源定義(CRD)進(jìn)行驗(yàn)證。不過(guò)kubeval可以忽略它們垦江。

盡管Kubeval是檢查和驗(yàn)證資源的絕佳選擇帽馋,但請(qǐng)注意,通過(guò)測(cè)試的資源并不能保證符合最佳實(shí)踐比吭。舉個(gè)例子绽族,在容器鏡像中使用最新的標(biāo)簽被認(rèn)為不是最佳實(shí)踐。然而衩藤,Kubeval并不會(huì)將其作為錯(cuò)誤報(bào)告吧慢,它會(huì)在沒(méi)有警告的情況下驗(yàn)證YAML。

如果你想對(duì)YAML進(jìn)行打分赏表,并抓住諸如使用最新的標(biāo)簽這樣的違規(guī)行為怎么辦检诗?如何根據(jù)最佳實(shí)踐檢查你的YAML文件匈仗?

Kube-score

主頁(yè):https://github.com/zegl/kube-score

Kube-score分析YAML清單,并根據(jù)內(nèi)置的檢查進(jìn)行評(píng)分逢慌。這些檢查是根據(jù)安全建議和最佳實(shí)踐而選擇的悠轩,例如:

  • 以非root用戶身份運(yùn)行容器。

  • 為pods指定健康檢查攻泼。

  • 定義資源請(qǐng)求和限制火架。

  • 檢查的結(jié)果可以是OK、WARNING或CRITICAL忙菠。

你可以在線試用kube-score何鸡,也可以在本地安裝。在寫(xiě)這篇文章時(shí)只搁,最新的版本是1.7.0讓我們?cè)囍弥暗?code>manifest base-valid.yaml來(lái)運(yùn)行它:

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文件通過(guò)了kubeval檢查音比,但kube-score指出了幾個(gè)不足之處。

  • 缺少了readiness probe

  • 缺少內(nèi)存和CPU請(qǐng)求和限制氢惋。

  • 缺少Poddisruptionbudgets

  • 缺少反親和規(guī)則以最大化可用性洞翩。

  • 容器以root身份運(yùn)行。

這些都是你應(yīng)該解決的有效點(diǎn)焰望,以使你的部署更加健壯和可靠骚亿。kube-score命令會(huì)輸出一個(gè)可讀性高的結(jié)果,包含所有的WARNING和CRITICAL違規(guī)行為熊赖,這在開(kāi)發(fā)過(guò)程中是非常好的来屠。如果你打算把它作為持續(xù)集成流水線的一部分,你可以用--output-format ci這個(gè)標(biāo)志來(lái)使用更簡(jiǎn)潔的輸出震鹉,它還可以打印級(jí)別為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類(lèi)似俱笛,當(dāng)有一個(gè)CRITICAL檢查失敗時(shí),kube-score會(huì)返回一個(gè)非零的退出代碼传趾,但你配置它在WARNINGs時(shí)也會(huì)失敗迎膜。還有一個(gè)內(nèi)置的檢查來(lái)驗(yàn)證不同API版本的資源,類(lèi)似于kubeval浆兰。然而磕仅,這些信息是硬編碼在kube-score本身,你不能選擇不同的Kubernetes版本簸呈。因此榕订,如果你升級(jí)你的集群或你有幾個(gè)不同的集群運(yùn)行不同的版本,這可能會(huì)限制你使用這一工具蜕便。

請(qǐng)注意劫恒,有一個(gè)open issue可以實(shí)現(xiàn)這個(gè)功能。你可以在官方網(wǎng)站上了解更多關(guān)于kube-score的信息:https://github.com/zegl/kube-score

Kube-score檢查是執(zhí)行最佳實(shí)踐的優(yōu)秀工具玩裙,但如果你想自定義兼贸,或者添加自己的規(guī)則呢段直?暫時(shí)不可以,Kube-score的設(shè)計(jì)不是可擴(kuò)展的溶诞,你不能添加或調(diào)整政策鸯檬。如果你想寫(xiě)自定義檢查來(lái)遵守你的組織政策,你可以使用接下來(lái)的四個(gè)選項(xiàng)之——config-lint螺垢、copper喧务、conftest或polaris。

Config-lint

Config-lint是一個(gè)旨在驗(yàn)證以YAML枉圃、JSON功茴、Terraform、CSV和Kubernetes manifest編寫(xiě)的配置文件的工具孽亲。你可以使用項(xiàng)目網(wǎng)站上的說(shuō)明安裝它:

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

在撰寫(xiě)本文時(shí)坎穿,最新的版本是1.5.0。

Config-lint沒(méi)有內(nèi)置對(duì)Kubernetes manifest的檢查。你必須編寫(xiě)自己的規(guī)則來(lái)執(zhí)行驗(yàn)證。這些規(guī)則被寫(xiě)成YAML文件摩瞎,稱為規(guī)則集,具有以下結(jié)構(gòu):

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

讓我們來(lái)詳細(xì)看看孵延。type字段表示你將用config-lint檢查什么類(lèi)型的配置——一般是Kubernetes manifest。

files字段除了接受單個(gè)文件外亲配,還接受一個(gè)目錄作為輸入尘应。

rules字段是你可以定義自定義檢查的地方。比方說(shuō)吼虎,你希望檢查Deployment中的鏡像是否總是從受信任的鏡像倉(cāng)庫(kù)(如my-company.com/myapp:1.0)中提取犬钢。實(shí)現(xiàn)這種檢查的 config-lint 規(guī)則可以是這樣的:

- 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/"

每條規(guī)則必須具有以下屬性。

  • id——這是對(duì)規(guī)則的唯一標(biāo)識(shí)思灰。

  • severity——它必須是FAILURE娜饵、WARNING和NON_COMPLIANT中的一個(gè)。

  • message——如果違反了一個(gè)規(guī)則官辈,這個(gè)字符串的內(nèi)容會(huì)被顯示出來(lái)。

  • resource——你希望這個(gè)規(guī)則被應(yīng)用到的資源種類(lèi)遍坟。

  • assertions——將對(duì)指定資源進(jìn)行評(píng)估的條件列表拳亿。

在上面的規(guī)則中,every assertion檢查每個(gè)容器中的Deployment(key:spec.templates.spec.contains)是否使用受信任的鏡像(即以"my-company.com/"開(kāi)頭的鏡像)愿伴。

完整的規(guī)則集看起來(lái)如下:

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/"

如果你想要測(cè)試檢查肺魁,你可以將規(guī)則集保存為check_image_repo.yaml

現(xiàn)在隔节,讓我們對(duì)base-valid.yaml文件進(jìn)行驗(yàn)證鹅经。


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"
  }
]

它失敗了〖徘海現(xiàn)在,讓我們考慮以下manifest和有效的鏡像倉(cāng)庫(kù):

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運(yùn)行相同的檢查并且將不會(huì)報(bào)告違規(guī):

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

Config-lint是一個(gè)很有前途的框架瘾晃,它可以讓你使用YAML DSL為Kubernetes YAML manifest編寫(xiě)自定義檢查贷痪。但如果你想表達(dá)更復(fù)雜的邏輯和檢查呢?是不是YAML的限制性太大蹦误?如果你能用真正的編程語(yǔ)言來(lái)表達(dá)這些檢查呢劫拢?

Copper

主頁(yè):https://github.com/cloud66-oss/copper

Copper V2是一個(gè)使用自定義檢查來(lái)驗(yàn)證清單的框架——就像config-lint一樣。然而强胰,Copper并沒(méi)有使用YAML來(lái)定義檢查舱沧。取而代之的是,測(cè)試是用JavaScript編寫(xiě)的偶洋,Copper提供了一個(gè)庫(kù)熟吏,里面有一些基本的幫助程序來(lái)協(xié)助讀取Kubernetes對(duì)象和報(bào)告錯(cuò)誤。

你可以按照官方文檔來(lái)安裝Copper玄窝。在寫(xiě)這篇文章的時(shí)候牵寺,最新的版本是2.0.1:

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

與config-lint類(lèi)似,Copper沒(méi)有內(nèi)置檢查哆料。讓我們寫(xiě)一個(gè)檢查缸剪,以確保部署只能從受信任的倉(cāng)庫(kù)(如my-company.com)拉取容器鏡像。創(chuàng)建一個(gè)新文件check_image_repo.js东亦,內(nèi)容如下:

$$.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)
            }
        });
    }
});

現(xiàn)在杏节,要根據(jù)我們的base-valid.yaml manifest運(yùn)行這項(xiàng)檢查,可以使用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

正如你所想的典阵,你可以編寫(xiě)更復(fù)雜的檢查奋渔,比如驗(yàn)證Ingress manifest的域名,或者拒絕任何作為特權(quán)運(yùn)行的Pod壮啊。Copper有一些內(nèi)置的助手:

DockerImage函數(shù)讀取指定的輸入文件并創(chuàng)建一個(gè)包含以下屬性的對(duì)象:

  • name-包含鏡像名稱

  • tag-包含鏡像tag

  • registry-鏡像倉(cāng)庫(kù)

  • registry_url-包含協(xié)議和鏡像倉(cāng)庫(kù)

  • fqin代表整個(gè)完全合格的鏡像位置嫉鲸。

  • findByName函數(shù)可以幫助從輸入文件中找到給定kind和name的資源。
  • findByLabels函數(shù)可以幫助查找資源提供的kindlabels歹啼。

你可以在這里看到所有可用的幫助程序:

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

默認(rèn)情況下玄渗,它將整個(gè)輸入的YAML文件加載到$$變量中,并使其在你的腳本中可用(如果你過(guò)去使用jQuery狸眼,你可能會(huì)發(fā)現(xiàn)這個(gè)模式很熟悉)藤树。

除了不用學(xué)習(xí)自定義語(yǔ)言外,你還可以使用整個(gè)JavaScript語(yǔ)言來(lái)編寫(xiě)你的檢查拓萌,如字符串插值岁钓、函數(shù)等。值得注意的是,目前的copper版本嵌入了ES5版本的JavaScript引擎屡限,而不是ES6品嚣。想要了解更多,可以訪問(wèn)項(xiàng)目官網(wǎng):

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

如果Javascript不是你的首選語(yǔ)言钧大,或者你更喜歡用于查詢和描述策略的語(yǔ)言翰撑,你應(yīng)該看看conftest。

Conftest

Conftest是一個(gè)配置數(shù)據(jù)的測(cè)試框架拓型,可用于檢查和驗(yàn)證Kubernetes manifest额嘿。測(cè)試使用專(zhuān)門(mén)構(gòu)建的查詢語(yǔ)言Rego編寫(xiě)。

你可以按照項(xiàng)目網(wǎng)站上的說(shuō)明安裝conftest劣挫,在撰寫(xiě)本文時(shí)册养,最新的版本是0.18.2:

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

與config-lint和copper類(lèi)似压固,conftest也沒(méi)有任何內(nèi)置的檢查球拦。所以我們通過(guò)編寫(xiě)一個(gè)策略來(lái)試試。和前面的例子一樣帐我,你將檢查容器是否來(lái)自一個(gè)可信的來(lái)源坎炼。

創(chuàng)建一個(gè)新的目錄,conftest-checks和一個(gè)名為check_image_registry.rego的文件拦键,內(nèi)容如下:

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])
}

現(xiàn)在讓我們運(yùn)行conftest來(lái)驗(yàn)證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

當(dāng)然谣光,它是失敗的,因?yàn)殓R像不受信任芬为。上面的Rego文件指定了一個(gè)deny塊萄金,當(dāng)為true時(shí)就會(huì)評(píng)估為違規(guī)。當(dāng)你有多個(gè)deny塊時(shí)媚朦,conftest會(huì)獨(dú)立檢查它們氧敢,總體結(jié)果是任何一個(gè)塊的違規(guī)都會(huì)導(dǎo)致整體違規(guī)。

除了默認(rèn)的輸出格式外询张,conftest還支持JSON孙乖、TAP和通過(guò)--output標(biāo)志的表格格式,如果你希望將報(bào)告與現(xiàn)有的持續(xù)集成流水線集成份氧,那么這些格式將會(huì)很有幫助唯袄。為了幫助調(diào)試策略,conftest有一個(gè)方便的--trace標(biāo)志蜗帜,它可以打印conftest如何解析指定策略文件的跟蹤越妈。

Conftest策略可以作為artefacts在OCI(Open Container Initiative)倉(cāng)庫(kù)中發(fā)布和共享。命令push和pull允許發(fā)布一個(gè)工件和從遠(yuǎn)程倉(cāng)庫(kù)中提取一個(gè)現(xiàn)有的artefact钮糖。

讓我們看看使用conftest push將上述策略發(fā)布到本地docker倉(cāng)庫(kù)的演示。使用以下命令啟動(dòng)本地docker倉(cāng)庫(kù):

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

從另一個(gè)終端,導(dǎo)航到上面創(chuàng)建的conftest-checks目錄店归,并運(yùn)行以下命令:

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

該命令應(yīng)成功完成阎抒,并顯示以下信息:

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

現(xiàn)在,創(chuàng)建一個(gè)臨時(shí)目錄消痛,運(yùn)行conftest pull命令且叁,將上述bundle下載到臨時(shí)目錄中:

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

你會(huì)看到,在包含之前push的策略文件的臨時(shí)目錄中秩伞,有一個(gè)新的子目錄策略:

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

你甚至可以直接從倉(cāng)庫(kù)中運(yùn)行測(cè)試:

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還不是支持的鏡像倉(cāng)庫(kù)之一。然而纱新,如果你正在使用Azure容器倉(cāng)庫(kù)(ACR)或運(yùn)行你的容器倉(cāng)庫(kù)展氓,可能會(huì)通過(guò)測(cè)試。

artefact格式與開(kāi)放策略代理 (OPA) 綁定使用的格式相同脸爱,這使得使用 conftest 從現(xiàn)有的 OPA 綁定中運(yùn)行測(cè)試成為可能遇汞。

你可以在官方網(wǎng)站上了解更多關(guān)于共享策略和conftest的其他功能:

https://www.conftest.dev/

Polaris

主頁(yè):https://github.com/FairwindsOps/polaris

本文將探討的最后一個(gè)工具是polaris。Polaris既可以安裝在集群內(nèi)部簿废,也可以作為命令行工具靜態(tài)地分析Kubernetes manifest空入。當(dāng)作為命令行工具運(yùn)行時(shí),它包括幾個(gè)內(nèi)置的檢查族檬,涵蓋安全和最佳實(shí)踐等領(lǐng)域歪赢,類(lèi)似于kube-score。此外单料,你還可以用它來(lái)編寫(xiě)類(lèi)似config-lint埋凯、copper和conftest的自定義檢查。換句話說(shuō)看尼,polaris結(jié)合了兩個(gè)類(lèi)別中最好的:內(nèi)置和自定義檢查器递鹉。

你可以按照項(xiàng)目網(wǎng)站上的說(shuō)明安裝polaris命令行工具。在寫(xiě)這篇文章的時(shí)候藏斩,最新的版本是1.0.3:

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

安裝完成后躏结,你可以使用以下命令針對(duì)base-valid.yaml manifest運(yùn)行polaris:

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

上述命令將打印一個(gè)JSON格式的字符串,詳細(xì)說(shuō)明運(yùn)行的檢查和每個(gè)測(cè)試的結(jié)果狰域。輸出結(jié)果的結(jié)構(gòu)如下:

{
  "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類(lèi)似媳拴,polaris也發(fā)現(xiàn)了一些manifest未達(dá)到建議的最佳實(shí)踐的情況,其中包括:

  • 缺少健康檢查的pod兆览。

  • 容器鏡像沒(méi)有指定標(biāo)簽屈溉。

  • 容器以root身份運(yùn)行。

  • 沒(méi)有設(shè)置CPU和內(nèi)存請(qǐng)求和限制抬探。

  • 每項(xiàng)檢查都被劃分為警告或危險(xiǎn)的嚴(yán)重程度子巾。

要了解有關(guān)當(dāng)前內(nèi)置檢查的更多信息帆赢,請(qǐng)參閱文檔:

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

如果你對(duì)詳細(xì)的結(jié)果不感興趣,傳遞標(biāo)志--format score會(huì)打印一個(gè)范圍為1-100的數(shù)字线梗,polaris將其稱為分?jǐn)?shù)(score):

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

分?jǐn)?shù)越接近100椰于,符合度越高。如果你檢查polaris audit命令的退出代碼仪搔,你會(huì)發(fā)現(xiàn)它是0瘾婿。要使polaris審計(jì)退出時(shí)的代碼是非0,可以利用另外兩個(gè)標(biāo)志烤咧。

--set-exit-code-below-score標(biāo)志接受范圍為1-100的閾值分?jǐn)?shù)偏陪,當(dāng)分?jǐn)?shù)低于閾值時(shí),將以4的退出代碼退出煮嫌。當(dāng)你的基線分?jǐn)?shù)是75分笛谦,而你想在分?jǐn)?shù)低于75分時(shí)發(fā)出警報(bào)時(shí),這非常有用立膛。

當(dāng)任何危險(xiǎn)檢查失敗時(shí)揪罕,--set-exit-code-on-danger標(biāo)志將以3的退出代碼退出。

現(xiàn)在讓我們看看如何為polaris定義一個(gè)自定義檢查宝泵,以測(cè)試Deployment中的容器鏡像是否來(lái)自可信任的鏡像倉(cāng)庫(kù)好啰。自定義檢查以YAML格式定義,測(cè)試本身使用JSON Schema描述儿奶。下面的YAML代碼段定義了一個(gè)新的檢查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/.+$

讓我們仔細(xì)看看:

  • successMessage是檢查成功時(shí)顯示的字符串框往。

  • failureMessage是指當(dāng)測(cè)試不成功時(shí)顯示的信息。

  • category指的是其中一個(gè)類(lèi)別—鏡像闯捎、健康檢查椰弊、安全、網(wǎng)絡(luò)和資源瓤鼻。

  • target是一個(gè)字符串秉版,用于確定檢查所針對(duì)的規(guī)范對(duì)象,應(yīng)該是Container茬祷、Pod或Controller中的一個(gè)清焕。

  • 測(cè)試本身是在schema對(duì)象中使用JSON模式定義的。這里的檢查使用模式關(guān)鍵字來(lái)匹配鏡像是否來(lái)自允許的倉(cāng)庫(kù)祭犯。

要運(yùn)行上面定義的檢查秸妥,你需要?jiǎng)?chuàng)建一個(gè)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/.+$

讓我們來(lái)分析一下這個(gè)文件沃粗。

  • check字段指定了檢查和它們的嚴(yán)重性粥惧。由于你想在鏡像不受信任時(shí)發(fā)出警報(bào),所以checkImageRepo被分配了一個(gè)danger嚴(yán)重程度最盅。

  • 然后在customChecks對(duì)象中定義checkImageRepo檢查本身突雪。

你可以將上面的文件保存為custom_check.yaml起惕,然后用你想要驗(yàn)證的YAML manifest運(yùn)行polaris audit

你可以用base-valid.yaml manifest進(jìn)行測(cè)試:

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

你會(huì)發(fā)現(xiàn)挂签,polaris audit只運(yùn)行了上面定義的自定義檢查疤祭,但沒(méi)有成功。如果你將容器鏡像修改為my-company.com/http-echo:1.0饵婆,polaris將報(bào)告成功。Github倉(cāng)庫(kù)中包含了修改后的manifest戏售,所以你可以根據(jù)image-valid-mycompany.yaml manifest測(cè)試前面的命令侨核。

但是如何同時(shí)運(yùn)行內(nèi)置和自定義檢查呢?上面的配置文件應(yīng)該更新所有內(nèi)置的檢查標(biāo)識(shí)符灌灾,看起來(lái)應(yīng)該如下:

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/.+$

你可以在這里看到一個(gè)完整的配置文件的例子:

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

你可以用自定義和內(nèi)置檢查來(lái)測(cè)試base-valid.yaml manifest:

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

Polaris用你的自定義檢查增強(qiáng)了內(nèi)置檢查搓译,從而結(jié)合了兩種方式的最佳狀態(tài)。然而锋喜,如果不能使用更強(qiáng)大的語(yǔ)言些己,如Rego或JavaScript,可能會(huì)限制編寫(xiě)更復(fù)雜的檢查嘿般。

要了解更多關(guān)于polaris的信息段标,請(qǐng)查看項(xiàng)目網(wǎng)站:

https://github.com/FairwindsOps/polaris

總 結(jié)

雖然有很多工具可以驗(yàn)證、打分和精簡(jiǎn)Kubernetes YAML文件炉奴,但重要的是要有一個(gè)心理模型來(lái)了解你將如何設(shè)計(jì)和執(zhí)行檢查逼庞。舉個(gè)例子,如果你想讓Kubernetes manifest通過(guò)一個(gè)流水線瞻赶,kubeval可以是這樣一個(gè)流水線的第一步赛糟,因?yàn)樗?yàn)證對(duì)象定義是否符合Kubernetes API模式。一旦這項(xiàng)檢查成功砸逊,也許你可以繼續(xù)進(jìn)行更詳細(xì)的測(cè)試璧南,比如標(biāo)準(zhǔn)最佳實(shí)踐和自定義策略。Kube-score和polaris在這里是最優(yōu)秀的選擇师逸。

如果你有復(fù)雜的需求司倚,并且想要自定義檢查的細(xì)節(jié),你應(yīng)該考慮copper字旭、config-lint和conftest对湃。雖然conftest和config-lint都使用了更多的YAML來(lái)定義自定義驗(yàn)證規(guī)則,但copper給你提供了一個(gè)真正的編程語(yǔ)言遗淳,使其相當(dāng)有吸引力拍柒。但是,你應(yīng)該使用其中的一個(gè)屈暗,從頭開(kāi)始寫(xiě)所有的檢查拆讯,還是應(yīng)該使用Polaris脂男,只寫(xiě)額外的自定義檢查?這要根據(jù)情況而定种呐。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末宰翅,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子爽室,更是在濱河造成了極大的恐慌汁讼,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件阔墩,死亡現(xiàn)場(chǎng)離奇詭異嘿架,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)啸箫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)耸彪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人忘苛,你說(shuō)我怎么就攤上這事蝉娜。” “怎么了扎唾?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵召川,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我稽屏,道長(zhǎng)扮宠,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任狐榔,我火速辦了婚禮坛增,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘薄腻。我一直安慰自己收捣,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布庵楷。 她就那樣靜靜地躺著罢艾,像睡著了一般。 火紅的嫁衣襯著肌膚如雪尽纽。 梳的紋絲不亂的頭發(fā)上咐蚯,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音弄贿,去河邊找鬼春锋。 笑死,一個(gè)胖子當(dāng)著我的面吹牛差凹,可吹牛的內(nèi)容都是我干的期奔。 我是一名探鬼主播侧馅,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼呐萌!你這毒婦竟也來(lái)了馁痴?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤肺孤,失蹤者是張志新(化名)和其女友劉穎罗晕,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體赠堵,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡攀例,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了顾腊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡挖胃,死狀恐怖杂靶,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情酱鸭,我是刑警寧澤吗垮,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站凹髓,受9級(jí)特大地震影響烁登,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蔚舀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一饵沧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧赌躺,春花似錦狼牺、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至缅叠,卻和暖如春悄泥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背肤粱。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工弹囚, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人狼犯。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓余寥,卻偏偏與公主長(zhǎng)得像领铐,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子宋舷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345