Kubernetes YAML最佳實(shí)踐和策略

Kubernetes工作負(fù)載最常用YAML格式的文件來定義。

YAML的問題之一就是很難描述清單文件之間的約束或關(guān)系拷姿。
如果你希望檢查是否已從受信任的注冊(cè)表中提取部署到群集中的所有映像惭载,該怎么辦?
如何防止沒有Pod安全策略的工作負(fù)載提交到集群响巢?
集成靜態(tài)檢查可以在更接近開發(fā)生命周期的時(shí)間內(nèi)捕獲錯(cuò)誤和違反策略的行為描滔。
并且由于改善了資源定義的有效性和安全性,因此你可以相信生產(chǎn)工作負(fù)載遵循最佳實(shí)踐踪古。

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

  • API驗(yàn)證程序:此類工具針對(duì)Kubernetes API服務(wù)器驗(yàn)證給定的YAML清單含长。
  • 內(nèi)置檢查器:此類工具捆綁了針對(duì)安全性靶衍,最佳實(shí)踐等的自覺檢查。
  • 自定義驗(yàn)證器:此類工具允許使用多種語言(例如python和Javascript)編寫自定義檢查茎芋。

在本文中颅眶,你將學(xué)習(xí)到六個(gè)不同的工具:

Kubeval
Kube-score
Config-lint
Copper
Conftest
Polaris

Let's Go ~~~

基準(zhǔn)服務(wù)

首先部署一個(gè)基準(zhǔn)服務(wù),以便后面測試對(duì)比

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

部署完成并驗(yàn)證如下:

[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文件能部署成功田弥,但是涛酗,它遵循最佳做法嗎?

Let's start.

kubeval

kubeval的前提是與Kubernetes的任何交互都通過其REST API進(jìn)行偷厦。
因此商叹,可以使用API模式來驗(yàn)證給定的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

現(xiàn)在我們來改下base.yaml,刪除

  selector:
    matchLabels:
      app: http-echo

然后使用kubeval對(duì)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)

輸出看到一個(gè)WARN只泼,提示selector是必須的字段
然后恢復(fù)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之類的工具的優(yōu)勢在于,咱們可以在部署周期的早期發(fā)現(xiàn)此類錯(cuò)誤请唱。
另外弥咪,您不需要訪問集群即可運(yùn)行檢查-它們可以脫機(jī)運(yùn)行。
默認(rèn)情況下十绑,kubeval會(huì)根據(jù)最新的未發(fā)布的Kubernetes API模式驗(yàn)證資源聚至。
更多用法詳情請(qǐng)參見官網(wǎng)

kube-score

kube-score會(huì)對(duì)你提供的YAML清單進(jìn)行分析,并針對(duì)集群的內(nèi)置檢查對(duì)其進(jìn)行評(píng)分本橙。
kube-score提供在線版和離線
本文偷懶就用在線版了
首先打開https://kube-score.com/ 扳躬,然后在輸入框貼入寫好的YAML清單,這里以上文base.yaml來分析

image.png

解析結(jié)果如下

image.png

從如上可以看到針對(duì)這個(gè)文件給出的建議甚亭,比如資源限制贷币、鏡像TAG、Pod網(wǎng)絡(luò)策略等亏狰。不錯(cuò)吧役纹,非常好用的工具。骚揍。字管。

當(dāng)然啰挪,kube-score并不可擴(kuò)展信不,并且您不能添加或調(diào)整策略。
如果要編寫自定義檢查以符合組織策略亡呵,則可以使用以下四個(gè)工具之一:config-lint抽活,copper,conftest或Polaris锰什。

Config-lint

Config-lint是用于驗(yàn)證以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并沒有對(duì)Kubernetes清單進(jìn)行內(nèi)置檢查霜幼。你必須編寫自己的規(guī)則才能執(zhí)行任何驗(yàn)證。
規(guī)則被寫為YAML文件誉尖,稱為規(guī)則集罪既,并具有以下結(jié)構(gòu):

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

假設(shè)咱們希望檢查部署中的鏡像是否總是從可信任的倉庫(例如kubeops.net/app:1.0 )中提取。
實(shí)施此類檢查的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: "kubeops.net/"

一個(gè)完整的規(guī)則集如下所示:

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

如果要測試檢查铡恕,可以將規(guī)則集另存為check_image_repo.yaml琢感。
然后使用config-lint執(zhí)行檢查

[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,檢測不通過探熔。
現(xiàn)在我們來改下images地址為image: kubeops.net/http-echo,再來檢查一次

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

輸出不報(bào)錯(cuò)即為成功驹针。

Config-lint是一個(gè)很有前途的框架,可以讓你使用YAML DSL為Kubernetes YAML清單編寫自定義檢查诀艰。
但是柬甥,如果您想表達(dá)更復(fù)雜的邏輯和檢查該怎么辦?
YAML對(duì)此是否也有限制其垄?
如果您可以使用真正的編程語言來表達(dá)這些檢查暗甥,該怎么辦?接下來看Copper

Copper

Copper V2是一個(gè)使用自定義檢查來驗(yàn)證清單的框架捉捅,就像config-lint一樣撤防。
但是,Copper不使用YAML定義檢查棒口。
相反寄月,測試是用JavaScript編寫的,而Copper提供了一個(gè)包含一些基本幫助程序的庫无牵,以幫助讀取Kubernetes對(duì)象和報(bào)告錯(cuò)誤漾肮。

安裝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并沒有提供內(nèi)置檢查茎毁。
讓我們自定義一個(gè)檢查克懊,以確保部署鏡像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)
            }
        });
    }
});

執(zhí)行檢查

[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

現(xiàn)在修改為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是用于配置數(shù)據(jù)的測試框架七蜘,可用于檢查和驗(yàn)證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沒有任何內(nè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, "kubeops.net/")
  msg := sprintf("image '%v' doesn't come from kubeops.net repository", [image])
}

先修改base.yaml,image: docker.io/http-echo
使用conftest執(zhí)行檢測

[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

最后一個(gè)工具了碧库,Polaris既可以安裝在集群內(nèi)部柜与,也可以作為命令行工具來靜態(tài)分析Kubernetes清單巧勤。
作為命令行工具運(yùn)行時(shí),它包含多個(gè)內(nèi)置檢查弄匕,涉及諸如安全性和最佳實(shí)踐等方面颅悉,類似于kube-score。
另外迁匠,你可以使用它來編寫類似于config-lint签舞,copper和conftest的自定義檢查。
換句話說柒瓣,Polaris結(jié)合了兩類的優(yōu)點(diǎn):內(nèi)置和自定義檢查器儒搭。

安裝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對(duì)base.yaml進(jìn)行檢查
[root@k8s-node001 Test]# polaris audit --audit-path base.yaml
結(jié)果如下搂鲫,信息比較多,這里只截取部分信息,自己可以仔細(xì)看看分析出來的結(jié)果磺平。

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

另外魂仍,可以只輸出評(píng)分

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

下面使用YAML代碼段定義了一個(gè)稱為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/.+$

現(xiàn)在base.yaml的image為:image: docker.io/http-echo:v1.0.0
我們來使用自定義的規(guī)則執(zhí)行檢查

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

結(jié)果顯示"Message": "Image registry is not valid", "Success": false,
然后修改base.yaml的image為:image: kubeops.net/http-echo:v1.0.0
再次執(zhí)行檢查

[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,,檢查通過拣挪。擦酌。。
更多用法參見

總結(jié)

盡管有很多工具可以對(duì)Kubernetes YAML文件進(jìn)行驗(yàn)證菠劝,評(píng)分和整理赊舶,但重要的是要有一個(gè)健康的模型來設(shè)計(jì)和執(zhí)行檢查。
例如赶诊,如果你要考慮通過管道的Kubernetes清單笼平,則kubeval可能是該管道中的第一步,因?yàn)樗梢则?yàn)證對(duì)象定義是否符合Kubernetes API模式舔痪。一旦此檢查成功寓调,你可以繼續(xù)進(jìn)行更詳盡的測試,例如標(biāo)準(zhǔn)最佳實(shí)踐和自定義策略锄码。
Kube-score和Polaris是比較好的選擇夺英。
如果你有復(fù)雜的要求,并且想要自定義檢查的細(xì)節(jié)滋捶,則應(yīng)考慮使用copper 痛悯,config-lint和conftest。
盡管conftest和config-lint都使用更多的YAML來定義自定義驗(yàn)證規(guī)則炬太,但是Copper允許訪問一種真正的編程語言灸蟆,這使其頗具吸引力。
但是亲族,你應(yīng)該使用其中之一并從頭開始編寫所有檢查嗎炒考?還是應(yīng)該使用Polaris并僅編寫其他自定義檢查?
這都取決于你自己霎迫,合適自己的才是最好的斋枢。。知给。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末瓤帚,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子涩赢,更是在濱河造成了極大的恐慌戈次,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,919評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件筒扒,死亡現(xiàn)場離奇詭異怯邪,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)花墩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門悬秉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人冰蘑,你說我怎么就攤上這事和泌。” “怎么了祠肥?”我有些...
    開封第一講書人閱讀 163,316評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵武氓,是天一觀的道長。 經(jīng)常有香客問我仇箱,道長聋丝,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,294評(píng)論 1 292
  • 正文 為了忘掉前任工碾,我火速辦了婚禮弱睦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘渊额。我一直安慰自己况木,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,318評(píng)論 6 390
  • 文/花漫 我一把揭開白布旬迹。 她就那樣靜靜地躺著火惊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪奔垦。 梳的紋絲不亂的頭發(fā)上屹耐,一...
    開封第一講書人閱讀 51,245評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音椿猎,去河邊找鬼惶岭。 笑死寿弱,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的按灶。 我是一名探鬼主播症革,決...
    沈念sama閱讀 40,120評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼鸯旁!你這毒婦竟也來了噪矛?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,964評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤铺罢,失蹤者是張志新(化名)和其女友劉穎艇挨,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體韭赘,經(jīng)...
    沈念sama閱讀 45,376評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡酪穿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,592評(píng)論 2 333
  • 正文 我和宋清朗相戀三年惫东,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了劫樟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片葫盼。...
    茶點(diǎn)故事閱讀 39,764評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖瓦灶,靈堂內(nèi)的尸體忽然破棺而出鸠删,到底是詐尸還是另有隱情,我是刑警寧澤贼陶,帶...
    沈念sama閱讀 35,460評(píng)論 5 344
  • 正文 年R本政府宣布刃泡,位于F島的核電站,受9級(jí)特大地震影響碉怔,放射性物質(zhì)發(fā)生泄漏烘贴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,070評(píng)論 3 327
  • 文/蒙蒙 一撮胧、第九天 我趴在偏房一處隱蔽的房頂上張望桨踪。 院中可真熱鬧,春花似錦芹啥、人聲如沸锻离。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽汽纠。三九已至,卻和暖如春傀履,著一層夾襖步出監(jiān)牢的瞬間虱朵,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留碴犬,地道東北人絮宁。 一個(gè)月前我還...
    沈念sama閱讀 47,819評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像翅敌,于是被迫代替她去往敵國和親羞福。 傳聞我的和親對(duì)象是個(gè)殘疾皇子惕蹄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,665評(píng)論 2 354