Możesz przeczytać o samym pliku Helmfile i przykładach jego użycia w
Zapoznamy się z nieoczywistymi sposobami opisywania wydań w pliku sterowym
Załóżmy, że mamy pakiet wykresów Helm (na przykład postgres i jakąś aplikację zaplecza) i kilka środowisk (kilka klastrów Kubernetes, kilka przestrzeni nazw lub kilka obu). Bierzemy plik sterowy, czytamy dokumentację i zaczynamy opisywać nasze środowiska i wydania:
.
├── 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
Skończyło się na 2 środowiskach: devel, produkcja — każdy zawiera własne wartości dla wykresów zwolnienia steru. Wdrożymy je w następujący sposób:
helmfile -n <namespace> -e <env> apply
Różne wersje wykresów steru w różnych środowiskach
Co się stanie, jeśli będziemy musieli wdrożyć różne wersje backendu w różnych środowiskach? Jak sparametryzować wersję wydania? Wartości środowiskowe dostępne poprzez {{ .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 }}
...
Różne zestawy aplikacji w różnych środowiskach
Świetnie, ale co, jeśli nie musimy production
wdrożyć postgres, bo wiemy, że nie musimy wpychać bazy danych do k8s i na sprzedaż mamy wspaniały, osobny klaster Postgres? Aby rozwiązać ten problem, mamy etykiety
helmfile -n <namespace> -e devel apply
helmfile -n <namespace> -e production -l app=backend apply
To świetnie, ale osobiście wolę opisywać, które aplikacje należy wdrożyć w środowisku, nie używając argumentów uruchamiania, ale opisując same środowiska. Co robić? Możesz umieścić opisy wydań w osobnym folderze, stworzyć listę potrzebnych wydań w opisie środowiska i „wybrać” tylko te niezbędne wydania, resztę ignorując
.
├── 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
Notatka
Podczas korzystania z bases:
konieczne jest użycie separatora yaml ---
, dzięki czemu możesz szablonować wydania (i inne części, takie jak helmDefaults) z wartościami ze środowisk
W takim przypadku wersja Postgres nie zostanie nawet uwzględniona w opisie wersji produkcyjnej. Bardzo wygodnie!
Nadpisywane wartości globalne dla wydań
Oczywiście super, że można ustawić wartości dla wykresów steru dla każdego środowiska, ale co jeśli mamy opisanych kilka środowisk i chcemy np. ustawić to samo dla wszystkich affinity
, ale nie chcemy tego domyślnie konfigurować w samych wykresach, które są przechowywane w rzepie.
W tym przypadku dla każdego wydania moglibyśmy określić 2 pliki z wartościami: pierwszy z wartościami domyślnymi, które określą wartości samego wykresu, a drugi z wartościami dla środowiska, co z kolei nadpisze domyślne.
.
├── 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"
Definiowanie globalnych wartości dla wykresów steru wszystkich wydań na poziomie środowiska
Załóżmy, że tworzymy kilka wejść w kilku wersjach – możemy ręcznie zdefiniować dla każdego wykresu hosts:
, ale w naszym przypadku domena jest taka sama, więc dlaczego nie umieścić jej w jakiejś zmiennej globalnej i po prostu zastąpić jej wartość na wykresach? Aby to zrobić, pliki z wartościami, które chcemy sparametryzować, będą musiały mieć rozszerzenie .gotmpl
, aby plik sterowy wiedział, że należy go uruchomić przez silnik szablonów.
.
├── 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 }}
Notatka
Oczywiście ingres na wykresie postgres jest czymś niezwykle wątpliwym, więc ten artykuł podano po prostu jako sferyczny przykład w próżni i po to, aby nie wprowadzać do artykułu jakiejś nowej wersji tylko po to, by opisać ingres
Zastępowanie wpisów tajnych z wartości środowiska
Analogicznie do powyższego przykładu, możesz zastąpić zaszyfrowane za pomocą
.
├── 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
Notatka
By the way, getOrNil
- specjalna funkcja dla szablonów go w pliku sterowym, które nawet jeśli .Values.secrets
nie będzie istnieć, nie zgłosi błędu, ale pozwoli na wynik za pomocą funkcji default
zastąpić wartość domyślną
wniosek
Opisane rzeczy wydają się dość oczywiste, ale informacje na temat wygodnego opisu wdrożenia w kilku środowiskach przy użyciu pliku Helmfile są bardzo skąpe, a ja uwielbiam IaC (Infrastructure-as-Code) i chcę mieć jasny opis stanu wdrożenia.
Na zakończenie dodam, że zmienne dla środowiska domyślnego można z kolei sparametryzować zmiennymi środowiskowymi systemu operacyjnego danego runnera, z którego zostanie uruchomione wdrożenie, i w ten sposób uzyskać środowiska dynamiczne
helmfile.yaml
environments:
default:
values:
- global:
clusterDomain: {{ env "CLUSTER_DOMAIN" | default "cluster.local" }}
ingressDomain: {{ env "INGRESS_DOMAIN" }}
Źródło: www.habr.com