文件參考來源: https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/
今天要來介紹 k8s 中用來做資源管理的工具 kustomize
kustomize 是一個 standalone 的工具透過 kustomization file 來管理 k8s 物件
在 k8s 中可以透過以下指令來查看資料夾中包含的 kustomization file
kubectl kustomize $kustomization_directory
要佈署這些資源要使用以下指令:
kubectl apply -k $kustomization_directory
Kustomize 是一個用來客製化 k8s 設定檔的工具, 具有以下特性可以管理設定檔:
ConfigMap 跟 Secret 具有其他 k8s 物件比如說 Pod 的設定檔跟機敏資料
而這些設定檔跟機敏資料來源通常是來自 k8s 叢集之外, 比如說 .properties 或是 SSH keyfile
Kustomize 有 secretGenerator 與 configMapGenerator 可以從外部檔案產生 Secret 與 ConfigMap
來源檔案 application.properties 如下:
FOO=Bar
則可以透過設定 kustomization.yaml 如下
configMapGenerator:
- name: example-configmap-1
  files:
  - application.properties
然後在同一個資料夾執行以下指令
kubectl kustomize ./
產生出以下 ConfigMap :
apiVersion: v1
data:
  application.properties: |
         FOO=Bar
kind: ConfigMap
metadata:
  name: example-configmap-1-8mbdf7882g
來源檔案 .env 如下
FOO=Bar
則可以設定 kustomization.yaml 如下:
configMapGenerator:
- name: example-configmap-1
  envs:
  - .env
然後在同一個資料夾執行以下指令
kubectl kustomize ./
產生出以下 ConfigMap :
apiVersion: v1
data:
  FOO=Bar
kind: ConfigMap
metadata:
  name: example-configmap-1-8mbdf7882g
要注意的是 每個 .env 的設定值在產生出來的 ConfigMap 都是原本的 key-value 形式保存, 而 .properties 的所有內容則是都被當成 value
另外也有一種 literal 的作法
直接設定 kustomization.yaml 如下:
configMapGenerator:
- name: example-configmap-2
  literals:
  - FOO=Bar
然後在同一個資料夾執行以下指令
kubectl kustomize ./
產生出以下 ConfigMap:
apiVersion: v1
data:
  FOO: Bar
kind: ConfigMap
metadata:
  name: example-configmap-2-g2hdhfc6tk
上面由於是用 generator 產生出來的 configMap 所以名稱都帶有 hash
這邊的 hash 值都是隨意給的, 所以實際在機器上跑出來的應該不會根本文一樣
下面給一個實際的範例
給一個設定檔案 application.properties 如下:
FOO=BAR
給一設定檔 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
        volumeMounts:
        - name: config
          mountPath: /config
      volumes:
      - name: config
        configMap:
          name: example-configmap-1
就可以透過寫一個 kustomization.yaml 如下:
resources: 
- deployment.yaml
configMapGenerator:
- name: example-configmap-1
  files:
  - application.properties
然後在同一個資料夾執行以下指令
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
        volumeMounts:
        - mountPath: /config
          name: config
      volumes:
      - configMap:
          name: example-configmap-1-g4hk9g2ff8
        name: config
給定一個設定檔 password.txt 如下:
username=admin
password=secret
寫一個設定檔 kustomization.yaml 如下:
secretGenerator:
- name: example-secret-1
  files:
  - password.txt
然後在同一個資料夾執行以下指令
kubectl kustomize ./
產生出以下 Secret:
apiVersion: v1
data:
  password.txt: dXNlcm5hbWU9YWRtaW4KcGFzc3dvcmQ9c2VjcmV0Cg==
kind: Secret
metadata:
  name: example-secret-1-t2kt65hgtb
type: Opaque
另外也可以使用 literals 來建制 Secrets
建制 kustomization.yaml 如下
secretGenerator:
- name: examples-secret-2
  literals:
  - username=admin
  - password=secret
然後在同一個資料夾執行以下指令
kubectl kustomize ./
產生出以下 Secret:
apiVersion: v1
data:
  password: c2VjcmV0
  username: YWRtaW4=
kind: Secret
metadata:
  name: example-secret-2-t52t6g96d8
type: Opaque
下面給一個實際的範例
給定設定檔 password.txt 如下:
username=admin
password=secret
而設定檔 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
        volumeMounts:
        - name: password
          mountPath: /secrets
      volumes:
      - name: password
        secret:
          secretName: example-secret-1
就可以透過以下設定檔 kustomization.yaml
resources:
- deployment.yaml
secretGenerator:
- name: example-secret-1
  files:
  - password.txt
把 password.txt 跟 deployment.yaml 給結合在一起
另外對於 generator 其實有一些選項可以設定
比如說不想要檔案產生出來名稱有 hash 就可以設定 disableNameSuffixHash: true 如下
configMapGenerator:
- name: example-configmap-3
  literals:
  - FOO=Bar
generatorOptions:
  disableNameSuffixHash: true
  labels:
    type: generated
  annotations:
    note: generated
然後在同一個資料夾執行以下指令
kubectl kustomize ./
產生出來 ConfigMap 的 name 不會有 hash 如下
apiVersion: v1
data:
  FOO: Bar
kind: ConfigMap
metadata:
  annotations:
    note: generated
  labels:
    type: generated
  name: example-configmap-3
所謂的 cross-cutting field 指的是有一些共通欄位會出現各種設定檔之內
比如說 namespace, name prefix 或 suffix 或是 labels 或是 annotations
假設有一個設定檔 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
就可以透過設定 kustomization.yaml 如下
namespace: my-namespace
namePrefix: dev-
nameSuffix: "-001"
commonLabels:
  app: bingo
commonAnnotations:
  oncallPager: 800-555-1212
resources:
- deployment.yaml
然後在同一個資料夾執行以下指令
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 可以針對 Deployment 做一些附加屬性, 讓佈署增加彈性
可以透過 kustomizae 來組合同一個資料夾下的佈署資源
或是把其他資源附加到目前的資源內
以下是透過 kustomize 組合同一個資料夾下的 Deployment 跟 Service
建立一個 nginx 的 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
建立一個 nginx 的 service.yaml 如下
apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  ports:
  - port: 80
    protocol: TCP
  selector:
    run: my-nginx
然後就可以透過建立 kustomization.yaml 來把上面兩個資源組合在一起如下:
resources:
- deployment.yaml
- service.yaml
然後在同一個資料夾執行以下指令
kubectl kustomize ./
產生出來的佈署檔包含 Service, Deployment
Kustomize 可以讓設定檔切分成多個 patch 檔, 在發佈時組合所有 patch 成為一個發佈檔
patch 檔合成的機制分為 patchesStrategicMerge 與 patchesJson6902
這種模式寫法就是一堆檔案的路徑
而每個 patch 檔案需要符合 strategic merge patch
範例如下:
設定檔 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
建立 increase_replicas.yaml 來增加 replicas
increase_replicas.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  replicas: 3
建立 set_memory.yaml 來設定 memroy limit
set_memory.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  template:
    spec:
      containers:
      - name: my-nginx
        resources:
          limits:
            memory: 512Mi
建立 kustomization.ymal 如下
resources:
- deployment.yaml
patchesStrategicMerge:
- increase_replicas.yaml
- set_memory.yaml
然後在同一個資料夾執行以下指令
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
因為不是所有 Resource 欄位都能支援 strategic merge patches
所以 Kustomize 提供 patchesJson6902 來支援 JSON patch
範例如下:
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
建立一個 json patch 的設定檔 patch.yaml 如下:
- op: replace
  path: /spec/replicas
  value: 3
設定 kustomization.yaml 如下:
resources:
- deployment.yaml
patchesJson6902:
- target:
    group: apps
    version: v1
    kind: Deployment
    name: my-nginx
  path: patch.yaml
然後在同一個資料夾執行以下指令
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
在 kustomization.yaml 可以指定 images 來替換原本 Deployment 內部的 images
假設 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
建立 kustomization.yaml 如下:
resources:
- deployment.yaml
images:
- name: nginx
  newName: my.image.registry/nginx
  newTag: 1.4.0
然後在同一個資料夾執行以下指令
kubectl kustomize ./
產生出來的 Deployment 就會如下
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
有時候為了讓 Deployment 更有彈性
所會在 Deployment 引入變數
也可以在 Kustomize 設定變數值做變數替換
範例如下:
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)"]
service.yaml 如下:
apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  ports:
  - port: 80
    protocol: TCP
  selector:
    run: my-nginx
建立一個 ksutomization.yaml 如下:
namePrefix: dev-
nameSuffix: "-001"
resources:
- deployment.yaml
- service.yaml
vars:
- name: MY_SERVICE_NAME
  objref:
    kind: Service
    name: my-nginx
    apiVersion: v1
然後在同一個資料夾執行以下指令
kubectl kustomize ./
產生出來的 Deployment 就會如下
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
command 的變數被 kustomization.yaml 的設定所替換了
Kustomize 具有 base 跟 overlay 的概念
base 就是指在一個資料夾具有一個 kustomization.yaml 檔案包含所有佈署設定
overlay 指指在一個資料夾具有一個 kustomization.yaml 檔案把 base 參考到其他具有 kustomization.yaml 的路徑
base 可以被其他 overlay 引用
overlay 可以具有多個 base
base 範例如下
假設在資料夾 base
建立一個 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
建立 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
建立 base/kustomization.yaml 如下:
resources:
- deployment.yaml
- service.yaml
而 overlay 範例則如下
建立一個 dev 資料夾
mkdir dev
建立一個 dev/kustomization.yaml 如下
bases:
- ../base
namePrefix: dev-
建立一個 prod 資料夾
mkdir prod
建立一個 prod/kustomization.yaml 如下
bases:
- ../base
namePrefix: prod-
上面兩個 overlay 都引用了 base , 並且加入 namePrefix
以下指令用來佈署
kubectl apply -k $kustomization_dir/
範例如下:
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
建立 kustomization.yaml 如下:
namePrefix: dev-
commonLabels:
  app: my-nginx
resources:
- deployment.yaml
在同一個資料夾使用以下指令做佈署
kubectl apply -k ./
查看佈署可以使用以下兩個指令
kubectl get -k ./
kubectl describe -k ./
比較 k8s 物件狀態與佈署差別可以用以下指令
kubectl diff -k ./
刪除佈署
kubectl delete -k ./
在佈署 k8s 中會產生許多的設定檔
這時候管理設定檔就是一個很重要的事情
這時如果能透過一些工具來做不同環境的設定切割就會是很方便的事情
比如這個章節所提到的 kustomize