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]
Готово.