Organizarea implementării în mai multe medii k8s folosind helmfile

Helmfile - ambalaj pt cârmă, care vă permite să descrieți multe lansări ale cârmei într-un singur loc, să le parametrizați diagramele pentru mai multe medii și, de asemenea, să setați ordinea implementării lor.

Puteți citi despre helmfile în sine și exemple de utilizare în Readme и ghid de bune practici.

Ne vom familiariza cu modalități neevidente de a descrie versiunile în helmfile

Să presupunem că avem un pachet de diagrame helm (de exemplu, să spunem postgres și o aplicație backend) și mai multe medii (mai multe clustere kubernetes, mai multe spații de nume sau mai multe dintre ambele). Luăm fișierul helm, citim documentația și începem să descriem mediile și versiunile noastre:

    .
    ├── 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 ajuns să avem 2 medii: dezvolta, producere — fiecare conține propriile valori pentru diagramele de lansare a cârmei. Le vom implementa astfel:

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

Diferite versiuni de diagrame de cârmă în diferite medii

Ce se întâmplă dacă trebuie să lansăm versiuni diferite ale backend-ului în medii diferite? Cum se parametriză versiunea de lansare? Valorile de mediu disponibile prin {{ .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 }}
...

Set diferit de aplicații în medii diferite

Grozav, dar dacă nu avem nevoie production lansați postgres, pentru că știm că nu trebuie să împingem baza de date în k8s și avem de vânzare un minunat cluster postgres separat? Pentru a rezolva această problemă avem etichete

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

Acest lucru este grozav, dar personal prefer să descriu ce aplicații să implementez în mediu nu folosind argumente de lansare, ci în descrierea mediilor în sine. Ce să fac? Puteți plasa descrierile versiunilor într-un folder separat, puteți crea o listă cu versiunile necesare în descrierea mediului și puteți „prelua” doar versiunile necesare, ignorând restul

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

Nota

Când utilizați bases: este necesar să se folosească separator yaml ---, astfel încât să puteți șablona versiuni (și alte părți, cum ar fi helmDefaults) cu valori din medii

În acest caz, versiunea postgres nici măcar nu va fi inclusă în descrierea pentru producție. Foarte confortabil!

Valori globale suprascrise pentru versiuni

Desigur, este grozav că puteți seta valori pentru diagramele de conducere pentru fiecare mediu, dar ce se întâmplă dacă avem mai multe medii descrise și vrem, de exemplu, să setăm același lucru pentru toate affinity, dar nu vrem să-l configuram implicit în diagramele în sine, care sunt stocate în napi.

În acest caz, pentru fiecare lansare am putea specifica 2 fișiere cu valori: primul cu valori implicite, care va determina valorile diagramei în sine, iar al doilea cu valori pentru mediu, care la rândul lor va suprascrie cele implicite.

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

Definirea valorilor globale pentru diagramele de conducere ale tuturor lansărilor la nivel de mediu

Să presupunem că creăm mai multe intrări în mai multe versiuni - am putea defini manual pentru fiecare diagramă hosts:, dar în cazul nostru domeniul este același, așa că de ce să nu îl puneți într-o variabilă globală și pur și simplu să-i înlocuiți valoarea în diagrame? Pentru a face acest lucru, acele fișiere cu valori pe care dorim să le parametrizăm vor trebui să aibă extensia .gotmpl, astfel încât helmfile să știe că trebuie rulat prin motorul de șablon.

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

Nota

Evident, intrarea în diagrama postgres este ceva extrem de dubios, așa că acest articol este dat pur și simplu ca un exemplu sferic în vid și pentru a nu introduce vreo versiune nouă în articol doar de dragul de a descrie intrarea.

Înlocuirea secretelor din valorile mediului

Prin analogie cu exemplul de mai sus, le puteți înlocui pe cele criptate folosind secretele cârmei sensuri. În loc să ne creăm propriul fișier de secrete pentru fiecare lansare, în care putem defini valori criptate pentru diagramă, putem defini pur și simplu în versiunea default.yaml.gotmpl valorile care vor fi preluate din variabilele definite la nivelul mediului. Iar valorile pe care nu trebuie să le ascundem de nimeni pot fi ușor redefinite în valorile de lansare într-un anumit mediu.

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

Nota

Apropo, getOrNil - o funcție specială pentru șabloanele go în helmfile, care, chiar dacă .Values.secrets nu va exista, nu va arunca o eroare, dar va permite rezultatul folosind funcția default înlocuiți valoarea implicită

Concluzie

Lucrurile descrise par destul de evidente, dar informațiile despre o descriere convenabilă a implementării în mai multe medii care utilizează helmfile sunt foarte rare și îmi place IaC (Infrastructure-as-Code) și vreau să am o descriere clară a stării de implementare.

În concluzie, aș dori să adaug că variabilele pentru mediul implicit pot fi, la rândul lor, parametrizate cu variabilele de mediu ale sistemului de operare al unui anumit runner de pe care va fi lansată implementarea, și astfel să se obțină medii dinamice.

helmfile.yaml

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

Sursa: www.habr.com

Adauga un comentariu