Аб самім helmfile і прыкладах яго выкарыстання можна пачытаць у
Мы ж пазнаёмімся з невідавочным спосабам апісаць рэлізы ў helmfile
Дапушчальны, у нас есць пачак helm-чартаў (для прыкладу хай будзе postgres і нейкае backend прыкладанне) і некалькі акружэнняў (некалькі kubernetes кластараў, некалькі namespace'аў ці некалькі і таго, і іншага). Бярэм helmfile, чытаем дакументацыю і пачынаем апісваць нашы асяроддзі і рэлізы:
.
├── envs
│ ├── devel
│ │ └── values
│ │ ├── backend.yaml
│ │ └── postgres.yaml
│ └── production
│ └── values
│ ├── backend.yaml
│ └── postgres.yaml
└── helmfile.yaml
helmfile.yaml
environments:
devel:
production:
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: 1.0.5
needs:
- postgres
values:
- envs/{{ .Environment.Name }}/values/backend.yaml
У нас атрымалася 2 асяроддзі: развіваць, вытворчасць - У кожным знаходзяцца свае значэнні для helm чартаў рэлізаў. Мы будзем дэплоіць у іх так:
helmfile -n <namespace> -e <env> apply
Розныя версіі helm чартаў у розных асяродках
Што рабіць, калі нам трэба выкочваць розныя версіі бэкенда ў розныя асяроддзі? Як параметрызаваць версію рэлізу? На дапамогу прыходзяць значэнні асяроддзя, даступныя праз {{ .Values }}
helmfile.yaml
environments:
devel:
+ values:
+ - charts:
+ versions:
+ backend: 1.1.0
production:
+ values:
+ - charts:
+ versions:
+ backend: 1.0.5
...
- name: backend
labels:
app: backend
wait: true
chart: private-helm-repo/backend
- version: 1.0.5
+ version: {{ .Values.charts.versions.backend }}
...
Розны набор прыкладанняў у розных асяродках
Выдатна, але што калі нам не трэба ў production
выкочваць postgres, таму што мы ведаем, што не трэба базу дадзеных штурхаць ў k8s і для прода ў нас есць выдатны асобны кластар postgres? Для вырашэння гэтай праблемы ў нас ёсць лэйблы (labels)
helmfile -n <namespace> -e devel apply
helmfile -n <namespace> -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.yaml
releases/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 файла з values: першы з дэфолтнымі значэннямі, якія будуць вызначаць значэнні самага чарта, а другі са значэннямі для асяроддзя, які ў сваю чаргу ўжо будзе перавызначаць дэфолтныя.
.
├── 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.yaml
releases/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.yaml
envs/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.yaml
helmfile.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) са значэнняў асяроддзя
Па аналогіі з вышэйпрыведзеным прыкладам можна падстаўляць і зашыфраваныя з дапамогай
.
├── 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.yaml
helmfile.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.domain
envs/production/values/backend.yaml
elasticsearch:
host: elastic-0.production.domain
нататка
Дарэчы, getOrNil
- спецыяльная функцыя для go шаблонаў у helmfile, якая, нават калі .Values.secrets
не будзе існаваць, не выкіне памылку, а дазволіць у выніку з дапамогай функцыі default
падставіць значэнне па-змаўчанні
Заключэнне
Апісаныя рэчы здаюцца даволі відавочнымі, але інфармацыя па зручным апісанні дэплою ў некалькі асяродкаў з дапамогай helmfile вельмі бедная, а я кахаю IaC(Infrastructure-as-Code) і жадаю мець выразнае апісанне стейта дэплою.
У заключэнне хачу дадаць, што зменныя для асяроддзя default можна ў сваю чаргу параметрызаваць зменнымі асяроддзі АС нейкага раннера, з якога будзе запускацца дэплой, і такім чынам атрымаць дынамічныя асяроддзі
helmfile.yaml
environments:
default:
values:
- global:
clusterDomain: {{ env "CLUSTER_DOMAIN" | default "cluster.local" }}
ingressDomain: {{ env "INGRESS_DOMAIN" }}
Крыніца: habr.com