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