Organizzazione del deployment in piΓΉ ambienti k8s con helmfile

Helmfile β€” un wrapper per helm, che consente di descrivere in un unico luogo piΓΉ rilasci helm, parametrizzare i loro chart per diversi ambienti e stabilire l'ordine della loro distribuzione.

Puoi leggere di helmfile e esempi del suo utilizzo in readme e guida alle migliori pratiche.

Ora esploreremo modi meno ovvi per descrivere i rilasci in helmfile.

Supponiamo di avere un insieme di chart helm (per esempio, postgres e un'applicazione backend) e diversi ambienti (piΓΉ cluster kubernetes, piΓΉ namespace o entrambi). Prendiamo helmfile, leggiamo la documentazione e iniziamo a descrivere i nostri ambienti e rilasci:

    .
    β”œβ”€β”€ envs
    β”‚Β Β  β”œβ”€β”€ devel
    β”‚Β Β  β”‚Β Β  └── values
    β”‚Β Β  β”‚Β Β      β”œβ”€β”€ backend.yaml
    β”‚Β Β  β”‚Β Β      └── postgres.yaml
    β”‚Β Β  └── production
    β”‚Β Β      └── values
    β”‚Β Β          β”œβ”€β”€ backend.yaml
    β”‚Β Β          └── postgres.yaml
    └── helmfile.yaml

helmfile.yaml

ambienti:
  devel:
  produzione:

rilasci:
  - nome: postgres
    etichette:
      app: postgres
    attesa: true
    chart: stable/postgresql
    versione: 8.4.0
    valori:
      - envs/{{ .Environment.Name }}/values/postgres.yaml
  - nome: backend
    etichette:
      app: backend
    attesa: true
    chart: private-helm-repo/backend
    versione: 1.0.5
    necessitΓ :
      - postgres
    valori:
      - envs/{{ .Environment.Name }}/values/backend.yaml

Abbiamo creato 2 ambienti: devel, produzione — ognuno contiene i propri valori per i chart helm dei rilasci. Li porteremo in produzione così:

helmfile -n  -e  apply

Versioni diverse dei chart helm in ambienti diversi

Cosa fare se dobbiamo rilasciare versioni diverse del backend in ambienti diversi? Come parametrizzare la versione del rilascio? A questo scopo, intervengono i valori degli ambienti, accessibili tramite {{ .Values }}

helmfile.yaml

ambienti:
  devel:
+   valori:
+   - charts:
+       versioni:
+         backend: 1.1.0
  produzione:
+   valori:
+   - charts:
+       versioni:
+         backend: 1.0.5
...
  - nome: backend
    etichette:
      app: backend
    attesa: true
    chart: private-helm-repo/backend
-   versione: 1.0.5
+   versione: {{ .Values.charts.versioni.backend }}
...

Set di applicazioni diverse in ambienti diversi

Ottimo, ma cosa succede se non dobbiamo produzione rilasciare postgres, perchΓ© sappiamo che non dobbiamo inserire un database in k8s e per la produzione abbiamo un eccellente 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 nell'ambiente non tramite argomenti di avvio, ma nella descrizione degli ambienti stessi. Cosa fare? Si può inserire la descrizione delle versioni in una cartella separata, nella descrizione dell'ambiente creare un elenco delle versioni necessarie e "collegare" solo le versioni 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


  ambienti:
    devel:
      valori:
      - grafici:
          versioni:
            backend: 1.1.0
      - app:
        - postgres
        - backend

    produzione:
      valori:
      - grafici:
          versioni:
            backend: 1.0.5
      - app:
        - backend

- rilasci:
-    - nome: postgres
-      etichette:
-        app: postgres
-      attendi: true
-      grafico: stable/postgresql
-      versione: 8.4.0
-      valori:
-        - envs/{{ .Environment.Name }}/valori/postgres.yaml
-    - nome: backend
-      etichette:
-        app: backend
-      attendi: true
-      grafico: private-helm-repo/backend
-     versione: {{ .Values.charts.versions.backend }}
-     necessitΓ :
-       - postgres
-     valori:
-       - envs/{{ .Environment.Name }}/valori/backend.yaml
+ ---
+ basi:
+ {{- range .Values.apps }}
+   - rilasci/{{ . }}.yaml
+ {{- end }}

rilasci/postgres.yaml

rilasci:
  - nome: postgres
    etichette:
      app: postgres
    attendi: true
    grafico: stable/postgresql
    versione: 8.4.0
    valori:
      - envs/{{ .Environment.Name }}/valori/postgres.yaml

rilasci/backend.yaml

rilasci:
  - nome: backend
    etichette:
      app: backend
    attendi: true
    grafico: private-helm-repo/backend
    versione: {{ .Values.charts.versions.backend }}
    necessitΓ :
      - postgres
    valori:
      - envs/{{ .Environment.Name }}/valori/backend.yaml

Nota

Quando utilizzi basi: Γ¨ necessario utilizzare il delimitatore yaml ---, per poter template i rilasci (e le altre parti, come helmDefaults) con i valori di environments

In questo caso il rilascio di postgres non verrΓ  nemmeno incluso nella descrizione per la produzione. Molto comodo!

Valori globali sovrascrivibili per i rilasci

È fantastico poter impostare valori per i chart helm per ogni ambiente, ma cosa succede se abbiamo descritto diversi ambienti e vogliamo, ad esempio, impostare un valore uguale per tutti affinity, ma non vogliamo configurarlo di default nei chart stessi, che sono conservati nei repository.

In questo caso potremmo specificare due file di values per ogni rilascio: il primo con i valori predefiniti, che definiranno i valori del chart stesso, e il secondo con i valori per l'ambiente, che a sua volta sovrascriverΓ  i valori predefiniti.

    .
    β”œβ”€β”€ 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

rilasci/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"

Definizione dei valori globali per i chart Helm di tutti i rilasci a livello di ambiente

Supponiamo che in diversi rilasci vengano creati piΓΉ ingress β€” potremmo definire manualmente per ogni chart hosts:, ma nel nostro caso il dominio Γ¨ lo stesso, quindi perchΓ© non estrarlo in una variabile globale e semplicemente sostituire il valore nei chart? A tal fine, i file values che vogliamo parametrizzare dovrebbero avere l'estensione .gotmpl, in modo che helmfile sappia che deve essere elaborato dal 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.yaml

helmfile.yaml

  ambienti:
    devel:
      valori:
      - grafici:
          versioni:
            backend: 1.1.0
      - app:
        - postgres
        - backend
+     - globale:
+         dominioIngress: k8s.devel.domain

    produzione:
      valori:
      - grafici:
          versioni:
            backend: 1.0.5
      - app:
        - backend
+     - globale:
+         dominioIngress: production.domain
  ---
  basi:
  {{- range .Values.apps }}
    - releases/{{ . }}.yaml
  {{- end }}

envs/default/values/backend.yaml.gotmpl

ingress:
  abilitato: true
  percorsi:
    - /api
  host:
    - {{ .Values.global.ingressDomain }}

envs/default/values/postgres.yaml.gotmpl

ingress:
  abilitato: true
  percorsi:
    - /
  host:
    - postgres.{{ .Values.global.ingressDomain }}

Nota

È chiaro che l'ingress nel chart di postgres è qualcosa di estremamente discutibile, quindi è riportato nell'articolo solo come esempio teorico e per non introdurre un nuovo rilascio solo per descrivere l'ingress.

Sostituzione dei segreti (secrets) dai valori dell'ambiente

Analogamente all'esempio sopra, Γ¨ possibile sostituire anche quelli crittografati con helm secrets valori. Invece di creare un file secrets per ogni rilascio, in cui definiamo i valori crittografati per il chart, possiamo semplicemente definire i valori nel default.yaml.gotmpl del rilascio, che verranno presi dalle variabili impostate a livello di ambiente. E i valori che non dobbiamo nascondere a nessuno possono essere facilmente ridefiniti nei valori del rilascio 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.yaml

helmfile.yaml

  ambienti:
    devel:
      valori:
      - grafici:
          versioni:
            backend: 1.1.0
      - app:
        - postgres
        - backend
      - globale:
          dominioIngress: k8s.devel.domain
+     segreti:
+       - envs/devel/secrets.yaml

    produzione:
      valori:
      - grafici:
          versioni:
            backend: 1.0.5
      - app:
        - backend
      - globale:
          dominioIngress: production.domain
+     segreti:
+       - envs/production/secrets.yaml
  ---
  basi:
  {{- range .Values.apps }}
    - releases/{{ . }}.yaml
  {{- end }}

envs/devel/secrets.yaml

segreti:
    elastic:
        password: ENC[AES256_GCM,data:hjCB,iv:Z1P6/6xBJgJoKLJ0UUVfqZ80o4L84jvZfM+uH9gBelc=,tag:dGqQlCZnLdRAGoJSj63rBQ==,type:int]
...

envs/production/secrets.yaml

segreti:
    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 "segreti.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

Nota

A proposito, getOrNil β€” una funzione speciale per i template go in helmfile, che, anche se .Values.segreti non esisterΓ , non genererΓ  un errore, ma permetterΓ  come risultato di usare la funzione default per inserire un valore predefinito

Conclusione

Le cose descritte possono sembrare piuttosto ovvie, ma le informazioni su come descrivere comodamente il deployment in piΓΉ ambienti usando helmfile sono molto scarse, e io amo IaC (Infrastructure-as-Code) e voglio avere una chiara descrizione dello stato del deployment.

In conclusione, vorrei aggiungere che le variabili per l'ambiente predefinito possono essere a loro volta parametrizzate con le variabili d'ambiente del sistema operativo di un certo runner, dal quale verrΓ  avviato il deployment, e in questo modo si possono ottenere ambienti dinamici.

helmfile.yaml

ambienti:
  predefinito:
    valori:
    - globale:
        clusterDomain: {{ env "CLUSTER_DOMAIN" | default "cluster.local" }}
        ingressDomain: {{ env "INGRESS_DOMAIN" }}

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server πŸ”₯ Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster