Kubernetes YAML最佳實踐和策略
- 2020 年 11 月 3 日
- 筆記
- cloudnative, DevOps, docker, Kubernetes
Kubernetes工作負載最常用YAML格式的文件來定義。
YAML的問題之一就是很難描述清單文件之間的約束或關係。
如果你希望檢查是否已從受信任的註冊表中提取部署到群集中的所有映像,該怎麼辦?
如何防止沒有Pod安全策略的工作負載提交到集群?
集成靜態檢查可以在更接近開發生命周期的時間內捕獲錯誤和違反策略的行為。
並且由於改善了資源定義的有效性和安全性,因此你可以相信生產工作負載遵循最佳實踐。
Kubernetes YAML文件的靜態檢查生態系統可以分為以下幾類:
- API驗證程式:此類工具針對Kubernetes API伺服器驗證給定的YAML清單。
- 內置檢查器:此類工具捆綁了針對安全性,最佳實踐等的自覺檢查。
- 自定義驗證器:此類工具允許使用多種語言(例如python和Javascript)編寫自定義檢查。
在本文中,你將學習到六個不同的工具:
Kubeval
Kube-score
Config-lint
Copper
Conftest
Polaris
Let’s Go ~~~
基準服務
首先部署一個基準服務,以便後面測試對比
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文件能部署成功,但是,它遵循最佳做法嗎?
Let’s start.
kubeval
kubeval的前提是與Kubernetes的任何交互都通過其REST API進行。
因此,可以使用API模式來驗證給定的YAML輸入是否符合該模式。
安裝kubeval
wget //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提供在線版和離線版
本文偷懶就用在線版了
首先打開//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 //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
//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 //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 //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': //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允許訪問一種真正的程式語言,這使其頗具吸引力。
但是,你應該使用其中之一併從頭開始編寫所有檢查嗎?還是應該使用Polaris並僅編寫其他自定義檢查?
這都取決於你自己,合適自己的才是最好的。。。