Organisieren der Bereitstellung in mehreren K8S-Umgebungen mithilfe von Helmfile

Helmdatei - Hülle für Helm, mit dem Sie viele Helm-Releases an einem Ort beschreiben, ihre Diagramme für mehrere Umgebungen parametrisieren und auch die Reihenfolge ihrer Bereitstellung festlegen können.

Über Helmfile selbst und Beispiele für seine Verwendung können Sie in lesen readme и Best Practices-Leitfaden.

Wir werden uns mit nicht offensichtlichen Möglichkeiten zur Beschreibung von Releases in Helmfile vertraut machen

Nehmen wir an, wir haben ein Paket von Helm-Charts (zum Beispiel Postgres und einige Backend-Anwendungen) und mehrere Umgebungen (mehrere Kubernetes-Cluster, mehrere Namespaces oder mehrere von beidem). Wir nehmen die Helmdatei, lesen die Dokumentation und beginnen mit der Beschreibung unserer Umgebungen und Releases:

    .
    ├── 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

Am Ende hatten wir zwei Umgebungen: devel, Produktion – jeder enthält seine eigenen Werte für die Helm-Release-Charts. Wir werden sie wie folgt bereitstellen:

helmfile -n <namespace> -e <env> apply

Verschiedene Versionen von Steuerkarten in verschiedenen Umgebungen

Was ist, wenn wir verschiedene Versionen des Backends in verschiedenen Umgebungen einführen müssen? Wie parametriere ich die Release-Version? Die verfügbaren Umweltwerte durch {{ .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 }}
...

Verschiedene Anwendungen in unterschiedlichen Umgebungen

Großartig, aber was ist, wenn wir es nicht müssen? production Postgres einführen, weil wir wissen, dass wir die Datenbank nicht in k8s verschieben müssen und zum Verkauf einen wunderbaren separaten Postgres-Cluster haben? Um dieses Problem zu lösen, haben wir Etiketten

helmfile -n <namespace> -e devel apply
helmfile -n <namespace> -e production -l app=backend apply

Das ist großartig, aber ich persönlich beschreibe lieber, welche Anwendungen in der Umgebung bereitgestellt werden sollen, nicht mit Startargumenten, sondern in der Beschreibung der Umgebungen selbst. Was zu tun ist? Sie können die Release-Beschreibungen in einem separaten Ordner ablegen, eine Liste der erforderlichen Releases in der Umgebungsbeschreibung erstellen und nur die erforderlichen Releases „abholen“ und den Rest ignorieren

    .
    ├── 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

Beachten

Wenn Sie bases: Es ist notwendig, ein Yaml-Trennzeichen zu verwenden ---, sodass Sie Releases (und andere Teile wie helmDefaults) mit Werten aus Umgebungen als Vorlage erstellen können

In diesem Fall wird die Postgres-Version nicht einmal in die Beschreibung für die Produktion aufgenommen. Sehr bequem!

Überschreibbare globale Werte für Releases

Natürlich ist es großartig, dass Sie für jede Umgebung Werte für Helmdiagramme festlegen können, aber was ist, wenn wir mehrere Umgebungen beschrieben haben und wir beispielsweise für alle dieselben Werte festlegen möchten? affinity, aber wir möchten es nicht standardmäßig in den Diagrammen selbst konfigurieren, die in Turnips gespeichert sind.

In diesem Fall könnten wir für jede Version zwei Dateien mit Werten angeben: die erste mit Standardwerten, die die Werte des Diagramms selbst bestimmen, und die zweite mit Werten für die Umgebung, die wiederum die Werte überschreiben Standardwerte.

    .
    ├── 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"

Definieren globaler Werte für Helmdiagramme aller Releases auf Umgebungsebene

Nehmen wir an, wir erstellen in mehreren Releases mehrere Ingress-Daten – wir könnten sie für jedes Diagramm manuell definieren hosts:, aber in unserem Fall ist die Domäne dieselbe. Warum also nicht sie in eine globale Variable einfügen und ihren Wert einfach in die Diagramme einsetzen? Dazu müssen die Dateien mit Werten, die wir parametrisieren möchten, die Erweiterung haben .gotmpl, damit die Helmdatei weiß, dass sie über die Template-Engine ausgeführt werden muss.

    .
    ├── 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 }}

Beachten

Offensichtlich ist das Eindringen in das Postgres-Diagramm etwas äußerst Zweifelhaftes, daher wird dieser Artikel lediglich als kugelförmiges Beispiel im luftleeren Raum gegeben und um nicht nur zur Beschreibung des Eindringens eine neue Version in den Artikel einzuführen

Ersetzen von Geheimnissen durch Umgebungswerte

Analog zum obigen Beispiel können Sie verschlüsselte durch ersetzen Helmgeheimnisse Bedeutungen. Anstatt für jede Version eine eigene Secrets-Datei zu erstellen, in der wir verschlüsselte Werte für das Diagramm definieren können, können wir einfach in der Version default.yaml.gotmpl die Werte definieren, die aus den in der Version definierten Variablen übernommen werden Umgebungsebene. Und die Werte, die wir vor niemandem verbergen müssen, können in den Release-Werten in einer bestimmten Umgebung problemlos neu definiert werden.

    .
    ├── 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

Beachten

Übrigens getOrNil - eine spezielle Funktion für Go-Vorlagen in Helmfile, die, auch wenn .Values.secrets wird nicht existieren, wird keinen Fehler auslösen, aber das Ergebnis mithilfe der Funktion zulassen default Ersetzen Sie den Standardwert

Abschluss

Die beschriebenen Dinge scheinen ziemlich offensichtlich zu sein, aber Informationen zu einer praktischen Beschreibung der Bereitstellung in mehreren Umgebungen mithilfe von Helmfile sind sehr rar, und ich liebe IaC (Infrastructure-as-Code) und möchte eine klare Beschreibung des Bereitstellungsstatus haben.

Abschließend möchte ich hinzufügen, dass die Variablen für die Standardumgebung wiederum mit den Umgebungsvariablen des Betriebssystems eines bestimmten Läufers parametrisiert werden können, von dem aus die Bereitstellung gestartet wird, und so dynamische Umgebungen erhalten

helmfile.yaml

environments:
  default:
    values:
    - global:
        clusterDomain: {{ env "CLUSTER_DOMAIN" | default "cluster.local" }}
        ingressDomain: {{ env "INGRESS_DOMAIN" }}

Source: habr.com

Kommentar hinzufügen