Blog | WeScale

Helm 4 : Un pas en avant, prenons le temps de regarder ailleurs

Rédigé par Aubin Morand | 24/03/2026

Pourquoi il est temps de repenser votre stratégie Kubernetes

Helm 4 est arrivé en novembre 2025. Six ans après Helm 3, cette nouvelle version majeure promet de réduire la dette technique, d'améliorer les performances et d'introduire de nouvelles fonctionnalités.

Mais alors que la communauté se prépare à célébrer cette évolution et que les entreprises ont déjà commencé à créer des tickets Jira pour préparer la migration, il est temps de poser une question inconfortable : Helm est-il toujours la meilleure solution pour déployer vos applications Kubernetes ? Ou perpétuons-nous simplement une habitude devenue obsolète ?

Helm 4 : Que nous apporte vraiment cette nouvelle version ?

Les nouveautés annoncées

La version 4 de Helm apporte plusieurs changements (liste non exhaustive) :

  • Système de plugins entièrement redessiné avec support WebAssembly
  • Support des digests OCI pour installer des chart avec leur version en sha256
  • Values multi-documents pour (enfin) splitter les énormes fichiers values.yaml
  • Fonctions de template étendues (mustToYaml, mustToJson)

Le problème de continuité

Helm 4 se trouve dans une position difficile : il doit innover tout en maintenant une compatibilité quasi-totale avec Helm 3 pour éviter de fragmenter l'écosystème. Cette contrainte, bien qu'elle protège les utilisateurs existants, limite drastiquement la capacité du projet à résoudre ses problèmes fondamentaux.

Les problèmes structurels de Helm (que la v4 n’adresse pas)

1. Le templating Go : un écueil à chaque frappe de clavier

Helm utilise le moteur de templating Go, ce qui semble logique puisque Helm est écrit en Go. Mais cette décision technique crée une barrière d'entrée importante et des manifestes rapidement illisibles.

Exemple:


{{- if .Values.ingress.enabled -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: {{ include "myapp.fullname" . }}
  {{- with .Values.ingress.annotations }}
  annotations:
    {{- toYaml . | nindent 4 }}
  {{- end }}
spec:
  {{- if .Values.ingress.tls }}
  tls:
    {{- range .Values.ingress.tls }}
    - hosts:
        {{- range .hosts }}
        - {{ . | quote }}
        {{- end }}
      secretName: {{ .secretName }}
    {{- end }}
  {{- end }}
  rules:
    {{- range .Values.ingress.hosts }}
    - host: {{ .host | quote }}
      http:
        paths:
          {{- range .paths }}
          - path: {{ .path }}
            pathType: {{ .pathType }}
            backend:
              service:
                name: {{ $.Values.service.name }}
                port:
                  number: {{ $.Values.service.port }}
          {{- end }}
    {{- end }}
{{- end }}

Les problèmes :

  • Debugging cauchemardesque : L’exemple ci-dessus est simple. J’encourage ceux prônant la simplicité de Helm à jeter un œil aux templates de la chart de déploiement de Gitlab par exemple.
  • Pas d'IDE support natif : L'autocomplétion et la validation sont limités
  • Whitespace hell : Les tirets ({{- vs {{) pour gérer les espaces rendent le code fragile
  • Courbe d'apprentissage : Il faut maîtriser Kubernetes ET le templating Go
  • Difficilement testable : Des solutions pour tester des chart helm existants (helm-unittest pour du test unitaire, terratest pour de l’intégration, etc ). J’ai essayé, j’ai détesté. On n’est pas très loin du push and pray.

2. La gestion d'état : une fausse bonne idée

Helm maintient l'état des déploiements dans des secrets stockés dans le cluster. En théorie, c'est sympa pour les rollbacks. En pratique, c'est une source de problèmes :

Problème 1 : Divergence entre l'état Helm et la réalité

Imaginons ce scénario courant :

  1. Vous déployez une application avec Helm
  2. Un collègue bien intentionné modifie manuellement un ConfigMap avec kubectl edit
  3. Vous lancez helm upgrade
  4. Helm ne détecte pas le changement manuel et écrase silencieusement la modification

Problème 2 : Pollution du namespace

$ kubectl get secrets -n production | grep helm
sh.helm.release.v1.myapp.v1 helm.sh/release.v1 1 180d
sh.helm.release.v1.myapp.v2 helm.sh/release.v1 1 175d
sh.helm.release.v1.myapp.v3 helm.sh/release.v1 1 170d
...
sh.helm.release.v1.myapp.v47 helm.sh/release.v1 1 2d

Chaque release Helm crée un nouveau secret. Votre cluster accumule ces secrets qui ne servent qu'aux rollbacks Helm.

Problème 3 : Les rollbacks sont trompeurs

helm rollback ne fait PAS un vrai rollback Kubernetes, il ne rollback que l’image que helm se fait de l’état précédent.

Ca peut sembler être un comportement attendu, mais ça veut dire en pratique que des ressources orphelines ou mal gérées peuvent continuer d’exister, notamment des PV, PVC, CRD, jobs, etc.

De plus, helm ne re-télécharge pas systématiquement les dépendances, ce qui pose des problèmes de versionning:

# Chart.yaml
dependencies:
- name: postgresql
version: "12.x.x" # not pinned
repository: "https://charts.bitnami.com/bitnami"

Ici, dans le cas d’un rollback, je n’ai aucune garantie que je vais retrouver le même comportement avant mon upgrade qui a raté. Ceci peut sembler n’être juste une question d'hygiène de gestion des versions, mais quand on commence à dépendre d’une chart externe, qui peut elle-même dépendre d’une autre chart sur laquelle on n’a pas de contrôle car on n’a pas défini de version.

3. Le piège des dépendances

Les charts Helm peuvent avoir des dépendances (sous-charts). C'est puissant sur le papier, mais devient vite ingérable :

# Chart.yaml
dependencies:
- name: postgresql
version: "12.1.2"
repository: "https://charts.bitnami.com/bitnami"
- name: redis
version: "17.3.7"
repository: "https://charts.bitnami.com/bitnami"

Les problèmes :

  • Conflits de versions : Deux dépendances qui requièrent des versions incompatibles d'un même sous-chart
  • Cascade de values.yaml : Pour configurer une sous-dépendance, vous devez naviguer dans une hiérarchie de valeurs complexe
  • Mises à jour risquées : Mettre à jour une dépendance peut casser l'ensemble de votre application
  • Des charts dans des charts dans des charts : Le piège principal est de faire du zèle de factorisation. On se retrouve avec des charts dans des charts (encore une fois, la charte de déploiement de Gitlab), et on peut facilement se retrouver dans le problème du diamant

4. La courbe d'apprentissage invisible

Helm se présente comme simple : "Kubernetes package manager". Mais en réalité, pour l'utiliser correctement, il faut maîtriser :

  1. Kubernetes (bien évidemment)
  2. Le templating Go (avec ses fonctions spécifiques)
  3. La structure des charts (templates/, values.yaml, Chart.yaml, helpers.tpl)
  4. Les hooks Helm (pre-install, post-upgrade, etc.)
  5. La gestion des CRDs (qui ont des règles spéciales)
  6. Les named templates et les scopes (., $)
  7. Les fonctions spécifiques Helm (include, required, toYaml, etc.)

C'est un DSL complet à apprendre EN PLUS de Kubernetes (et du templating Go).

5. Le problème de la "Helm-ification"

Beaucoup de projets open-source fournissent des charts Helm officiels.

Le problème ? Ces charts tentent d'être universels et supportent des dizaines de cas d'usage, résultant en :

  • Des fichiers values.yaml de 500+ lignes
  • Des templates tellement génériques qu'ils sont incompréhensibles
  • Des configurations obscures pour des cas d'usage de niche

Exemple réel : Le chart officiel de Prometheus a plus de 1300 lignes de configuration avec commentaires. Ça fait un document de 1300 lignes à comprendre pour utiliser le produit.

Les alternatives : un écosystème mature

Heureusement, l'écosystème Kubernetes propose des alternatives.

Kustomize : La simplicité native

Philosophie : On reste proche des racines

Exemple d'utilisation :

# base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  replicas: 1
  template:
    spec:
      containers:
      - name: myapp
        image: myapp:1.0.0

# overlays/production/kustomization.yaml
resources:
- ../../base

replicas:
- name: myapp
  count: 3

images:
- name: myapp
  newTag: 1.0.5

Avantages :

  • Natif Kubernetes : intégré directement dans kubectl (kubectl apply -k)
  • Pas de templating
  • Lisible
  • Git-friendly (on peut faire des merge requests et de review, le luxe!)
  • Composition simple : bases + overlays pour différents environnements

Inconvénients :

  • Pas de package management
  • Pas d'historique de déploiement : donc pas de rollback
  • Moins de fonctionnalités avancées : pas de server-side hook, difficulté dans les cas de déploiement complexes (multi-cluster avec multi cloud provider et leurs spécificités)

Quand l'utiliser :

  • On a un ownership total sur l’application
  • On a une configuration multi-environnements avec peu de variations
  • On comprend le kubernetes vanilla, on l’aime, on ne jure que par ça, mais on a besoin de variabiliser un peu

Timoni : La puissance de CUE

Philosophie : On veut du typage, on veut de la fiabilité, on aime la production

Exemple :

// values.cue
#Config: {
    metadata: {
        name: string & =~"^[a-z0-9-]+$"
        namespace: string | *"default"
    }
    replicas: int & >=1 & <=10
    image: {
        repository: string
        tag: string
    }
}

values: #Config & {
    metadata: name: "myapp"
    replicas: 3
    image: {
        repository: "myapp"
        tag: "v1.2.3"
    }
}

Avantages :

  • Typage fort : les erreurs de configuration sont détectées avant le déploiement
  • Validation à la compilation : impossible de déployer une configuration invalide
  • Pas de logique complexe : CUE est déclaratif, pas impératif
  • Module system : réutilisation élégante du code
  • Support OCI : distribution via des registres OCI, on a notre “package” de déploiement à côté de notre image de conteneur applicatif.

Inconvénients :

  • Courbe d'apprentissage : un nouveau langage à apprendre.
  • Écosystème jeune : peu ou pas de dépendances pré packagés disponibles à utiliser en dépendance (penser postgresql, redis, etc)
  • Tooling : aussi peu d'intégrations IDE qu'avec helm (à savoir pas grand chose)
  • Adoption limitée : communauté plus petite

Quand l'utiliser :

  • Infrastructures complexes nécessitant une validation stricte
  • Équipes avec une forte culture de typage et validation
  • Projets où on a un SLA a tenir

Carvel (ytt + kapp) : La séparation des responsabilités

Philosophie : Séparer le templating (ytt) du déploiement (kapp).

ytt pour le templating :

#@ load("@ytt:data", "data")

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: myapp-config
data:
  database_url: #@ data.values.database.url
  #@ if data.values.features.caching:
  cache_enabled: "true"
  #@ end

kapp pour le déploiement :

kapp deploy -a myapp -f config/ --diff-changes

Avantages :

  • Séparation claire : templating ≠ déploiement
  • kapp gère vraiment l'état
  • Diff visuel
  • Gestion des dépendances : le moteur de règles permet d’ordonner la création de ressources
  • Rollback intelligent : basé sur l'état réel, pas sur des secrets

Inconvénients :

  • Toolchain multiple : deux outils à installer et maintenir
  • Moins connu : documentation et exemples moins abondants
  • Syntaxe ytt : encore un DSL à apprendre (bien que plus simple que Helm)

Quand l'utiliser :

  • Besoin d'un vrai suivi de l'état des ressources
  • Déploiements complexes nécessitant un ordre d'exécution précis
  • On veut séparer la templating du déploiement

ArgoCD / FluxCD : L'approche GitOps

Philosophie : Git est notre source de vérité, ce qu’on met dans git, c’est ce qu’on a dans notre cluster Kubernetes. Et en passant, on réconcilie (presque) tout le monde.

Exemple avec ArgoCD :

# Application manifest
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp
spec:
  project: default
  source:
    repoURL: https://github.com/myorg/myapp
    targetRevision: main
    path: k8s/production
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

Avantages :

  • Git comme source de vérité : un commit = un déploiement
  • Audit trail complet : l'historique Git devient l'historique des déploiements
  • Réconciliation automatique : détecte et corrige les drifts
  • Multi-cluster : gestion centralisée de plusieurs clusters
  • Compatible avec tout : peut déployer Helm (avec perte de support des hooks dans le cas d’argo), Kustomize, YAML brut

Inconvénients :

  • Infrastructure additionnelle : tire des sujets d’architecture, de droits, etc
  • Complexité initiale
  • Changement de paradigme : toute modification doit passer par Git. Même si c’est moins un sujet aujourd’hui, les équipes doivent être acculturées.
  • Secrets management : nécessite des outils supplémentaires (External Secrets, Kops, etc)

Quand l'utiliser :

  • Organisation mature adoptant le GitOps
  • Besoin de compliance et d'audit
  • Gestion multi-cluster
  • Besoin d’automatiser

Pulumi / CDK8s : Infrastructure as Code

Philosophie : Utiliser un vrai langage de programmation pour définir une infrastructure.

Exemple Pulumi (TypeScript) :

import * as k8s from "@pulumi/kubernetes";

const appLabels = { app: "nginx" };

const deployment = new k8s.apps.v1.Deployment("nginx", {
    spec: {
        selector: { matchLabels: appLabels },
        replicas: 3,
        template: {
            metadata: { labels: appLabels },
            spec: {
                containers: [{
                    name: "nginx",
                    image: "nginx:1.21",
                    ports: [{ containerPort: 80 }],
                }],
            },
        },
    },
});

const service = new k8s.core.v1.Service("nginx", {
    spec: {
        type: "LoadBalancer",
        selector: appLabels,
        ports: [{ port: 80, targetPort: 80 }],
    },
});

export const serviceIp = service.status.loadBalancer.ingress[0].ip;

Avantages :

  • Langage de programmation complet : boucles, conditions, fonctions
  • Type safety (selon le langage utilisé)
  • Testable (avec plus ou moins de facilité selon le langage utilisé)
  • Réutilisabilité : créer des composants, publier des packages npm/PyPI/…
  • Multi-cloud : gérer K8s + AWS + Azure dans le même code

Inconvénients :

  • Courbe d'apprentissage : besoin de connaître le langage de programmation
  • Philosophie différente : impératif vs déclaratif
  • Pulumi Backend : nécessite un backend pour stocker l'état, c’est ce que fait helm avec les secrets, on retombe dans les mêmes travers
  • Debugging : erreurs potentiellement plus complexes, car dépendantes de l’implémentation et de la version de la librairie

Quand l'utiliser :

  • Équipes avec forte culture dev
  • Infrastructure complexe nécessitant de la logique
  • Besoin de tests automatisés de l'infrastructure
  • Multi-cloud ou infrastructure hybride

Plain Kubernetes YAML : Le retour aux sources

Parfois, la meilleure solution est la plus simple.

Avantages :

  • Zéro abstraction : ce que vous voyez est ce que vous déployez
  • Zéro dépendance : sauf kubectl, mais difficile de faire du kubernetes sans
  • Debugging trivial : on débug toujours du kubernetes, peu importe la surcouche qu’on y applique.

Inconvénients :

  • Répétition : duplication de code entre environnements

Quand l'utiliser :

  • Prototypes et PoCs
  • Applications très simples (< 5 ressources Kubernetes)
  • Apprentissage de Kubernetes
  • Déploiements one-shot
  • Sans retenue si on couple avec du GitOps

Alors, faut-il encore utiliser Helm ?

La réponse est nuancée : Ça dépend.

Il est judicieux de rester sur Helm si :

  • Vous déployez principalement des applications tierces (PostgreSQL, Redis, Kafka, etc.) d’une source de confiance (exemple: Bitnami est maintenant derrière un paywall, c'était auparavant une source commune de chart et image communautaires)
  • Vous utilisez intensivement l'écosystème de charts communautaires
  • Vous n'avez pas les ressources pour une migration
  • Vous avez un biais du coût irrécupérable

Il serait peut être pas mal d’envisager le plain Yaml / Kustomize si :

  • Vous développez principalement des applications internes
  • Vous voulez rester proche du Kubernetes natif
  • Vos configurations sont relativement simples avec quelques variations environnementales
  • Vous privilégiez la lisibilité et la simplicité

On peut penser à Timoni si :

  • La validation stricte des configurations est un besoin
  • Vous appréciez le typage fort
  • Vous êtes prêt à investir dans l'apprentissage de CUE

Mais dans tous les cas, pensez au GitOps si :

  • Vous voulez de la traçabilité
  • Vous gérez plusieurs clusters
  • Vous voulez une réconciliation automatique
  • Votre organisation est mature

La bonne solution, c'est accepter qu'elle n’est pas universelle

Helm a été révolutionnaire en 2016. Il a démocratisé le déploiement d'applications sur Kubernetes et a permis à des milliers d'organisations d'adopter cette technologie.

Aujourd'hui, le paysage a changé.

Kubernetes est mature, les équipes sont plus expérimentées, et nous avons compris que l'abstraction n'est pas toujours la réponse.

Parfois, la simplicité d'un overlay Kustomize sera avantageuse par rapport la flexibilité et la réusabilité d'une chart Helm.

Parfois, la mise en place d’une validation stricte avec Timoni permettra de fiabiliser les déploiements.

Parfois, la chart helm nous est fournie, est maintenue, alors pourquoi ne pas l’utiliser, tant qu’on fait confiance à la source.

Helm 4 est une évolution nécessaire pour maintenir l'outil pertinent, mais elle n'adresse pas les problèmes fondamentaux du produit.

La vraie question n'est pas "Faut-il utiliser Helm ?" mais "Quel est le bon outil dans MON contexte pour déployer sur Kubernetes ?"

La maturité technologique, c'est savoir choisir le bon outil pour le bon problème, pas suivre ce que tout le monde utilise.

 

Et puis aujourd’hui encore, je ne sais toujours pas si on dit “un” chart ou “une” chart helm. Et rien que ça, ça méritait un article.