Implementering af deployment i flere k8s-miljøer ved hjÌlp af helmfile

Helmfile — et wrapper til helm, som gør det muligt at beskrive mange helm releases på ét sted, parametrisere deres charts til flere miljøer og også angive rækken for deres deployment.

Om helmfile og eksempler pĂĽ dets brug kan lĂŚses i readme og bedste praksis guide.

Vi vil møde nogle uventede müder at beskrive releases i helmfile pü

Lad os sige, at vi har en rÌkke helm charts (for eksempel postgres og en backend applikation) og flere miljøer (flere Kubernetes klynger, flere namespaces eller en kombination af begge). Vi tager helmfile, lÌser dokumentationen og begynder at beskrive vores miljøer og 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

Vi har fået 2 miljøer: devel, production — i hver af dem er der sine værdier for helm charts af releases. Vi vil deploye i dem sådan her:

helmfile -n  -e  apply

Forskellige versioner af helm charts i forskellige miljøer

Hvad skal vi gøre, hvis vi skal rulle forskellige versioner af backend ud i forskellige miljøer? Hvordan kan vi parameterisere versionen af et release? Her kommer miljøvÌrdierne ind, der er tilgÌngelige via {{ .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 }}
...

Forskellige sÌt af applikationer i forskellige miljøer

Godt, men hvad hvis vi ikke har brug for at production rulle postgres ud, fordi vi ved, at vi ikke skal have databasen i k8s, og til produktion har vi en fantastisk separat postgres klynge? For at løse dette problem har vi labels

helmfile -n  -e devel apply
helmfile -n  -e production -l app=backend apply

Det er fantastisk, men personligt foretrÌkker jeg at beskrive, hvilke applikationer der skal implementeres i miljøet, ikke ved hjÌlp af opstartargumenter, men i selve miljøbeskrivelserne. Hvad gør man? Man kan placere versionbeskrivelserne i en separat mappe, oprette en liste over nødvendige versioner i miljøbeskrivelsen og "tilknytte" kun de nødvendige versioner og ignorere de øvrige.

    .
    ├── envs
    │   ├── devel
    │   │   └── values
    │   │       ├── backend.yaml
    │   │       └── postgres.yaml
    │   └── production
    │       └── values
    │           ├── backend.yaml
    │           └── postgres.yaml
+   ├── releases
+   │   ├── backend.yaml
+   │   └── postgres.yaml
    └── helmfile.yaml

helmfile.yaml


  miljøer:
    devel:
      vĂŚrdier:
      - charts:
          versioner:
            backend: 1.1.0
      - apps:
        - postgres
        - backend

    production:
      vĂŚrdier:
      - charts:
          versioner:
            backend: 1.0.5
      - apps:
        - backend

- releases:
-    - navn: postgres
-      labels:
-        app: postgres
-      vent: true
-      chart: stable/postgresql
-      version: 8.4.0
-      vĂŚrdier:
-        - envs/{{ .Environment.Name }}/values/postgres.yaml
-    - navn: backend
-      labels:
-        app: backend
-      vent: true
-      chart: private-helm-repo/backend
-     version: {{ .Values.charts.versions.backend }}
-     behov:
-       - postgres
-     vĂŚrdier:
-       - envs/{{ .Environment.Name }}/values/backend.yaml
+ ---
+ baser:
+ {{- range .Values.apps }}
+   - releases/{{ . }}.yaml
+ {{- end }}

releases/postgres.yaml

releases:
  - navn: postgres
    labels:
      app: postgres
    vent: true
    chart: stable/postgresql
    version: 8.4.0
    vĂŚrdier:
      - envs/{{ .Environment.Name }}/values/postgres.yaml

releases/backend.yaml

releases:
  - navn: backend
    labels:
      app: backend
    vent: true
    chart: private-helm-repo/backend
    version: {{ .Values.charts.versions.backend }}
    behov:
      - postgres
    vĂŚrdier:
      - envs/{{ .Environment.Name }}/values/backend.yaml

Note

Ved brug af baser: det er nødvendigt at bruge yaml-separator. ---, sü man kan anvende releases (og andre dele, süsom helmDefaults) med vÌrdier fra miljøerne.

I sĂĽ fald vil postgres-versionen ikke engang vĂŚre med i beskrivelsen for produktion. Meget praktisk!

Globale vĂŚrdier til overskrivning for versioner.

Det er selvfølgelig fantastisk, at man kan angive vÌrdier for helm charts for hvert miljø, men hvad hvis vi har beskrevet flere miljøer, og vi ønsker, for eksempel, at angive en vÌrdi, som skal vÌre den samme for alle. affinity, men ikke ønsker at indstille det som standard i selve charts, der er gemt i repoerne.

I sü fald kunne vi for hver version angive 2 filer med vÌrdier: en første med standardvÌrdierne, der vil bestemme chartets vÌrdier, og den anden med vÌrdierne for miljøet, som sü vil overskrive standarderne.

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

Definition of global values for helm charts of all releases at the environment level

Suppose we create several ingress in multiple releases — we could manually define this for each chart hosts:, but in our case the domain is the same, so why not pull it out into a global variable and just substitute its value into the charts? For this, the files with values that we want to parameterize should have the extension .gotmpl, so that helmfile knows to process them through the 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

  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 }}

Note

Det er ĂĽbenlyst, at ingress i Postgres-chartet er noget ekstremt tvivlsomt, sĂĽ det nĂŚvnes i artiklen blot som et sfĂŚrisk eksempel i et vakuum og for at undgĂĽ at introducere en ny version kun for at beskrive ingress.

IndsÌttelse af hemmeligheder (secrets) fra miljøvariabler.

Pü samme müde som det ovenfor nÌvnte eksempel kan man indsÌtte vÌrdier, der er krypteret med helm secrets . I stedet for at oprette en unik secrets-fil for hver version, hvor der bestemmes krypterede vÌrdier for chartet, kan vi blot definere vÌrdier i releases default.yaml.gotmpl, som vil blive hentet fra variabler angivet pü miljøniveau. Og vÌrdier, som vi ikke behøver at skjule fra nogen, kan vi trygt overskrive i release-vÌrdierne i det specifikke miljø.

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

Note

Forresten, getOrNil — en speciel funktion til go-skabeloner i helmfile, som, selv hvis .Values.secrets ikke eksisterer, ikke kaster en fejl, men muliggør at bruge funktionen default til at indsætte en standardværdi.

Konklusion

De beskrevne ting virker ret übenlyse, men informationen om praktisk beskrivelse af deployment i flere miljøer med helmfile er meget begrÌnset, og jeg elsker IaC (Infrastructure-as-Code) og ønsker en klar beskrivelse af deployment state.

Afslutningsvis vil jeg tilføje, at variablerne for standardmiljøet kan parametriseres med miljøvariablerne fra et givet runner, som deploymenten vil blive kørt fra, og dermed skabe dynamiske miljøer.

helmfile.yaml

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

Kilde: habr.com

Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere 🔥 Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere | ProHoster