Organisation du déploiement sur plusieurs environnements K8s à l'aide de helmfile

Fichier de barre - emballage pour barre, qui vous permet de décrire de nombreuses versions de Helm en un seul endroit, de paramétrer leurs graphiques pour plusieurs environnements et également de définir l'ordre de leur déploiement.

Vous pouvez en savoir plus sur le fichier helm lui-même et des exemples de son utilisation dans readme и guide des bonnes pratiques.

Nous ferons connaissance avec des manières non évidentes de décrire les versions dans le fichier helmfile

Disons que nous avons un pack de graphiques de barre (par exemple, disons postgres et une application backend) et plusieurs environnements (plusieurs clusters Kubernetes, plusieurs espaces de noms ou plusieurs des deux). Nous prenons le fichier helm, lisons la documentation et commençons à décrire nos environnements et nos versions :

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

Nous nous sommes retrouvés avec 2 environnements : développement, production — chacun contient ses propres valeurs pour les tableaux de relâchement de la barre. Nous leur déploierons comme ceci :

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

Différentes versions de graphiques de barre dans différents environnements

Que se passe-t-il si nous devons déployer différentes versions du backend dans différents environnements ? Comment paramétrer la version release ? Les valeurs environnementales disponibles à travers {{ .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 }}
...

Différents ensembles d'applications dans différents environnements

Super, mais que se passe-t-il si nous n'en avons pas besoin production déployer Postgres, parce que nous savons que nous n'avons pas besoin de pousser la base de données dans K8 et que nous avons à vendre un merveilleux cluster Postgres séparé ? Pour résoudre ce problème nous avons des étiquettes

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

C'est génial, mais personnellement, je préfère décrire quelles applications déployer dans l'environnement non pas en utilisant des arguments de lancement, mais dans la description des environnements eux-mêmes. Ce qu'il faut faire? Vous pouvez placer les descriptions de versions dans un dossier séparé, créer une liste des versions nécessaires dans la description de l'environnement et « récupérer » uniquement les versions nécessaires, en ignorant le reste.

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

Заметка

Lorsque vous utilisez le bases: il est nécessaire d'utiliser le séparateur yaml ---, afin que vous puissiez modéliser les versions (et d'autres parties, telles que helmDefaults) avec les valeurs des environnements

Dans ce cas, la version postgres ne sera même pas incluse dans la description de production. Très confortablement !

Valeurs globales remplaçables pour les versions

Bien sûr, c'est formidable que vous puissiez définir des valeurs pour les graphiques de barre pour chaque environnement, mais que se passe-t-il si nous avons plusieurs environnements décrits et que nous voulons, par exemple, définir la même chose pour tous ? affinity, mais nous ne voulons pas le configurer par défaut dans les graphiques eux-mêmes, qui sont stockés dans les navets.

Dans ce cas, pour chaque version, nous pourrions spécifier 2 fichiers avec des valeurs : le premier avec des valeurs par défaut, qui détermineront les valeurs du graphique lui-même, et le second avec des valeurs pour l'environnement, qui à leur tour remplaceront les ceux par défaut.

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

Définition des valeurs globales pour les graphiques de barre de toutes les versions au niveau de l'environnement

Disons que nous créons plusieurs entrées dans plusieurs versions - nous pourrions définir manuellement pour chaque graphique hosts:, mais dans notre cas, le domaine est le même, alors pourquoi ne pas le mettre dans une variable globale et simplement substituer sa valeur dans les graphiques ? Pour ce faire, les fichiers avec les valeurs que nous souhaitons paramétrer devront avoir l'extension .gotmpl, afin que helmfile sache qu'il doit être exécuté via le moteur de modèles.

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

Заметка

De toute évidence, l'entrée dans le graphique postgres est quelque chose d'extrêmement douteux, donc cet article est donné simplement comme un exemple sphérique dans le vide et afin de ne pas introduire une nouvelle version dans l'article juste pour décrire l'entrée.

Remplacer les secrets par les valeurs environnementales

Par analogie avec l'exemple ci-dessus, vous pouvez remplacer les cryptés en utilisant secrets de barre significations. Au lieu de créer notre propre fichier secrets pour chaque version, dans lequel nous pouvons définir des valeurs cryptées pour le graphique, nous pouvons simplement définir dans la version default.yaml.gotmpl les valeurs qui seront extraites des variables définies au niveau niveau environnement. Et les valeurs que nous n'avons besoin de cacher à personne peuvent être facilement redéfinies dans les valeurs de version dans un environnement spécifique.

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

Заметка

Soit dit en passant, getOrNil - une fonction spéciale pour les modèles go dans helmfile, qui, même si .Values.secrets n'existera pas, ne générera pas d'erreur, mais permettra le résultat en utilisant la fonction default remplacer la valeur par défaut

Conclusion

Les choses décrites semblent assez évidentes, mais les informations sur une description pratique du déploiement dans plusieurs environnements à l'aide de helmfile sont très rares, et j'adore IaC (Infrastructure-as-Code) et je souhaite avoir une description claire de l'état de déploiement.

En conclusion, je voudrais ajouter que les variables de l'environnement par défaut peuvent, à leur tour, être paramétrées avec les variables d'environnement de l'OS d'un certain runner à partir duquel le déploiement sera lancé, et ainsi obtenir des environnements dynamiques

helmfile.yaml

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

Source: habr.com

Ajouter un commentaire