— обвивка за , която позволява на едно място да се описват множество helm релизи, да се параметризира техният чарт за различни среди, както и да се задава реда на тяхното внедряване.
За самия helmfile и примери за неговото използване можете да прочетете в и .
Ние ще се запознаем с не толкова очевидни начини как да опишем релизите в helmfile
Да кажем, че имаме набор от helm чартове (за примера да бъде postgres и някакво backend приложение) и няколко среди (няколко kubernetes клъстера, няколко namespace или и двете). Вземаме helmfile, четем документацията и започваме да описваме нашите среди и релизи:
.
├── envs
│ ├── devel
│ │ └── values
│ │ ├── backend.yaml
│ │ └── postgres.yaml
│ └── production
│ └── values
│ ├── backend.yaml
│ └── postgres.yaml
└── helmfile.yamlhelmfile.yaml
среди:
devel:
production:
релизи:
- име: postgres
етикети:
приложение: postgres
чакай: true
чарт: stable/postgresql
версия: 8.4.0
стойности:
- envs/{{ .Environment.Name }}/values/postgres.yaml
- име: backend
етикети:
приложение: backend
чакай: true
чарт: private-helm-repo/backend
версия: 1.0.5
нужда:
- postgres
стойности:
- envs/{{ .Environment.Name }}/values/backend.yamlПолучихме 2 среди: devel, production — във всяка от които се намират свои стойности за helm чартовете на релизите. Ние ще внедряваме в тях по следния начин:
helmfile -n -e applyРазлични версии на helm чартове в различни среди
Какво да правим, ако трябва да внедряваме различни версии на бекенда в различни среди? Как да параметризируем версията на релиза? На помощ идват стойностите на средата, достъпни чрез {{ .Values }}
helmfile.yaml
среди:
devel:
+ стойности:
+ - чартове:
+ версии:
+ backend: 1.1.0
production:
+ стойности:
+ - чартове:
+ версии:
+ backend: 1.0.5
...
- име: backend
етикети:
приложение: backend
чакай: true
чарт: private-helm-repo/backend
- версия: 1.0.5
+ версия: {{ .Values.charts.versions.backend }}
...Различен набор от приложения в различни среди
Чудесно, но какво ако не ни трябва да production внедряваме postgres, защото знаем, че не трябва да слагаме база данни в k8s и за продукция имаме страхотен отделен клъстер postgres? За решаване на този проблем имаме етикети (labels)
helmfile -n -e devel apply
helmfile -n -e production -l app=backend applyТова е страхотно, но лично предпочитам да описвам какви приложения да се разгръщат в средата не чрез аргументи при стартиране, а в описанието на самите среди. Какво да направя? Може да поставите описанията на версиите в отделна папка, в описанието на средата да създадете списък на нужните версии и да "освободите" само нужните версии, игнорирайки останалите.
.
├── envs
│ ├── devel
│ │ └── values
│ │ ├── backend.yaml
│ │ └── postgres.yaml
│ └── production
│ └── values
│ ├── backend.yaml
│ └── postgres.yaml
+ ├── releases
+ │ ├── backend.yaml
+ │ └── postgres.yaml
└── helmfile.yaml
helmfile.yaml
environments:
devel:
values:
- charts:
versions:
backend: 1.1.0
- apps:
- postgres
- backend
production:
values:
- charts:
versions:
backend: 1.0.5
- apps:
- backend
- releases:
- - name: postgres
- labels:
- app: postgres
- wait: true
- chart: stable/postgresql
- version: 8.4.0
- values:
- - envs/{{ .Environment.Name }}/values/postgres.yaml
- - name: backend
- labels:
- app: backend
- wait: true
- chart: private-helm-repo/backend
- version: {{ .Values.charts.versions.backend }}
- needs:
- - postgres
- values:
- - envs/{{ .Environment.Name }}/values/backend.yaml
+ ---
+ bases:
+ {{- range .Values.apps }}
+ - releases/{{ . }}.yaml
+ {{- end }}releases/postgres.yaml
releases:
- name: postgres
labels:
app: postgres
wait: true
chart: stable/postgresql
version: 8.4.0
values:
- envs/{{ .Environment.Name }}/values/postgres.yamlreleases/backend.yaml
releases:
- name: backend
labels:
app: backend
wait: true
chart: private-helm-repo/backend
version: {{ .Values.charts.versions.backend }}
needs:
- postgres
values:
- envs/{{ .Environment.Name }}/values/backend.yamlБележка
При използването на bases: необходимо е задължително да се използва yaml разделител ---, за да може да се шаблонизира releases (и останалите части, като helmDefaults) с параметри от environments
В такъв случай, версията postgres дори няма да влезе в описанието за production. Много удобно!
Препокриваеми глобални стойности за версиите
Разбира се, страхотно е, че можем да задаваме стойности за helm чартовете за всяка среда, но какво ако имаме описани няколко среди и искаме, да кажем, да зададем идентична стойност за всички affinity, но не искаме да я настройваме по подразбиране в самите чартове, които се съхраняват в репозитории.
В такъв случай бихме могли да зададем за всяка версия 2 файла с стойности: един с дефолтни стойности, които ще определят стойностите на самия чарт, и втори със стойности за средата, който от своя страна вече ще препокрива дефолтните.
.
├── envs
+ │ ├── default
+ │ │ └── values
+ │ │ ├── backend.yaml
+ │ │ └── postgres.yaml
│ ├── devel
│ │ └── values
│ │ ├── backend.yaml
│ │ └── postgres.yaml
│ └── production
│ └── values
│ ├── backend.yaml
│ └── postgres.yaml
├── releases
│ ├── backend.yaml
│ └── postgres.yaml
└── helmfile.yamlreleases/backend.yaml
releases:
- name: backend
labels:
app: backend
wait: true
chart: private-helm-repo/backend
version: {{ .Values.charts.versions.backend }}
needs:
- postgres
values:
+ - envs/default/values/backend.yaml
- envs/{{ .Environment.Name }}/values/backend.yamlenvs/default/values/backend.yaml
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values:
- backend
topologyKey: "kubernetes.io/hostname"Определяне на глобални стойности за helm чартовете на всички релизи на ниво среда
Да предположим, че в няколко релиза създаваме няколко ingress — можем ръчно да определим за всеки чарт hosts:, но в нашия случай домейнът е един и същ, защо да не го изнесем в глобална променлива и просто да задаваме нейното значение в чартовете? За това файловете с values, които искаме да параметризиране, трябва да имат разширение .gotmpl, за да може helmfile да знае, че трябва да се обработят през шаблонизатора.
.
├── envs
│ ├── default
│ │ └── values
- │ │ ├── backend.yaml
- │ │ ├── postgres.yaml
+ │ │ ├── backend.yaml.gotmpl
+ │ │ └── postgres.yaml.gotmpl
│ ├── devel
│ │ └── values
│ │ ├── backend.yaml
│ │ └── postgres.yaml
│ └── production
│ └── values
│ ├── backend.yaml
│ └── postgres.yaml
├── releases
│ ├── backend.yaml
│ └── postgres.yaml
└── helmfile.yamlhelmfile.yaml
environments:
devel:
values:
- charts:
versions:
backend: 1.1.0
- apps:
- postgres
- backend
+ - global:
+ ingressDomain: k8s.devel.domain
production:
values:
- charts:
versions:
backend: 1.0.5
- apps:
- backend
+ - global:
+ ingressDomain: production.domain
---
bases:
{{- range .Values.apps }}
- releases/{{ . }}.yaml
{{- end }}envs/default/values/backend.yaml.gotmpl
ingress:
enabled: true
paths:
- /api
hosts:
- {{ .Values.global.ingressDomain }}envs/default/values/postgres.yaml.gotmpl
ingress:
enabled: true
paths:
- /
hosts:
- postgres.{{ .Values.global.ingressDomain }}Бележка
Очевидно, че ingress в чарта postgres е нещо изключително съмнително, затова в статията това е приведено просто като сферичен пример в вакуум и за да не въвеждаме нов релиз само за описанието на ingress.
Подмяната на секрети (secrets) от стойностите на средата.
По аналогия с горния пример, можем да подменим и стойности, шифрувани с помощта на стоиности. Вместо да създаваме файл със секрети за всеки релиз, в който да определяме за чарта шифрованите стойности, можем просто да определим в default.yaml.gotmpl стойности, които ще бъдат взети от променливи, зададени на ниво среди. А стойностите, които не желаем да крием от никого, можем спокойно да преопределим в стойностите на релиза в конкретната среда.
.
├── envs
│ ├── default
│ │ └── values
│ │ ├── backend.yaml
│ │ └── postgres.yaml
│ ├── devel
│ │ ├── values
│ │ │ ├── backend.yaml
│ │ │ └── postgres.yaml
+ │ │ └── secrets.yaml
│ └── production
│ ├── values
│ │ ├── backend.yaml
│ │ └── postgres.yaml
+ │ └── secrets.yaml
├── releases
│ ├── backend.yaml
│ └── postgres.yaml
└── helmfile.yamlhelmfile.yaml
environments:
devel:
values:
- charts:
versions:
backend: 1.1.0
- apps:
- postgres
- backend
- global:
ingressDomain: k8s.devel.domain
+ secrets:
+ - envs/devel/secrets.yaml
production:
values:
- charts:
versions:
backend: 1.0.5
- apps:
- backend
- global:
ingressDomain: production.domain
+ secrets:
+ - envs/production/secrets.yaml
---
bases:
{{- range .Values.apps }}
- releases/{{ . }}.yaml
{{- end }}envs/devel/secrets.yaml
secrets:
elastic:
password: ENC[AES256_GCM,data:hjCB,iv:Z1P6/6xBJgJoKLJ0UUVfqZ80o4L84jvZfM+uH9gBelc=,tag:dGqQlCZnLdRAGoJSj63rBQ==,type:int]
...envs/production/secrets.yaml
secrets:
elastic:
password: ENC[AES256_GCM,data:ZB/VpTFk8f0=,iv:EA//oT1Cb5wNFigTDOz3nA80qD9UwTjK5cpUwLnEXjs=,tag:hMdIUaqLRA8zuFBd82bz6A==,type:str]
...envs/default/values/backend.yaml.gotmpl
elasticsearch:
host: elasticsearch
port: 9200
password: {{ .Values | getOrNil "secrets.elastic.password" | default "password" }}envs/devel/values/backend.yaml
elasticsearch:
host: elastic-0.devel.domainenvs/production/values/backend.yaml
elasticsearch:
host: elastic-0.production.domainБележка
Между другото, getOrNil — специална функция за go шаблони в helmfile, която дори когато .Values.secrets не съществуват, не ще предизвика грешка, а ще позволи в резултат с помощта на функция default да подменим стойност по подразбиране.
Заключение
Описаните неща изглеждат доста очевидни, но информацията за удобно описание на деплоя в няколко среди с помощта на helmfile е много оскъдна, а аз обичам IaC (Infrastructure-as-Code) и искам да имам ясно описание на състоянието на деплоя.
В заключение искам да добавя, че променливите за средата по подразбиране могат да бъдат параметризирани от променливи на операционната система на даденото сядане, от което ще бъде стартиран деплой, и по този начин да се получат динамични среди.
helmfile.yaml
environments:
default:
values:
- global:
clusterDomain: {{ env "CLUSTER_DOMAIN" | default "cluster.local" }}
ingressDomain: {{ env "INGRESS_DOMAIN" }}Източник: habr.com
