Skip to main content

Helm

⎈ Helm - Package Manager pour Kubernetes

📋 Table des Matières


🎯 Introduction

Helm est le gestionnaire de paquets (package manager) pour Kubernetes, souvent appelé "le apt/yum/homebrew de Kubernetes".

Qu'est-ce que Helm ?

Helm permet de :

  • Packager des applications Kubernetes
  • Déployer des applications complexes en une commande
  • Versionner les déploiements
  • Partager des configurations réutilisables
  • Rollback facilement en cas de problème
  • Gérer les dépendances entre applications

Pourquoi utiliser Helm ?

Sans Helm :

kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
kubectl apply -f ingress.yaml
kubectl apply -f configmap.yaml
kubectl apply -f secret.yaml
kubectl apply -f pvc.yaml
# ... 20 fichiers plus tard

Avec Helm :

helm install mon-app ./mon-chart

Cas d'usage

  • 📦 Déployer des applications complexes (bases de données, monitoring, etc.)
  • 🔄 Gérer plusieurs environnements (dev, staging, prod)
  • 🎯 Standardiser les déploiements dans l'équipe
  • 📚 Réutiliser des configurations
  • 🚀 CI/CD automatisés
  • 📊 Marketplace d'applications (Artifact Hub)

📥 Installation

Linux

# Méthode 1 : Script officiel (Recommandée)
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

# Méthode 2 : Binaire direct
curl -LO https://get.helm.sh/helm-v3.13.3-linux-amd64.tar.gz
tar -zxvf helm-v3.13.3-linux-amd64.tar.gz
sudo mv linux-amd64/helm /usr/local/bin/helm
rm -rf linux-amd64 helm-v3.13.3-linux-amd64.tar.gz

# Méthode 3 : Package manager
# Debian/Ubuntu
curl https://baltocdn.com/helm/signing.asc | gpg --dearmor | sudo tee /usr/share/keyrings/helm.gpg > /dev/null
sudo apt-get install apt-transport-https --yes
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
sudo apt-get update
sudo apt-get install helm

# Arch Linux
sudo pacman -S helm

# Fedora
sudo dnf install helm

# Vérifier
helm version

macOS

# Via Homebrew (Recommandé)
brew install helm

# Ou binaire direct
curl -LO https://get.helm.sh/helm-v3.13.3-darwin-amd64.tar.gz
tar -zxvf helm-v3.13.3-darwin-amd64.tar.gz
sudo mv darwin-amd64/helm /usr/local/bin/helm
rm -rf darwin-amd64 helm-v3.13.3-darwin-amd64.tar.gz

# Vérifier
helm version

Windows

# Via Chocolatey
choco install kubernetes-helm

# Via Scoop
scoop install helm

# Via Winget
winget install Helm.Helm

# Vérifier
helm version

Configuration de l'autocomplétion

# Bash
echo 'source <(helm completion bash)' >> ~/.bashrc
source ~/.bashrc

# Zsh
echo 'source <(helm completion zsh)' >> ~/.zshrc
source ~/.zshrc

# Fish
helm completion fish | source

# PowerShell
helm completion powershell | Out-String | Invoke-Expression

Vérifier l'installation

# Version de Helm
helm version

# Aide
helm help

# Lister les commandes
helm list

# Configuration
helm env

🧩 Concepts Fondamentaux

Architecture Helm

┌─────────────────────────────────────────┐
│           Helm Client (CLI)             │
│  • Gestion des Charts                   │
│  • Communication avec Kubernetes        │
└─────────────────┬───────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────────┐
│       Kubernetes API Server             │
│  • Stockage des releases (Secrets)      │
│  • Déploiement des ressources           │
└─────────────────────────────────────────┘

Note : Helm 3 n'utilise plus Tiller (composant serveur de Helm 2).


Terminologie

Chart

Un Chart est un package Helm contenant :

  • Templates YAML de ressources Kubernetes
  • Fichier Chart.yaml (métadonnées)
  • Fichier values.yaml (configuration par défaut)
  • Fichiers helpers et documentation
mon-chart/
├── Chart.yaml          # Métadonnées du chart
├── values.yaml         # Valeurs par défaut
├── charts/             # Charts dépendants
├── templates/          # Templates Kubernetes
│   ├── deployment.yaml
│   ├── service.yaml
│   ├── ingress.yaml
│   └── _helpers.tpl    # Helpers de templating
└── README.md

Release

Une Release est une instance d'un Chart déployée dans Kubernetes.

# Un même chart peut avoir plusieurs releases
helm install blog-prod ./wordpress
helm install blog-staging ./wordpress
helm install blog-dev ./wordpress

Repository

Un Repository est un serveur hébergeant des Charts.

# Exemples de repositories
https://charts.bitnami.com/bitnami
https://kubernetes.github.io/ingress-nginx
https://prometheus-community.github.io/helm-charts

Values

Les Values sont les variables de configuration d'un Chart.

# values.yaml
replicaCount: 3
image:
  repository: nginx
  tag: "1.25"
service:
  type: LoadBalancer
  port: 80

🎮 Commandes Essentielles

Recherche et Information

# Rechercher un chart sur Artifact Hub
helm search hub <mot-clé>
helm search hub wordpress
helm search hub prometheus

# Rechercher dans les repos ajoutés
helm search repo <mot-clé>
helm search repo nginx
helm search repo mysql

# Voir toutes les versions d'un chart
helm search repo nginx --versions

# Informations sur un chart
helm show chart bitnami/nginx
helm show values bitnami/nginx
helm show readme bitnami/nginx
helm show all bitnami/nginx

Installation et Gestion

# Installer un chart
helm install <nom-release> <chart>

# Exemples
helm install my-nginx bitnami/nginx
helm install my-db bitnami/postgresql
helm install monitoring prometheus-community/kube-prometheus-stack

# Installer avec un namespace
helm install my-app bitnami/nginx --namespace production --create-namespace

# Installer avec des valeurs personnalisées
helm install my-app bitnami/nginx --set replicaCount=3
helm install my-app bitnami/nginx --values custom-values.yaml
helm install my-app bitnami/nginx -f values-prod.yaml

# Installer depuis un fichier local
helm install my-app ./mon-chart

# Installer depuis une archive
helm install my-app mon-chart-1.0.0.tgz

# Dry-run (tester sans installer)
helm install my-app bitnami/nginx --dry-run --debug

Lister et Inspecter

# Lister les releases
helm list
helm ls

# Lister dans tous les namespaces
helm list --all-namespaces
helm list -A

# Lister dans un namespace spécifique
helm list -n production

# Lister toutes les releases (même celles échouées)
helm list --all

# Voir le statut d'une release
helm status <nom-release>

# Voir l'historique des révisions
helm history <nom-release>

# Voir les valeurs utilisées
helm get values <nom-release>

# Voir les manifests déployés
helm get manifest <nom-release>

# Voir toutes les infos
helm get all <nom-release>

Mise à Jour

# Mettre à jour une release
helm upgrade <nom-release> <chart>

# Exemples
helm upgrade my-nginx bitnami/nginx
helm upgrade my-nginx bitnami/nginx --set replicaCount=5
helm upgrade my-nginx bitnami/nginx -f values-prod.yaml

# Upgrade avec installation si n'existe pas
helm upgrade --install my-app bitnami/nginx

# Force le remplacement
helm upgrade my-app bitnami/nginx --force

# Réutiliser les valeurs précédentes
helm upgrade my-app bitnami/nginx --reuse-values

# Voir les changements avant d'appliquer
helm upgrade my-app bitnami/nginx --dry-run --debug

Rollback

# Rollback à la version précédente
helm rollback <nom-release>

# Rollback à une version spécifique
helm rollback <nom-release> <revision>

# Exemples
helm rollback my-nginx 1
helm rollback my-nginx 3

# Dry-run du rollback
helm rollback my-nginx 2 --dry-run

Désinstallation

# Désinstaller une release
helm uninstall <nom-release>

# Exemples
helm uninstall my-nginx
helm uninstall my-app -n production

# Garder l'historique (pour rollback ultérieur)
helm uninstall my-nginx --keep-history

# Supprimer avec un timeout
helm uninstall my-nginx --timeout 5m

📚 Repositories

Gestion des Repositories

# Ajouter un repository
helm repo add <nom> <url>

# Repositories populaires
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add stable https://charts.helm.sh/stable
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo add grafana https://grafana.github.io/helm-charts
helm repo add jetstack https://charts.jetstack.io
helm repo add elastic https://helm.elastic.co

# Lister les repositories
helm repo list

# Mettre à jour les repositories
helm repo update

# Supprimer un repository
helm repo remove <nom>

# Chercher dans un repository
helm search repo <nom-repo>/<chart>

Repositories Populaires

# Bitnami - Applications populaires
helm repo add bitnami https://charts.bitnami.com/bitnami
# Charts: WordPress, MySQL, PostgreSQL, Redis, MongoDB, Nginx, Apache

# Ingress NGINX
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
# Charts: ingress-nginx

# Prometheus & Grafana
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo add grafana https://grafana.github.io/helm-charts
# Charts: kube-prometheus-stack, prometheus, alertmanager, grafana

# Cert-Manager (Let's Encrypt)
helm repo add jetstack https://charts.jetstack.io
# Charts: cert-manager

# Elastic Stack
helm repo add elastic https://helm.elastic.co
# Charts: elasticsearch, kibana, filebeat, metricbeat

# HashiCorp
helm repo add hashicorp https://helm.releases.hashicorp.com
# Charts: vault, consul

# GitLab
helm repo add gitlab https://charts.gitlab.io
# Charts: gitlab, gitlab-runner

# Mettre à jour après ajout
helm repo update

Artifact Hub

Artifact Hub est le registre central des Charts Helm.

# Rechercher sur Artifact Hub
helm search hub wordpress
helm search hub nginx
helm search hub postgresql

# Voir les détails
# Ouvrir https://artifacthub.io/

🔨 Créer un Chart

Créer un Chart depuis zéro

# Créer la structure d'un chart
helm create mon-app

# Structure générée
mon-app/
├── Chart.yaml
├── values.yaml
├── charts/
├── templates/
│   ├── NOTES.txt
│   ├── _helpers.tpl
│   ├── deployment.yaml
│   ├── hpa.yaml
│   ├── ingress.yaml
│   ├── service.yaml
│   ├── serviceaccount.yaml
│   └── tests/
│       └── test-connection.yaml
└── .helmignore

Fichier Chart.yaml

# Chart.yaml
apiVersion: v2
name: mon-app
description: Une application Node.js
type: application
version: 1.0.0        # Version du chart
appVersion: "2.5.0"   # Version de l'application

keywords:
  - nodejs
  - web
  - api

home: https://github.com/user/mon-app
sources:
  - https://github.com/user/mon-app

maintainers:
  - name: Votre Nom
    email: [email protected]
    url: https://votresite.com

icon: https://example.com/icon.png

dependencies:
  - name: postgresql
    version: "12.x.x"
    repository: https://charts.bitnami.com/bitnami
    condition: postgresql.enabled
  - name: redis
    version: "17.x.x"
    repository: https://charts.bitnami.com/bitnami
    condition: redis.enabled

Fichier values.yaml

# values.yaml - Configuration par défaut

# Nombre de réplicas
replicaCount: 3

# Configuration de l'image
image:
  repository: myapp/nodejs-app
  pullPolicy: IfNotPresent
  tag: "2.5.0"

# Pull secrets pour registries privés
imagePullSecrets: []

# Nom personnalisé
nameOverride: ""
fullnameOverride: ""

# Service Account
serviceAccount:
  create: true
  annotations: {}
  name: ""

# Annotations des pods
podAnnotations: {}

# Labels des pods
podLabels: {}

# Security Context
podSecurityContext:
  fsGroup: 2000

securityContext:
  capabilities:
    drop:
    - ALL
  readOnlyRootFilesystem: true
  runAsNonRoot: true
  runAsUser: 1000

# Service
service:
  type: ClusterIP
  port: 80
  targetPort: 3000

# Ingress
ingress:
  enabled: false
  className: "nginx"
  annotations: {}
    # cert-manager.io/cluster-issuer: letsencrypt-prod
  hosts:
    - host: app.example.com
      paths:
        - path: /
          pathType: Prefix
  tls: []
    # - secretName: app-tls
    #   hosts:
    #     - app.example.com

# Ressources
resources:
  limits:
    cpu: 500m
    memory: 512Mi
  requests:
    cpu: 250m
    memory: 256Mi

# Autoscaling
autoscaling:
  enabled: false
  minReplicas: 2
  maxReplicas: 10
  targetCPUUtilizationPercentage: 80
  targetMemoryUtilizationPercentage: 80

# Volumes
volumes: []
volumeMounts: []

# Node selector
nodeSelector: {}

# Tolerations
tolerations: []

# Affinity
affinity: {}

# Variables d'environnement
env:
  - name: NODE_ENV
    value: "production"
  - name: PORT
    value: "3000"

# ConfigMaps
configMaps: {}

# Secrets
secrets: {}

# Health checks
livenessProbe:
  httpGet:
    path: /health
    port: 3000
  initialDelaySeconds: 30
  periodSeconds: 10

readinessProbe:
  httpGet:
    path: /ready
    port: 3000
  initialDelaySeconds: 5
  periodSeconds: 5

# Dépendances
postgresql:
  enabled: true
  auth:
    username: myapp
    password: changeme
    database: myapp_db

redis:
  enabled: true
  auth:
    enabled: false

Template Deployment

# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "mon-app.fullname" . }}
  labels:
    {{- include "mon-app.labels" . | nindent 4 }}
spec:
  {{- if not .Values.autoscaling.enabled }}
  replicas: {{ .Values.replicaCount }}
  {{- end }}
  selector:
    matchLabels:
      {{- include "mon-app.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      annotations:
        checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
      {{- with .Values.podAnnotations }}
        {{- toYaml . | nindent 8 }}
      {{- end }}
      labels:
        {{- include "mon-app.labels" . | nindent 8 }}
        {{- with .Values.podLabels }}
        {{- toYaml . | nindent 8 }}
        {{- end }}
    spec:
      {{- with .Values.imagePullSecrets }}
      imagePullSecrets:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      serviceAccountName: {{ include "mon-app.serviceAccountName" . }}
      securityContext:
        {{- toYaml .Values.podSecurityContext | nindent 8 }}
      containers:
      - name: {{ .Chart.Name }}
        securityContext:
          {{- toYaml .Values.securityContext | nindent 12 }}
        image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
        imagePullPolicy: {{ .Values.image.pullPolicy }}
        ports:
        - name: http
          containerPort: {{ .Values.service.targetPort }}
          protocol: TCP
        env:
        {{- range .Values.env }}
        - name: {{ .name }}
          value: {{ .value | quote }}
        {{- end }}
        livenessProbe:
          {{- toYaml .Values.livenessProbe | nindent 12 }}
        readinessProbe:
          {{- toYaml .Values.readinessProbe | nindent 12 }}
        resources:
          {{- toYaml .Values.resources | nindent 12 }}
        {{- with .Values.volumeMounts }}
        volumeMounts:
          {{- toYaml . | nindent 12 }}
        {{- end }}
      {{- with .Values.volumes }}
      volumes:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      {{- with .Values.nodeSelector }}
      nodeSelector:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      {{- with .Values.affinity }}
      affinity:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      {{- with .Values.tolerations }}
      tolerations:
        {{- toYaml . | nindent 8 }}
      {{- end }}

Helpers (_helpers.tpl)

{{/*
Expand the name of the chart.
*/}}
{{- define "mon-app.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Create a default fully qualified app name.
*/}}
{{- define "mon-app.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}

{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "mon-app.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Common labels
*/}}
{{- define "mon-app.labels" -}}
helm.sh/chart: {{ include "mon-app.chart" . }}
{{ include "mon-app.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}

{{/*
Selector labels
*/}}
{{- define "mon-app.selectorLabels" -}}
app.kubernetes.io/name: {{ include "mon-app.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

{{/*
Create the name of the service account to use
*/}}
{{- define "mon-app.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "mon-app.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}

📝 Templating

Fonctions de Base

# Valeurs
{{ .Values.replicaCount }}
{{ .Values.image.repository }}
{{ .Values.image.tag }}

# Release
{{ .Release.Name }}
{{ .Release.Namespace }}
{{ .Release.Service }}

# Chart
{{ .Chart.Name }}
{{ .Chart.Version }}
{{ .Chart.AppVersion }}

# Kubernetes
{{ .Capabilities.KubeVersion }}

# Fichiers
{{ .Files.Get "config.txt" }}
{{ .Template.BasePath }}

Fonctions de Transformation

# Quotes
value: {{ .Values.env | quote }}

# Upper/Lower
name: {{ .Values.name | upper }}
name: {{ .Values.name | lower }}

# Default
replicas: {{ .Values.replicaCount | default 3 }}

# ToString
port: {{ .Values.port | toString }}

# Truncate
name: {{ .Values.name | trunc 63 | trimSuffix "-" }}

# Replace
image: {{ .Values.image.name | replace ":" "-" }}

# Indent / nindent
labels:
  {{- toYaml .Values.labels | nindent 4 }}

Conditions

{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
# ...
{{- end }}

{{- if and .Values.postgresql.enabled .Values.redis.enabled }}
# Les deux activés
{{- end }}

{{- if or .Values.service.type "ClusterIP" }}
# Service type est ClusterIP ou non défini
{{- end }}

{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}

Boucles

# Range sur une liste
{{- range .Values.env }}
- name: {{ .name }}
  value: {{ .value | quote }}
{{- end }}

# Range sur un dictionnaire
{{- range $key, $value := .Values.annotations }}
{{ $key }}: {{ $value | quote }}
{{- end }}

# Range avec index
{{- range $index, $item := .Values.items }}
- index: {{ $index }}
  value: {{ $item }}
{{- end }}

With (Scope)

{{- with .Values.ingress }}
  {{- if .enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: {{ .name }}
  annotations:
    {{- toYaml .annotations | nindent 4 }}
  {{- end }}
{{- end }}

⚙️ Values et Configuration

Hiérarchie des Values

# 1. values.yaml du chart (défauts)
# 2. values.yaml des charts parents
# 3. Fichier -f custom-values.yaml
# 4. Valeurs --set sur la ligne de commande

# Exemple de priorité
helm install my-app ./chart \
  -f values-prod.yaml \          # Priorité 3
  --set replicaCount=5 \          # Priorité 4 (override tout)
  --set image.tag=v2.0.0

Fichiers de Values par Environnement

# Structure
mon-chart/
├── values.yaml              # Défauts
├── values-dev.yaml          # Dev
├── values-staging.yaml      # Staging
└── values-production.yaml   # Production
# values-dev.yaml
replicaCount: 1
image:
  tag: "latest"
  pullPolicy: Always
ingress:
  enabled: false
resources:
  requests:
    cpu: 100m
    memory: 128Mi
# values-production.yaml
replicaCount: 5
image:
  tag: "2.5.0"
  pullPolicy: IfNotPresent
ingress:
  enabled: true
  hosts:
    - host: app.production.com
resources:
  requests:
    cpu: 500m
    memory: 512Mi
  limits:
    cpu: 1000m
    memory: 1Gi
# Déployer en dev
helm install my-app ./mon-chart -f values-dev.yaml

# Déployer en production
helm install my-app ./mon-chart -f values-production.yaml

Overrides avec --set

# Valeur simple
--set replicaCount=3

# Valeur imbriquée
--set image.tag=v2.0.0
--set image.repository=myregistry/myapp

# Plusieurs valeurs
helm install my-app ./chart \
  --set replicaCount=5 \
  --set image.tag=v2.0.0 \
  --set service.type=LoadBalancer

# Listes
--set  env[0].name=NODE_ENV,env[0].value=production

# Depuis un fichier
--set-file config=/path/to/config.txt

# Depuis JSON
--set-json 'labels={"env":"prod","team":"backend"}'

🪝 Hooks

Les Hooks permettent d'exécuter des actions à des moments spécifiques du cycle de vie d'une release.

Types de Hooks

# templates/pre-install-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: {{ include "mon-app.fullname" . }}-pre-install
  annotations:
    "helm.sh/hook": pre-install
    "helm.sh/hook-weight": "-5"
    "helm.sh/hook-delete-policy": hook-succeeded
spec:
  template:
    spec:
      containers:
      - name: pre-install
        image: busybox
        command: ['sh', '-c', 'echo "Pre-install hook"']
      restartPolicy: Never

Hooks disponibles

# Installation
helm.sh/hook: pre-install      # Avant l'installation
helm.sh/hook: post-install     # Après l'installation

# Upgrade
helm.sh/hook: pre-upgrade      # Avant la mise à jour
helm.sh/hook: post-upgrade     # Après la mise à jour

# Rollback
helm.sh/hook: pre-rollback     # Avant le rollback
helm.sh/hook: post-rollback    # Après le rollback

# Suppression
helm.sh/hook: pre-delete       # Avant la suppression
helm.sh/hook: post-delete      # Après la suppression

# Tests
helm.sh/hook: test             # Lors de `helm test`

Hook Weight (Ordre d'exécution)

annotations:
  "helm.sh/hook-weight": "-5"   # Exécuté en premier
  "helm.sh/hook-weight": "0"    # Par défaut
  "helm.sh/hook-weight": "5"    # Exécuté en dernier

Hook Delete Policy

annotations:
  # Supprimer après succès
  "helm.sh/hook-delete-policy": hook-succeeded
  
  # Supprimer après échec
  "helm.sh/hook-delete-policy": hook-failed
  
  # Supprimer avant lancement d'un nouveau hook
  "helm.sh/hook-delete-policy": before-hook-creation