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