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 API:
[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]
Теперь посмотрим, как настроить это приложение для двух окружений – 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.
Используем 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]
Готово.