Sommaire
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 :
- Vous déployez une application avec Helm
- Un collègue bien intentionné modifie manuellement un ConfigMap avec
kubectl edit - Vous lancez
helm upgrade - 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 :
- Kubernetes (bien évidemment)
- Le templating Go (avec ses fonctions spécifiques)
- La structure des charts (templates/, values.yaml, Chart.yaml, helpers.tpl)
- Les hooks Helm (pre-install, post-upgrade, etc.)
- La gestion des CRDs (qui ont des règles spéciales)
- Les named templates et les scopes (., $)
- 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.yamlde 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.

