β un wrapper per , che consente di descrivere in un luogo centrale molteplici rilasci di helm, parametrizzare i loro chart per diversi ambienti e stabilire lβordine del loro deployment.
Puoi leggere di helmfile e degli esempi del suo utilizzo nella e .
Noi ci concentreremo su modi non ovvi per descrivere i rilasci in helmfile.
Supponiamo di avere un insieme di helm chart (per esempio, postgres e una certa applicazione backend) e diversi ambienti (diversi cluster Kubernetes, diversi namespace o entrambi). Prendiamo helmfile, leggiamo la documentazione e iniziamo a descrivere i nostri ambienti e i nostri rilasci:
.
βββ envs
β βββ devel
β β βββ values
β β βββ backend.yaml
β β βββ postgres.yaml
β βββ production
β βββ values
β βββ backend.yaml
β βββ postgres.yaml
βββ helmfile.yamlhelmfile.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.yamlAbbiamo creato 2 ambienti: devel, production β ciascuno contiene i propri valori per i rilasci dei chart helm. Li deployeremo in questo modo:
helmfile -n -e applyDiverse versioni dei chart helm in diversi ambienti
Cosa fare se dobbiamo rilasciare diverse versioni del backend in diversi ambienti? Come parametrizzare la versione del rilascio? In nostro aiuto vengono i valori dell'ambiente, accessibili tramite {{ .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 }}
...Diverso set di applicazioni in diversi ambienti
Ottimo, ma e se non ci serve rilasciare production postgres, perchΓ© sappiamo che non dobbiamo inserire un database in k8s e per la produzione abbiamo un meraviglioso cluster postgres separato? Per risolvere questo problema abbiamo etichette (labels)
helmfile -n -e devel apply
helmfile -n -e production -l app=backend applyΓ fantastico, ma personalmente preferisco descrivere quali applicazioni distribuire in un ambiente non attraverso gli argomenti di avvio, ma nella descrizione dei singoli ambienti. Cosa fare? Si puΓ² inserire la descrizione delle release in una cartella separata, nel descrizione dell'ambiente si puΓ² creare un elenco delle release necessarie e "collegare" solo quelle necessarie, ignorando le altre.
.
βββ 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.yamlNota
Quando utilizzi bases: Γ¨ necessario utilizzare obbligatoriamente il delimitatore yaml ---, in modo da poter template unc releases (e altre parti, come helmDefaults) con i valori provenienti dagli environments
In questo caso, la release postgres non verrΓ nemmeno inclusa nella descrizione per la produzione. Molto comodo!
Valori globali sovrascrivibili per le release
Certo, Γ¨ fantastico che si possano impostare valori diversi per i chart helm di ogni ambiente, ma cosa succede se abbiamo diversi ambienti e vogliamo, ad esempio, impostare lo stesso valore per tutti affinity, ma non vogliamo configurarlo per impostazione predefinita nei chart stessi, che sono archiviati nei repository.
In questo caso, potremmo per ogni release specificare 2 file con valori: il primo con i valori di default, che determineranno i valori del chart stesso, e il secondo con i valori per l'ambiente, che a sua volta sovrascriverΓ i default.
.
βββ 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"Definizione dei valori globali per i chart helm di tutti i rilasci a livello di ambiente
Supponiamo di avere piΓΉ ingress in diversi rilasci β potremmo definire manualmente per ciascun chart hosts:, ma nel nostro caso il dominio Γ¨ lo stesso e quindi perchΓ© non estrarlo in una variabile globale e semplicemente usare il suo valore nei chart? A tal fine, i file con i valori che vogliamo parametrizzare dovranno avere estensione .gotmpl, in modo che helmfile sappia che deve passarli attraverso il templater.
.
βββ 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 }}Nota
Γ evidente che l'ingress nel chart di postgres sia qualcosa di estremamente discutibile, quindi nell'articolo Γ¨ riportato semplicemente come esempio sferico nel vuoto e per non introdurre una nuova release solo per descrivere l'ingress.
Sostituzione dei segreti (secrets) dai valori di ambiente.
Analogamente all'esempio sopra, Γ¨ possibile sostituire anche i valori cifrati attraverso il . Invece di creare un file secrets per ogni release in cui definire i valori cifrati per il chart, possiamo semplicemente definire nel file default.yaml.gotmpl della release i valori che verranno presi dalle variabili definite a livello di ambiente. E i valori che non dobbiamo tenere nascosti possono essere sovrascritti tranquillamente nei valori della release in un ambiente specifico.
.
βββ 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.domainNota
A proposito, getOrNil β una funzione speciale per i modelli go in helmfile, che, anche se .Values.secrets non esiste, non genera un errore, ma consente alla fine di utilizzare la funzione default per inserire il valore di default.
Conclusione
Le cose descritte sembrano piuttosto ovvie, ma le informazioni su come descrivere comodamente il deployment in diversi ambienti usando helmfile sono piuttosto scarse, e io amo IaC (Infrastructure-as-Code) e voglio avere una chiara descrizione dello stato del deployment.
In conclusione, voglio aggiungere che le variabili per l'ambiente predefinito possono a loro volta essere parametrizzate tramite le variabili di ambiente del sistema operativo di un runner da cui verrΓ eseguito il deployment, ottenendo in questo modo ambienti dinamici.
helmfile.yaml
environments:
default:
values:
- global:
clusterDomain: {{ env "CLUSTER_DOMAIN" | default "cluster.local" }}
ingressDomain: {{ env "INGRESS_DOMAIN" }}Fonte: habr.com
