【K8s任務】使用 Kustomize 對 Kubernetes 物件進行宣告式管理
參考:https://kubernetes.io/zh/docs/tasks/manage-kubernetes-objects/kustomization/
Kustomize 是一個獨立的工具,用來通過 kustomization 檔案 定製 Kubernetes 物件。
從 1.14 版本開始,kubectl 也開始支援使用 kustomization 檔案來管理 Kubernetes 物件。 要檢視包含 kustomization 檔案的目錄中的資源,執行下面的命令:
kubectl kustomize <kustomization_directory>
要應用這些資源,使用引數 --kustomize 或 -k 標誌來執行 kubectl apply:
kubectl apply -k <kustomization_directory>
Kustomize 概述
Kustomize 是一個用來定製 Kubernetes 配置的工具。它提供以下功能特性來管理 應用配置檔案:
- 從其他來源生成資源
- 為資源設定貫穿性(Cross-Cutting)欄位
- 組織和定製資源集合
生成資源
ConfigMap 和 Secret 包含其他 Kubernetes 物件(如 Pod)所需要的配置或敏感資料。 ConfigMap 或 Secret 中資料的來源往往是叢集外部,例如某個 .properties 檔案或者 SSH 金鑰檔案。 Kustomize 提供 secretGenerator 和 configMapGenerator,可以基於檔案或字面 值來生成 Secret 和 ConfigMap。
configMapGenerator
要基於檔案來生成 ConfigMap,可以在 configMapGenerator 的 files 列表中新增表項。 下面是一個根據 .properties 檔案中的資料條目來生成 ConfigMap 的示例:
# 生成一個 application.properties 檔案 cat <<EOF >application.properties FOO=Bar EOF cat <<EOF >./kustomization.yaml configMapGenerator: - name: example-configmap-1 files: - application.properties EOF
所生成的 ConfigMap 可以使用下面的命令來檢查:
kubectl kustomize ./
所生成的 ConfigMap 為:
apiVersion: v1
data:
application.properties: |
FOO=Bar
kind: ConfigMap
metadata:
name: example-configmap-1-8mbdf7882g
要從 env 檔案生成 ConfigMap,請在 configMapGenerator 中的 envs 列表中新增一個條目。 下面是一個用來自 .env 檔案的資料生成 ConfigMap 的例子:
# 建立一個 .env 檔案
cat <<EOF >.env
FOO=Bar
EOF
cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: example-configmap-1
envs:
- .env
EOF
可以使用以下命令檢查生成的 ConfigMap:
kubectl kustomize ./
生成的 ConfigMap 為:
apiVersion: v1
data:
FOO=Bar
kind: ConfigMap
metadata:
name: example-configmap-1-8mbdf7882g
說明: .env 檔案中的每個變數在生成的 ConfigMap 中成為一個單獨的鍵。 這與之前的示例不同,前一個示例將一個名為 .properties 的檔案(及其所有條目)嵌入到同一個鍵的值中。
ConfigMap 也可基於字面的鍵值偶對來生成。要基於鍵值偶對來生成 ConfigMap, 在 configMapGenerator 的 literals 列表中新增表項。下面是一個例子,展示 如何使用鍵值偶對中的資料條目來生成 ConfigMap 物件:
cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: example-configmap-2
literals:
- FOO=Bar
EOF
可以用下面的命令檢查所生成的 ConfigMap:
kubectl kustomize ./
所生成的 ConfigMap 為:
apiVersion: v1
data:
FOO: Bar
kind: ConfigMap
metadata:
name: example-configmap-2-g2hdhfc6tk
要在 Deployment 中使用生成的 ConfigMap,使用 configMapGenerator 的名稱對其進行引用。 Kustomize 將自動使用生成的名稱替換該名稱。
這是使用生成的 ConfigMap 的 deployment 示例:
# 建立一個 application.properties 檔案
cat <<EOF >application.properties
FOO=Bar
EOF
cat <<EOF >deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
labels:
app: my-app
spec:
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: my-app
volumeMount:
- name: config
mountPath: /config
volumes:
- name: config
configMap:
name: example-configmap-1
EOF
cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
configMapGenerator:
- name: example-configmap-1
files:
- application.properties
EOF
生成 ConfigMap 和 Deployment:
kubectl kustomize ./
生成的 Deployment 將通過名稱引用生成的 ConfigMap:
apiVersion: v1
data:
application.properties: |
FOO=Bar
kind: ConfigMap
metadata:
name: example-configmap-1-g4hk9g2ff8
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: my-app
name: my-app
spec:
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- image: my-app
name: app
volumeMount:
- mountPath: /config
name: config
volumes:
- configMap:
name: example-configmap-1-g4hk9g2ff8
name: config
secretGenerator
你可以基於檔案或者鍵值偶對來生成 Secret。要使用檔案內容來生成 Secret, 在 secretGenerator 下面的 files 列表中新增表項。 下面是一個根據檔案中資料來生成 Secret 物件的示例:
# 建立一個 password.txt 檔案
cat <<EOF >./password.txt
username=admin
password=secret
EOF
cat <<EOF >./kustomization.yaml
secretGenerator:
- name: example-secret-1
files:
- password.txt
EOF
所生成的 Secret 如下:
apiVersion: v1
data:
password.txt: dXNlcm5hbWU9YWRtaW4KcGFzc3dvcmQ9c2VjcmV0Cg==
kind: Secret
metadata:
name: example-secret-1-t2kt65hgtb
type: Opaque
要基於鍵值偶對字面值生成 Secret,先要在 secretGenerator 的 literals 列表中新增表項。下面是基於鍵值偶對中資料條目來生成 Secret 的示例:
cat <<EOF >./kustomization.yaml
secretGenerator:
- name: example-secret-2
literals:
- username=admin
- password=secret
EOF
所生成的 Secret 如下:
apiVersion: v1
data:
password: c2VjcmV0
username: YWRtaW4=
kind: Secret
metadata:
name: example-secret-2-t52t6g96d8
type: Opaque
與 ConfigMaps 一樣,生成的 Secrets 可以通過引用 secretGenerator 的名稱在部署中使用:
# 建立一個 password.txt 檔案
cat <<EOF >./password.txt
username=admin
password=secret
EOF
cat <<EOF >deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
labels:
app: my-app
spec:
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: my-app
volumeMount:
- name: password
mountPath: /secrets
volumes:
- name: password
secret:
secretName: example-secret-1
EOF
cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
secretGenerator:
- name: example-secret-1
files:
- password.txt
EOF
generatorOptions
所生成的 ConfigMap 和 Secret 都會包含內容雜湊值字尾。 這是為了確保內容發生變化時,所生成的是新的 ConfigMap 或 Secret。 要禁止自動新增字尾的行為,使用者可以使用 generatorOptions。 除此以外,為生成的 ConfigMap 和 Secret 指定貫穿性選項也是可以的。
cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: example-configmap-3
literals:
- FOO=Bar
generatorOptions:
disableNameSuffixHash: true
labels:
type: generated
annotations:
note: generated
EOF
執行 kubectl kustomize ./ 來檢視所生成的 ConfigMap:
apiVersion: v1
data:
FOO: Bar
kind: ConfigMap
metadata:
annotations:
note: generated
labels:
type: generated
name: example-configmap-3
設定貫穿性欄位
在專案中為所有 Kubernetes 物件設定貫穿性欄位是一種常見操作。 貫穿性欄位的一些使用場景如下:
- 為所有資源設定相同的名字空間
- 為所有物件新增相同的字首或字尾
- 為物件新增相同的標籤集合
- 為物件新增相同的註解集合
下面是一個例子:
# 建立一個 deployment.yaml
cat <<EOF >./deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
EOF
cat <<EOF >./kustomization.yaml
namespace: my-namespace
namePrefix: dev-
nameSuffix: "-001"
commonLabels:
app: bingo
commonAnnotations:
oncallPager: 800-555-1212
resources:
- deployment.yaml
EOF
執行 kubectl kustomize ./ 檢視這些欄位都被設定到 Deployment 資源上:
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
oncallPager: 800-555-1212
labels:
app: bingo
name: dev-nginx-deployment-001
namespace: my-namespace
spec:
selector:
matchLabels:
app: bingo
template:
metadata:
annotations:
oncallPager: 800-555-1212
labels:
app: bingo
spec:
containers:
- image: nginx
name: nginx
組織和定製資源
一種常見的做法是在專案中構造資源集合並將其放到同一個檔案或目錄中管理。 Kustomize 提供基於不同檔案來組織資源並向其應用補丁或者其他定製的能力。
組織
Kustomize 支援組合不同的資源。kustomization.yaml 檔案的 resources 欄位 定義配置中要包含的資源列表。你可以將 resources 列表中的路徑設定為資源配置檔案 的路徑。下面是由 Deployment 和 Service 構成的 NGINX 應用的示例:
# 建立 deployment.yaml 檔案
cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 2
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
ports:
- containerPort: 80
EOF
# 建立 service.yaml 檔案
cat <<EOF > service.yaml
apiVersion: v1
kind: Service
metadata:
name: my-nginx
labels:
run: my-nginx
spec:
ports:
- port: 80
protocol: TCP
selector:
run: my-nginx
EOF
# 建立 kustomization.yaml 來組織以上兩個資源
cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
- service.yaml
EOF
kubectl kustomize ./ 所得到的資源中既包含 Deployment 也包含 Service 物件。
定製
補丁檔案(Patches)可以用來對資源執行不同的定製。 Kustomize 通過 patchesStrategicMerge 和 patchesJson6902 支援不同的打補丁 機制。patchesStrategicMerge 的內容是一個檔案路徑的列表,其中每個檔案都應可解析為 策略性合併補丁(Strategic Merge Patch)。 補丁檔案中的名稱必須與已經載入的資源的名稱匹配。 建議構造規模較小的、僅做一件事情的補丁。 例如,構造一個補丁來增加 Deployment 的副本個數;構造另外一個補丁來設定記憶體限制。
# 建立 deployment.yaml 檔案
cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 2
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
ports:
- containerPort: 80
EOF
# 生成一個補丁 increase_replicas.yaml
cat <<EOF > increase_replicas.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
replicas: 3
EOF
# 生成另一個補丁 set_memory.yaml
cat <<EOF > set_memory.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
template:
spec:
containers:
- name: my-nginx
resources:
limits:
memory: 512Mi
EOF
cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
patchesStrategicMerge:
- increase_replicas.yaml
- set_memory.yaml
EOF
執行 kubectl kustomize ./ 來檢視 Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
replicas: 3
selector:
matchLabels:
run: my-nginx
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- image: nginx
name: my-nginx
ports:
- containerPort: 80
resources:
limits:
memory: 512Mi
並非所有資源或者欄位都支援策略性合併補丁。為了支援對任何資源的任何欄位進行修改, Kustomize 提供通過 patchesJson6902 來應用 JSON 補丁 的能力。為了給 JSON 補丁找到正確的資源,需要在 kustomization.yaml 檔案中指定資源的 組(group)、版本(version)、類別(kind)和名稱(name)。 例如,為某 Deployment 物件增加副本個數的操作也可以通過 patchesJson6902 來完成:
# 建立一個 deployment.yaml 檔案
cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 2
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
ports:
- containerPort: 80
EOF
# 建立一個 JSON 補丁檔案
cat <<EOF > patch.yaml
- op: replace
path: /spec/replicas
value: 3
EOF
# 建立一個 kustomization.yaml
cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
patchesJson6902:
- target:
group: apps
version: v1
kind: Deployment
name: my-nginx
path: patch.yaml
EOF
執行 kubectl kustomize ./ 以檢視 replicas 欄位被更新:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
replicas: 3
selector:
matchLabels:
run: my-nginx
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- image: nginx
name: my-nginx
ports:
- containerPort: 80
除了補丁之外,Kustomize 還提供定製容器映象或者將其他物件的欄位值注入到容器 中的能力,並且不需要建立補丁。 例如,你可以通過在 kustomization.yaml 檔案的 images 欄位設定新的映象來 更改容器中使用的映象。
cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 2
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
ports:
- containerPort: 80
EOF
cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
images:
- name: nginx
newName: my.image.registry/nginx
newTag: 1.4.0
EOF
執行 kubectl kustomize ./ 以檢視所使用的映象已被更新:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
replicas: 2
selector:
matchLabels:
run: my-nginx
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- image: my.image.registry/nginx:1.4.0
name: my-nginx
ports:
- containerPort: 80
有些時候,Pod 中執行的應用可能需要使用來自其他物件的配置值。 例如,某 Deployment 物件的 Pod 需要從環境變數或命令列引數中讀取讀取 Service 的名稱。 由於在 kustomization.yaml 檔案中新增 namePrefix 或 nameSuffix 時 Service 名稱可能發生變化,建議不要在命令引數中硬編碼 Service 名稱。 對於這種使用場景,Kustomize 可以通過 vars 將 Service 名稱注入到容器中。
# 建立一個 deployment.yaml 檔案
cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 2
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
command: ["start", "--host", "$(MY_SERVICE_NAME)"]
EOF
# 建立一個 service.yaml 檔案
cat <<EOF > service.yaml
apiVersion: v1
kind: Service
metadata:
name: my-nginx
labels:
run: my-nginx
spec:
ports:
- port: 80
protocol: TCP
selector:
run: my-nginx
EOF
cat <<EOF >./kustomization.yaml
namePrefix: dev-
nameSuffix: "-001"
resources:
- deployment.yaml
- service.yaml
vars:
- name: MY_SERVICE_NAME
objref:
kind: Service
name: my-nginx
apiVersion: v1
EOF
執行 kubectl kustomize ./ 以檢視注入到容器中的 Service 名稱是 dev-my-nginx-001:
apiVersion: apps/v1
kind: Deployment
metadata:
name: dev-my-nginx-001
spec:
replicas: 2
selector:
matchLabels:
run: my-nginx
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- command:
- start
- --host
- dev-my-nginx-001
image: nginx
name: my-nginx
基準(Bases)與覆蓋(Overlays)
Kustomize 中有 基準(bases) 和 覆蓋(overlays) 的概念區分。 基準 是包含 kustomization.yaml 檔案的一個目錄,其中包含一組資源及其相關的定製。 基準可以是本地目錄或者來自遠端倉庫的目錄,只要其中存在 kustomization.yaml 檔案即可。 覆蓋 也是一個目錄,其中包含將其他 kustomization 目錄當做 bases 來引用的 kustomization.yaml 檔案。 基準不瞭解覆蓋的存在,且可被多個覆蓋所使用。 覆蓋則可以有多個基準,且可針對所有基準中的資源執行組織操作,還可以在其上執行定製。
# 建立一個包含基準的目錄
mkdir base
# 建立 base/deployment.yaml
cat <<EOF > base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 2
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
EOF
# 建立 base/service.yaml 檔案
cat <<EOF > base/service.yaml
apiVersion: v1
kind: Service
metadata:
name: my-nginx
labels:
run: my-nginx
spec:
ports:
- port: 80
protocol: TCP
selector:
run: my-nginx
EOF
# 建立 base/kustomization.yaml
cat <<EOF > base/kustomization.yaml
resources:
- deployment.yaml
- service.yaml
EOF
此基準可在多個覆蓋中使用。你可以在不同的覆蓋中新增不同的 namePrefix 或 其他貫穿性欄位。下面是兩個使用同一基準的覆蓋:
mkdir dev
cat <<EOF > dev/kustomization.yaml
bases:
- ../base
namePrefix: dev-
EOF
mkdir prod
cat <<EOF > prod/kustomization.yaml
bases:
- ../base
namePrefix: prod-
EOF
如何使用 Kustomize 來應用、檢視和刪除物件
在 kubectl 命令中使用 --kustomize 或 -k 引數來識別被 kustomization.yaml 所管理的資源。 注意 -k 要指向一個 kustomization 目錄。例如:
kubectl apply -k <kustomization 目錄>/
假定使用下面的 kustomization.yaml,
# 建立 deployment.yaml 檔案
cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 2
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
ports:
- containerPort: 80
EOF
# 建立 kustomization.yaml
cat <<EOF >./kustomization.yaml
namePrefix: dev-
commonLabels:
app: my-nginx
resources:
- deployment.yaml
EOF
執行下面的命令來應用 Deployment 物件 dev-my-nginx:
kubectl apply -k ./
deployment.apps/dev-my-nginx created
執行下面的命令之一來檢視 Deployment 物件 dev-my-nginx:
kubectl get -k ./
kubectl describe -k ./
執行下面的命令來比較 Deployment 物件 dev-my-nginx 與清單被應用之後 叢集將處於的狀態:
kubectl diff -k ./
執行下面的命令刪除 Deployment 物件 dev-my-nginx:
kubectl delete -k ./
deployment.apps "dev-my-nginx" deleted