Nelm le futur de Helm ?

thumbernail Kubernetes

Nelm : Redécouvrez le Déploiement de Charts Helm sur Kubernetes

Dans l'écosystème Kubernetes, Helm s'est imposé comme le gestionnaire de paquets de facto, simplifiant la définition, l'installation et la mise à niveau des applications. Cependant, malgré ses qualités, Helm 3 présente certaines limitations et complexités qui peuvent frustrer les équipes DevOps. C'est là qu'intervient Nelm, une alternative open-source construite sur les fondations de Helm 3, mais repensée pour offrir plus de contrôle, de prévisibilité et de robustesse dans vos déploiements.

Utilisé comme moteur de déploiement principal par l'outil CI/CD werf, Nelm conserve une compatibilité de premier ordre avec les Charts Helm existants tout en introduisant des fonctionnalités puissantes inspirées d'autres outils éprouvés comme Terraform.

Ce billet explore en profondeur ce qu'est Nelm, pourquoi vous pourriez l'envisager à la place de Helm, et comment ses fonctionnalités clés peuvent améliorer vos workflows de déploiement Kubernetes.

Qu'est-ce que Nelm ? Une Alternative Améliorée à Helm 3

Nelm n'est pas une réécriture complète de Helm, mais plutôt une évolution. Il reprend la base de code de Helm 3, assure la compatibilité avec ses concepts fondamentaux (Charts, Releases), mais réimplémente ou améliore des parties cruciales, notamment le moteur de déploiement.

Les objectifs principaux de Nelm sont de résoudre des problèmes connus de Helm 3 et d'introduire des capacités plus avancées :

  1. Prévisualisation Fiable des Changements : Offrir une fonctionnalité similaire à terraform plan pour savoir exactement ce qui va changer dans le cluster avant d'appliquer.

  2. Gestion des Mises à Jour Robuste : Remplacer le mécanisme de "Fusion à Trois Voies" (3-Way Merge) de Helm par l'Application Côté Serveur (Server-Side Apply) de Kubernetes, plus fiable.

  3. Ordonnancement Avancé : Permettre un contrôle fin sur l'ordre de déploiement des ressources, y compris les dépendances externes.

  4. Gestion Sécurisée des Secrets : Intégrer nativement le chiffrement des fichiers de valeurs ou de fichiers arbitraires.

  5. Suivi Détaillé et Visibilité : Améliorer le suivi de l'état des ressources, afficher les logs et événements en continu pendant le déploiement.

  6. Correction de Bugs Helm : Résoudre des problèmes récurrents de Helm, comme les erreurs liées aux versions d'API obsolètes.

Tout en offrant ces améliorations, Nelm maintient la compatibilité avec le format des Charts Helm et le stockage des informations de release (Secrets Kubernetes), permettant une transition potentielle depuis Helm.

Pourquoi Envisager Nelm ? Les Fonctionnalités Clés

Explorons les aspects qui différencient Nelm et peuvent apporter une valeur significative à vos processus de déploiement.

1. Planification Précise avec nelm release plan install

L'un des défis avec Helm est de comprendre précisément l'impact d'une commande helm upgrade. Nelm s'attaque à ce problème avec nelm release plan install. Cette commande utilise un dry-run basé sur Server-Side Apply pour générer un diff fiable entre l'état actuel dans le cluster et l'état désiré après application du Chart.

Exemple : Planifier la mise à jour d'une application web

Imaginons un Chart simple pour une application web my-webapp avec un Déploiement et un Service.

  • Chart.yaml:
    apiVersion: v2
    name: my-webapp
    version: 0.1.0
    appVersion: "1.0"
  • values.yaml:
    replicaCount: 1
    image:
      repository: my-registry/my-webapp
      tag: "1.0"
    service:
      type: ClusterIP
      port: 80
  • templates/deployment.yaml:
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: {{ .Release.Name }}-app
    spec:
      replicas: {{ .Values.replicaCount }}
      selector:
        matchLabels:
          app: {{ .Release.Name }}
      template:
        metadata:
          labels:
            app: {{ .Release.Name }}
        spec:
          containers:
            - name: web
              image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
              ports:
                - containerPort: 8080 # Supposons que l'app écoute sur 8080
  • templates/service.yaml:
    apiVersion: v1
    kind: Service
    metadata:
      name: {{ .Release.Name }}-svc
    spec:
      type: {{ .Values.service.type }}
      ports:
        - port: {{ .Values.service.port }}
          targetPort: 8080 # Port du container
      selector:
        app: {{ .Release.Name }}

Après un premier déploiement (nelm release install -n web -r myapp ./my-webapp), nous voulons mettre à jour l'image vers la version "1.1" et passer à 2 réplicas. Planifions ce changement :

nelm release plan install -n web -r myapp ./my-webapp \
  --set image.tag="1.1" \
  --set replicaCount=2

Nelm affichera un résultat clair, indiquant uniquement les modifications prévues sur le Déploiement :

Planning release install "myapp" (namespace: "web")

┌ Update Deployment/myapp-app
│     namespace: web
│   spec:
│ -   replicas: 1
│ +   replicas: 2
│     template:
│       spec:
│         containers:
│           - name: web
│ -           image: "my-registry/my-webapp:1.0"
│ +           image: "my-registry/my-webapp:1.1"
│             ports:
└ Update Deployment/myapp-app

Planned changes summary for release "myapp" (namespace: "web"):
- update: 1 resource(s)

Cette clarté permet d'éviter les surprises et de valider les changements avant de les appliquer réellement avec nelm release install.

2. Server-Side Apply (SSA) au lieu de 3-Way Merge

Le mécanisme de "Fusion à Trois Voies" (3-Way Merge) utilisé par Helm pour calculer les patchs de mise à jour est basé sur l'état connu de la dernière release Helm réussie. Si des modifications manuelles ont eu lieu (kubectl edit), ou si une release précédente a échoué partiellement, Helm peut générer des patchs incorrects ou écraser des changements inattendus.

Nelm utilise l'Application Côté Serveur (Server-Side Apply), une fonctionnalité native de Kubernetes. Avec SSA, c'est le serveur d'API Kubernetes lui-même qui calcule le patch en se basant sur l'état réel de la ressource dans le cluster et l'état désiré soumis par Nelm. Chaque champ est "possédé" par un gestionnaire (ici, Nelm). Cela résout les problèmes de désynchronisation et assure des mises à jour beaucoup plus robustes et prévisibles. C'est la méthode adoptée par des outils modernes comme FluxCD.

3. Ordonnancement Avancé des Ressources

Helm propose un ordonnancement basique via les Hooks et leurs poids (helm.sh/hook-weight). Nelm étend considérablement ces capacités :

  • Annotation werf.io/weight : Similaire à helm.sh/hook-weight, mais applicable à toutes les ressources (pas seulement les hooks). Les ressources avec le même poids sont déployées en parallèle. Les groupes de poids sont déployés séquentiellement (du plus petit au plus grand).

    Exemple : Déployer une ConfigMap avant un Déploiement.

    # templates/configmap.yaml
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: myapp-config
      annotations:
        werf.io/weight: "-10" # Déployé en premier
    data:
      config.json: |
        {"setting": "value"}

    # templates/deployment.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: myapp-deployment
      annotations:
        werf.io/weight: "0" # Déployé après la ConfigMap
    spec:
      # ... spec utilisant la ConfigMap
  • Annotation werf.io/deploy-dependency-<id> : La méthode la plus puissante. Permet à une ressource d'attendre qu'une autre ressource de la même release atteigne un état spécifique (present ou ready) avant de se déployer.

    Exemple : Un Job qui initialise une base de données (StatefulSet) avant le démarrage de l'application (Deployment).

    # templates/database-sts.yaml
    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      name: myapp-db
    spec:
      # ... spec du StatefulSet

    # templates/init-db-job.yaml
    apiVersion: batch/v1
    kind: Job
    metadata:
      name: myapp-init-db
      annotations:
        # Attend que le StatefulSet soit prêt avant de lancer le Job
        werf.io/deploy-dependency-db: "state=ready,kind=StatefulSet,name=myapp-db"
        # Note: Le Job lui-même est souvent un hook Helm (pre-install/pre-upgrade)
        helm.sh/hook: pre-install, pre-upgrade
        helm.sh/hook-weight: "5" # Exécuté après le STS mais avant le déploiement applicatif
    spec:
      # ... spec du Job d'initialisation

    # templates/app-deployment.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: myapp-app
      annotations:
        # Attend que le Job d'initialisation soit terminé (présent suffit souvent pour un Job)
        # Ou si le Job doit absolument réussir, on pourrait pointer vers un état Ready hypothétique si le Job le supportait.
        # Ici, on suppose que la présence du Job terminé est suffisante.
        # Alternativement, si le Job crée une ressource signal (comme un Secret), on pourrait dépendre de cette ressource.
        # Pour cet exemple, nous allons supposer que le hook Helm suffit à séquencer.
        # Si le Job n'était PAS un hook, cette dépendance serait cruciale :
        # werf.io/deploy-dependency-init: "state=present,kind=Job,name=myapp-init-db"
        werf.io/weight: "10" # Assure qu'il se déploie après les hooks de poids inférieur
    spec:
      # ... spec de l'application

(Note : L'exemple combine hooks Helm et dépendances Nelm pour illustrer. Souvent, deploy-dependency remplace le besoin de certains hooks complexes.)*

  • Annotations *.external-dependency.werf.io/* : Permet d'attendre la disponibilité de ressources hors de la release actuelle, typiquement gérées par des opérateurs (ex: un ClusterIssuer de cert-manager, une Database de KubeDB).

    Exemple : Une Ingress qui nécessite un ClusterIssuer géré séparément.

    # templates/ingress.yaml
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: myapp-ingress
      annotations:
        # ID unique pour cette dépendance externe
        certissuer.external-dependency.werf.io/resource: "ClusterIssuer.v1.cert-manager.io/letsencrypt-prod"
        # Si le ClusterIssuer était dans un namespace spécifique (non applicable ici car ClusterIssuer est cluster-scoped) :
        # certissuer.external-dependency.werf.io/namespace: "cert-manager"

        # Annotation standard pour utiliser le ClusterIssuer
        cert-manager.io/cluster-issuer: "letsencrypt-prod"
    spec:
      # ... spec de l'Ingress

Nelm attendra que la ressource ClusterIssuer nommée letsencrypt-prod (avec le groupe d'API cert-manager.io/v1) soit prête avant de créer l'Ingress.

4. Suivi Amélioré des Ressources et Affichage des Logs

Pendant une commande nelm release install, Nelm fournit une sortie beaucoup plus riche que Helm :

  • Tableau de Suivi : Un tableau est affiché périodiquement, montrant l'état de chaque ressource gérée (Waiting, Progressing, Ready, Failed). Pour les ressources comme les Déploiements ou StatefulSets, il suit aussi l'état des Pods sous-jacents.
  • Détection Heuristique pour les CRDs : Nelm tente de déterminer l'état "Ready" de nombreuses Custom Resources en analysant leurs champs status, ce qui fonctionne pour environ la moitié des CRDs sans configuration supplémentaire.
  • Affichage des Logs en Continu : Nelm récupère et affiche les logs des conteneurs des Pods créés ou mis à jour pendant le déploiement. Très utile pour déboguer rapidement les problèmes de démarrage.
  • Affichage des Événements : En ajoutant l'annotation werf.io/show-service-messages: "true" à une ressource, Nelm affichera également les événements Kubernetes associés à cette ressource pendant le déploiement.

Des annotations permettent de personnaliser ce comportement (filtrer les logs par regex, skipper les logs pour certains conteneurs, ignorer les échecs pour des ressources non critiques, etc.). Voir la section Documentation du README pour la liste complète (werf.io/log-regex, werf.io/skip-logs, werf.io/fail-mode, etc.).

5. Gestion Native des Secrets Chiffrés

Gérer les secrets (mots de passe, clés d'API) dans Git est toujours délicat. Nelm intègre une solution inspirée de helm-secrets, mais nativement :

  • Fichiers de Valeurs Chiffrés : Vous pouvez créer des fichiers comme secret-values.yaml qui sont chiffrés sur disque mais déchiffrés en mémoire lors du nelm install ou nelm render. Leurs valeurs sont accessibles via .Values comme d'habitude.

  • Fichiers Arbitraires Chiffrés : Vous pouvez chiffrer n'importe quel fichier (ex: config.json, cert.pem) et le placer dans un répertoire secret/ à la racine du Chart. Ces fichiers peuvent être injectés dans les templates via la fonction {{ werf_secret_file "monfichier.txt" }}.

Le chiffrement utilise une clé AES-256-GCM générée via nelm chart secret key create et fournie via la variable d'environnement NELM_SECRET_KEY.

Exemple : Utiliser des identifiants de base de données chiffrés

  1. Générer une clé (à faire une seule fois et à stocker de manière sécurisée) :
    export NELM_SECRET_KEY=$(nelm chart secret key create)
    # Stocker cette clé quelque part (ex: variable CI/CD, gestionnaire de secrets)
    echo $NELM_SECRET_KEY
  1. Créer/éditer un fichier de valeurs secret :
    # Ouvre $EDITOR pour créer/modifier le fichier, le chiffre à la sauvegarde
    nelm chart secret values-file edit my-webapp/secret-values.yaml
  1. Contenu de secret-values.yaml (dans l'éditeur) :
    database:
      username: dbuser
      password: SecurePassword123!

Le fichier my-webapp/secret-values.yaml sur disque contiendra des données chiffrées.

  1. Utiliser les valeurs dans un template (ex: pour créer un Secret Kubernetes) :
    # templates/db-secret.yaml
    apiVersion: v1
    kind: Secret
    metadata:
      name: {{ .Release.Name }}-db-credentials
    type: Opaque
    stringData:
      username: {{ .Values.database.username }}
      password: {{ .Values.database.password }}
  1. Déployer (Nelm utilisera secret-values.yaml automatiquement si NELM_SECRET_KEY est définie) :
    export NELM_SECRET_KEY=... # Fournir la clé
    nelm release install -n web -r myapp ./my-webapp

Exemple : Utiliser un fichier de configuration API chiffré

  1. Créer/éditer un fichier secret dans le répertoire secret/ :
    # Assurez-vous que NELM_SECRET_KEY est définie
    mkdir -p my-webapp/secret
    nelm chart secret file edit my-webapp/secret/api-config.json
  1. Contenu de api-config.json (dans l'éditeur) :
    {
      "apiKey": "abcdef123456",
      "endpoint": "https://api.example.com/v1"
    }

Le fichier my-webapp/secret/api-config.json sur disque sera chiffré.

  1. Injecter le contenu dans une ConfigMap :
    # templates/api-configmap.yaml
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: {{ .Release.Name }}-api-config
    data:
      config.json: |
        {{- werf_secret_file "api-config.json" | nindent 8 }}
  1. Déployer :
    export NELM_SECRET_KEY=... # Fournir la clé
    nelm release install -n web -r myapp ./my-webapp

La ConfigMap dans Kubernetes contiendra le JSON déchiffré.

Compatibilité avec Helm

Nelm se veut un remplacement "drop-in" pour de nombreux cas d'usage de Helm :

  • Charts : Les Charts Helm 3 sont directement utilisables avec Nelm sans modification. Toutes les fonctionnalités de templating, y compris les fonctions lookup, sont supportées.
  • Releases : Nelm utilise le même format de stockage des releases (Secrets dans le namespace de la release) que Helm 3. Théoriquement, vous pouvez alterner entre helm et nelm pour gérer la même release (bien que ce ne soit généralement pas recommandé en production).
  • Commandes CLI : La structure des commandes est différente, mais les fonctionnalités sont largement équivalentes. Voici une correspondance rapide :

    Helm Command Nelm Command
    helm upgrade --install --atomic --wait -n ns rel ./c nelm release install --auto-rollback -n ns -r rel ./c
    helm uninstall -n ns release nelm release uninstall -n ns -r release
    helm template ./chart nelm chart render ./chart
    helm lint ./chart nelm chart lint ./chart
    helm dependency build ./chart nelm chart dependency download ./chart
    helm get values -n ns release nelm release get values -n ns -r release
    (Plugin helm-diff) helm diff upgrade ... (Natif) nelm release plan install ...
    (Plugin helm-secrets) helm secrets ... (Natif) nelm chart secret ...
  • Plugins Helm : Non supportés directement. Nelm privilégie l'intégration native des fonctionnalités utiles (comme plan et secret).

La migration consiste principalement à adapter vos scripts CI/CD pour utiliser les commandes Nelm équivalentes.

Conclusion : Plus de Contrôle et de Fiabilité pour vos Déploiements

Nelm se présente comme une alternative sérieuse et réfléchie à Helm 3 pour les équipes qui recherchent plus de prévisibilité, de contrôle et de robustesse dans leurs déploiements Kubernetes. En remplaçant le 3-Way Merge par Server-Side Apply, en offrant une planification détaillée des changements, un ordonnancement avancé des ressources, un suivi amélioré et une gestion native des secrets, Nelm répond à plusieurs points de douleur courants rencontrés avec Helm.

Sa compatibilité avec les Charts Helm existants facilite son adoption. Si vous utilisez déjà werf, vous bénéficiez déjà de Nelm. Sinon, et si les fonctionnalités décrites ici vous semblent pertinentes pour vos défis de déploiement, Nelm mérite assurément d'être exploré.

Pour commencer, consultez les instructions d'installation et essayez-le sur un projet de test !

Découvrez les technologies d'alter way