Kustomize: робота з маніфестами Kubernetes – огляд, приклади
5 (2)

Автор |  15/01/2023
Click to rate this post!
[Total: 2 Average: 5]
You have already voted for this article with rating

Kustomizeсистема управління конфігураціями (configuration management tool) для Kubernetes, що дозволяє використовувати загальні набори маніфестів, які можуть бути змінені для кожного конкретного оточення/кластера, і може бути альтернативою шаблонам Helm (або доповнювати його).

Загальна концепція Kustomize – “where, what, and how” – “де, що і як”:

  • “де” – це наш базовий маніфест, наприклад deployment.yaml
  • “що” – що саме в маніфесті мінятимемо, наприклад кількість подів (replicas) у цьому деплойменті
  • “як” – файли конфігуарації Kustomize – kustomization.yaml

Огляд Kustomize

Для простого прикладу візьмемо файл kustomization.yaml з таким змістом:

resources:
- deployment.yaml
- service.yaml
namePrefix: dev-
namespace: development
commonLabels:
  environment: development

Тут описується, що потрібно взяти ресурси описані у файлах deployment.yaml та service.yaml, до імені кожного створюваного ресурсу додати префікс dev- ( namePrefix), деплоїти в namespace development , і додати labels environment: development.

Всі опції в Kustomize Feature List.

Крім того, Kustomize зручний для створення конфігурацій із загальних файлів, але для різних оточень.

У такому випадку використовується каталог overlays зі своїм набором kustomization.yaml:

Починаючи з версії 1.14, Kustomize вбудований у kubectl:

[simterm]

$ kubectl kustomize --help
Build a set of KRM resources using a 'kustomization.yaml' file. The DIR argument must be a path to a directory
containing 'kustomization.yaml', or a git repository URL with a path suffix specifying same with respect to the
repository root. If DIR is omitted, '.' is assumed.

Examples:
  # Build the current working directory
  kubectl kustomize
...

[/simterm]

І може використовуватися при apply, щоб спочатку зібрати (build) необхідний маніфест, і відразу відправити його в Kubernetes:

[simterm]

$ kubectl apply --help
...
  # Apply resources from a directory containing kustomization.yaml - e.g. dir/kustomization.yaml
  kubectl apply -k dir/
...

[/simterm]

А з версії 1.16 доступний і в kubeadm.

Крім kuberctl apply, Kustomize можна використовувати для:

  • kubectl get -k отримати ресурс із Kubernetes кластера
  • kubectl describe -k опис ресурсу в Kubernetes кластері
  • kubectl diff -k – порівняти локально згенерований маніфест із ресурсом у кластері
  • kubectl delete -k видалити ресурс із кластера

Деплой з Kustomize

Створюємо тестову директорію:

[simterm]

$ mkdir -p kustomize_example/base
$ cd kustomize_example/

[/simterm]

У каталозі base створимо два файли – в одному опишемо Deployment, в іншому Service:

[simterm]

$ vim -p base/deployment.yaml base/service.yaml

[/simterm]

У файлі deployment.yaml робимо запуск поду з контейнером nginxdemo :

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginxdemo
spec:
  selector:
    matchLabels:
      app: nginxdemo
  template:
    metadata:
      labels:
        app: nginxdemo
    spec:
      containers:
        - name: nginxdemo
          image: nginxdemos/hello
          ports:
          - name: http
            containerPort: 80
            protocol: TCP

І файл service.yaml із Сервісом для нього:

apiVersion: v1
kind: Service
metadata:
  name: nginxdemo
spec:
  selector:
    app: nginxdemo
  ports:
  - name: http
    port: 80

Далі, в тому ж каталозі base створюємо kustomization.yaml, в якому описуємо resources з чого ми збиратимемо наш майбутній маніфест для деплою:

resources:
  - deployment.yaml
  - service.yaml

І виконуємо сборку маніфесту:

[simterm]

$ kubectl kustomize base/
apiVersion: v1
kind: Service
metadata:
  name: nginxdemo
spec:
  ports:
  - name: http
    port: 80
  selector:
    app: nginxdemo
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginxdemo
spec:
  selector:
    matchLabels:
      app: nginxdemo
  template:
    metadata:
      labels:
        app: nginxdemo
    spec:
      containers:
      - image: nginxdemos/hello
        name: nginxdemo
        ports:
        - containerPort: 80
          name: http
          protocol: TCP

[/simterm]

Або через сам kustomize:

[simterm]

$ kustomize build base/
apiVersion: v1
kind: Service
metadata:
  name: nginxdemo
spec:
...

[/simterm]

Або виконуємо сборку і відразу деплоїмо:

[simterm]

$ kubectl apply -k base/
service/nginxdemo created
deployment.apps/nginxdemo created

[/simterm]

Перевіряємо:

[simterm]

$ kubectl get all -l app=nginxdemo
NAME                             READY   STATUS    RESTARTS   AGE
pod/nginxdemo-7f8f587c74-kbczf   1/1     Running   0          26s

NAME                                   DESIRED   CURRENT   READY   AGE
replicaset.apps/nginxdemo-7f8f587c74   1         1         1       26s

[/simterm]

Тепер подивимося, як налаштувати ці Deployment та Service для двох оточень – Dev та Prod.

Kustomize Overlays

Створюємо каталоги overlays/dev та overlays/prod:

[simterm]

$ mkdir -p overlays/{dev,prod}

[/simterm]

Отримуємо таку структуру:

[simterm]

$ tree .
.
|-- base
|   |-- deployment.yaml
|   |-- kustomization.yaml
|   `-- service.yaml
`-- overlays
    |-- dev
    `-- prod

[/simterm]

У каталогах dev та prod створюємо окремі kustomization.yaml, в яких описуємо bases:

bases:
- ../../base

Якщо зараз виконати kustomize build overlays/dev/, то отримаємо маніфест аналогічний до того, який створювали раніше.

Можливості Kustomize

namePrefix

Що б змінити цей маніфест – у файлах kustomization.yaml для Dev і Prod додамо, наприклад, namePrefix:

bases:
- ../../base

namePrefix: dev-

Перевіряємо:

[simterm]

$ kustomize build overlays/dev/
apiVersion: v1
kind: Service
metadata:
  name: dev-nginxdemo
spec:
  ports:
  - name: http
    port: 80
  selector:
    app: nginxdemo
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: dev-nginxdemo
...

[/simterm]

У полях name з’явився префікс dev .

patchesStrategicMerge

Далі, припустимо, ми хочемо на Dev мати 1 под, а на Prod – 3, тобто мати різні значення для поля replicas.

Використовуємо patchesStrategicMerge.

Створюємо файл патчу – overlays/dev/replicas.yaml. Тип та Ім’я ресурсу, які будемо патчити, повинні збігатися з ресурсом з base:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginxdemo
spec:
  replicas: 1

Аналогічно для Prod – файл overlays/prod/replicas.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginxdemo
spec:
  replicas: 3

У файлах overlays/dev/kustomization.yaml та overlays/prod/kustomization.yaml додаємо patchesStrategicMerge:

bases:
- ../../base

namePrefix: dev-

patchesStrategicMerge:
- replicas.yaml

Запускаємо:

[simterm]

$ kustomize build overlays/dev/
apiVersion: v1
kind: Service
metadata:
  name: dev-nginxdemo
spec:
  ports:
  - name: http
    port: 80
  selector:
    app: nginxdemo
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: dev-nginxdemo
spec:
  replicas: 1
...

[/simterm]

Деплоїмо:

[simterm]

$ kubectl apply -k overlays/dev/
service/dev-nginxdemo created
deployment.apps/dev-nginxdemo created

$ kubectl apply -k overlays/prod/
service/prod-nginxdemo created
deployment.apps/prod-nginxdemo created

[/simterm]

Перевіряємо:

[simterm]

$ kubectl get all -l app=nginxdemo
NAME                                  READY   STATUS    RESTARTS   AGE
pod/dev-nginxdemo-7f8f587c74-vh2gn    1/1     Running   0          37s
pod/nginxdemo-7f8f587c74-kbczf        1/1     Running   0          104m
pod/prod-nginxdemo-7f8f587c74-dpc76   1/1     Running   0          33s
pod/prod-nginxdemo-7f8f587c74-f5j4f   1/1     Running   0          33s
pod/prod-nginxdemo-7f8f587c74-zqg8z   1/1     Running   0          33s

NAME                                        DESIRED   CURRENT   READY   AGE
replicaset.apps/dev-nginxdemo-7f8f587c74    1         1         1       37s
replicaset.apps/nginxdemo-7f8f587c74        1         1         1       104m
replicaset.apps/prod-nginxdemo-7f8f587c74   3         3         3       33s

[/simterm]

configMapGenerator та secretGenerator

Kustomize також вміє генерувати нові ресурси із шаблонів.

Наприклад візьмемо ConfgiMap для алертів Grafana Loki .

Так як алерти однакові і для Dev, і для Prod – то описуємо configMapGenerator в base/kustomization.yaml:

resources:
  - deployment.yaml
  - service.yaml

configMapGenerator:
- name: loki-ruler-alerts
  files:
  - loki-ruler-alerts.yaml

У каталозі base створюємо сам файл loki-ruler-alers.yaml з контентом майбутнього ConfigMap:

groups:
  - name: systemd-alerts
    rules:
      - alert: Pod killed by OOM Killer
        expr: |
          sum(rate({job="systemd-journal"} |~ ".*OOM-killed.*" | regexp `pod=".*/(?P<pod>[a-zA-Z].*)".*` | pod!="" [15m])) by (pod, hostname) > 0.1
        for: 1s
        labels:
          severity: warning
        annotations:
          description: |-
            *OOM Killer detected in the WorkerNode's systemd-journal logs*
            WorkerNode: {{`{{ $labels.hostname }}`}}

Перевіряємо:

[simterm]

$ kustomize build base/
apiVersion: v1
data:
  loki-ruler-alerts.yaml: |
    groups:
      - name: systemd-alerts
        rules:
          - alert: Pod killed by OOM Killer
            expr: |
              sum(rate({job="systemd-journal"} |~ ".*OOM-killed.*" | regexp `pod=".*/(?P<pod>[a-zA-Z].*)".*` | pod!="" [15m])) by (pod, hostname) > 0.1
            for: 1s
            labels:
              severity: warning
            annotations:
              description: |-
                *OOM Killer detected in the WorkerNode's systemd-journal logs*
                WorkerNode: {{`{{ $labels.hostname }}`}}
kind: ConfigMap
metadata:
  name: loki-ruler-alerts-47678t7d89
---
apiVersion: v1
kind: Service
metadata:
  name: nginxdemo
...

[/simterm]

Крім того, можна згенерувати дані прямо з консолі.

Наприклад, щоб додати у файл base/kustomization.yaml новий Secret – виконуємо kustomize edit add secret:

[simterm]

$ cd base/
$ kustomize edit add secret nginx-password --from-literal=password=12345678

[/simterm]

Перевіряємо:

$ cat kustomization.yaml 
resources:
- deployment.yaml
- service.yaml

configMapGenerator:
- files:
  - loki-ruler-alerts.yaml
  name: loki-ruler-alerts
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
secretGenerator:
- literals:
  - password=12345678
  name: nginx-password
  type: Opaque

generatorOptions

Якщо ми застосовуємо base/kustomization.yaml, то до імен ConfigMap і Secret будуть додані постфікси:

[simterm]

$ kubectl apply -k base/
configmap/loki-ruler-alerts-47678t7d89 created
secret/nginx-password-72mh6dg77t created
service/nginxdemo unchanged
deployment.apps/nginxdemo unchanged

[/simterm]

47678t7d89 и 72mh6dg77t.

Щоб змінити цю поведінку – додаємо generatorOptions з опцією disableNameSuffixHash:

resources:
- deployment.yaml
- service.yaml
  
configMapGenerator:
- files:
  - loki-ruler-alerts.yaml
  name: loki-ruler-alerts

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
secretGenerator:
- literals:
  - password=12345678
  name: nginx-password
  type: Opaque

generatorOptions:
  disableNameSuffixHash: true

Деплоїмо:

[simterm]

$ kubectl apply -k base/
configmap/loki-ruler-alerts created
secret/nginx-password created
service/nginxdemo unchanged
deployment.apps/nginxdemo unchanged

[/simterm]

Тепер у нас такі імена, як ми їх вказали в шаблоні.

Helm && Kustomize

І приклад того, як можемо використовувати разом Helm && Kustomize, наприклад коли у вас є форк чарта, і ви не хочете змінювати дані.

Створюємо каталог хельм-чарту:

[simterm]

$ mkdir -p kustomize-helm

[/simterm]

Генеруємо в ньому чарт:

[simterm]

$ helm create kustomize-helm
Creating kustomize-helm

[/simterm]

Отримуємо структуру стандартного чарту:

[simterm]

$ tree . 
.
|-- kustomize-helm
|   |-- Chart.yaml
|   |-- charts
|   |-- templates
|   |   |-- NOTES.txt
|   |   |-- _helpers.tpl
|   |   |-- deployment.yaml
|   |   |-- hpa.yaml
|   |   |-- ingress.yaml
|   |   |-- service.yaml
|   |   |-- serviceaccount.yaml
|   |   `-- tests
|   |       `-- test-connection.yaml
|   `-- values.yaml
`-- templates

[/simterm]

Якщо виконаємо helm template kustomize-helm, то отримаємо згенеровані шаблони чарту:

[simterm]

$ helm template kustomize-helm
---
# Source: kustomize-helm/templates/serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: release-name-kustomize-helm
  labels:
    helm.sh/chart: kustomize-helm-0.1.0
    app.kubernetes.io/name: kustomize-helm
    app.kubernetes.io/instance: release-name
    app.kubernetes.io/version: "1.16.0"
    app.kubernetes.io/managed-by: Helm
---
# Source: kustomize-helm/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: release-name-kustomize-helm
  labels:
    helm.sh/chart: kustomize-helm-0.1.0
    app.kubernetes.io/name: kustomize-helm
    app.kubernetes.io/instance: release-name
...

[/simterm]

Тепер, щоб не міняти чарт, але створити свій власний Secret – в каталозі kustomize-helm створюємо файл kustomization.yaml, в якому використовуємо resources з файлом helm-all.yaml який згенеруємо за допомогою helm template:

resources:
- helm-all.yaml

secretGenerator:
- literals:
  - password=12345678
  name: nginx-password
  type: Opaque

Запускаємо

[simterm]

$ cd kustomize-helm/
$ helm template . > helm-all.yaml && kustomize build .              
apiVersion: v1
kind: ServiceAccount
metadata:
  labels:
    app.kubernetes.io/instance: release-name
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/name: kustomize-helm
    app.kubernetes.io/version: 1.16.0
    helm.sh/chart: kustomize-helm-0.1.0
  name: release-name-kustomize-helm
---
apiVersion: v1
data:
  password: MTIzNDU2Nzg=
kind: Secret
metadata:
  name: nginx-password-72mh6dg77t
type: Opaque
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/instance: release-name
...

[/simterm]

Готово.

Loading