TP Helm bis : découvertes sympathiques avec podinfo
Durée estimée : 1 h à 1 h 30 Niveau : débutant, après le TP « Helm : déployer Supabase » Prérequis : minikube, kubectl et Helm installés
Objectifs
Ce petit TP est plus léger que le précédent. On y découvre, de façon visuelle et rapide :
- une application de démonstration qu'on personnalise en direct avec des values ;
- la répartition de charge et l'auto-réparation vues depuis le navigateur ;
- les tests Helm qui réussissent... puis qui échouent ;
- le retour arrière automatique d'une mise à jour ratée (
--atomic) ; - son propre chart en 10 minutes, avec un message d'accueil en ASCII art et un mot de passe généré qui survit aux mises à jour.
L'application support est podinfo, une petite application web écrite pour les démonstrations Kubernetes. Elle affiche le nom du Pod qui répond, et on peut lui demander de tomber en panne, de ralentir ou de ne plus être prête. Idéal pour observer Kubernetes réagir.
Sommaire
- Partie 0 : Préparation
- Partie 1 : Installer en une commande
- Partie 2 : Personnaliser en direct
- Partie 3 : Jouer avec les pannes
- Partie 4 : Les tests Helm
- Partie 5 : La mise à jour qui s'annule toute seule
- Partie 6 : Son propre chart en 10 minutes
- Partie 7 : Nettoyage
- Annexe : Corrigés
Partie 0 : Préparation
Un petit cluster suffit. On publie le NodePort 30990 sur votre machine pour avoir une adresse stable sous Linux, macOS et Windows :
minikube start -p helm-fun --driver=docker --cpus=2 --memory=2048 --ports=30990:30990
kubectl create namespace fun
kubectl config set-context --current --namespace=fun
mkdir -p ~/tp-helm-fun && cd ~/tp-helm-fun
Ajoutez le dépôt de podinfo :
helm repo add podinfo https://stefanprodan.github.io/podinfo
helm repo update
helm search repo podinfo
Ce TP a été écrit avec la version 6.15.0 du chart. On la fixe dans toutes les commandes.
Partie 1 : Installer en une commande
Installez podinfo, exposé en NodePort, avec un message personnalisé, sans aucun fichier :
helm install fun podinfo/podinfo --version 6.15.0 -n fun \
--set service.type=NodePort \
--set service.nodePort=30990 \
--set ui.message="Bonjour depuis Helm"
kubectl get pods -w # Ctrl+C quand le Pod est Running
Ouvrez http://localhost:30990 : votre message s'affiche, avec le nom du Pod et la version de l'application.
À comparer avec le TP Supabase : dans le chart Supabase, impossible de fixer lenodePortcar le template ne l'exposait pas. Le chart podinfo, lui, le prévoit. Vérifiez-le :
La qualité d'un chart se juge aussi à ce qu'il permet de configurer.
Interrogez la même application en ligne de commande : podinfo répond en JSON quand ce n'est pas un navigateur qui l'appelle.
curl -s http://localhost:30990
Partie 2 : Personnaliser en direct
2.1 Passer à un fichier de values
Les --set deviennent vite illisibles. Créez values-fun.yaml :
replicaCount: 3
service:
type: NodePort
nodePort: 30990
ui:
color: "#8e44ad" # couleur de fond, au format hexadécimal
message: "Formation Kubernetes : tout va bien"
Appliquez avec upgrade --install, qui installe si la release n'existe pas et met à jour sinon. C'est la commande qu'on utilise dans les scripts et les pipelines CI/CD, car elle fonctionne dans les deux cas :
helm upgrade --install fun podinfo/podinfo --version 6.15.0 -n fun -f values-fun.yaml
kubectl get pods
Rechargez la page : la couleur et le message ont changé.
À vous : choisissez votre propre couleur et votre propre message, puis réappliquez. Quelques couleurs pour essayer : #e67e22, #16a085, #c0392b, #2c3e50.
2.2 Voir la répartition de charge
Avec 3 réplicas, chaque requête peut être servie par un Pod différent :
for i in $(seq 1 10); do curl -s http://localhost:30990 | grep hostname; done
Les noms de Pods alternent : le Service répartit les requêtes.
Dans le navigateur, le nom change moins souvent : le navigateur réutilise la même connexion TCP, qui reste attachée au même Pod. Ouvrez une fenêtre de navigation privée pour forcer une nouvelle connexion.
2.3 L'historique en un coup d'œil
helm history fun -n fun
helm get values fun -n fun
✅ Point de contrôle 2
- Quelle différence entre
helm installethelm upgrade --install? - Pourquoi les noms de Pods alternent-ils avec
curl?
Partie 3 : Jouer avec les pannes
podinfo expose des adresses spéciales pour simuler des incidents. Gardez un second terminal ouvert qui surveille les Pods :
kubectl get pods -w
3.1 Faire planter un Pod
curl -s http://localhost:30990/panic
Dans le terminal de surveillance, un Pod passe en Error puis redémarre : la colonne RESTARTS augmente. Le kubelet a relancé le conteneur. Si vous rechargez la page, le site répond toujours grâce aux deux autres Pods.
3.2 Retirer un Pod du Service sans le tuer
Demandez à un Pod de se déclarer « pas prêt » :
curl -s -X POST http://localhost:30990/readyz/disable
kubectl get pods
Un Pod affiche 0/1 dans la colonne READY, mais il est toujours Running. Vérifiez qu'il ne reçoit plus de trafic :
kubectl get endpointslices -l kubernetes.io/service-name=fun-podinfo -o jsonpath='{range .items[*].endpoints[*]}{.targetRef.name}{" ready="}{.conditions.ready}{"\n"}{end}'
for i in $(seq 1 10); do curl -s http://localhost:30990 | grep hostname; done
Le Pod « pas prêt » n'apparaît plus dans les réponses. C'est le rôle de la readinessProbe : un Pod qui démarre, qui est surchargé ou en maintenance est retiré de la répartition de charge sans être redémarré.
Différence entre les deux sondes :
Remettez le Pod en service. Comme on ne sait pas quel Pod recevra la requête, le plus simple est de le supprimer : le Deployment en recrée un neuf.
kubectl delete pod $(kubectl get pods -l app.kubernetes.io/name=fun-podinfo -o jsonpath='{range .items[?(@.status.containerStatuses[0].ready==false)]}{.metadata.name}{" "}{end}')
kubectl get pods
3.3 Quelques autres adresses amusantes
curl -s http://localhost:30990/version # version de l'application
curl -s http://localhost:30990/env | head -n 20 # variables d'environnement du Pod
curl -s http://localhost:30990/headers # en-têtes HTTP reçus
time curl -s http://localhost:30990/delay/3 > /dev/null # réponse volontairement lente
curl -si http://localhost:30990/status/418 | head -n 1 # renvoie le code HTTP demandé
Dans /env, repérez les variables KUBERNETES_SERVICE_HOST et FUN_PODINFO_SERVICE_PORT : Kubernetes injecte automatiquement dans chaque Pod l'adresse des Services du namespace.
Partie 4 : Les tests Helm
Le chart podinfo contient des tests. Lancez-les :
helm test fun -n fun
Chaque test affiche Succeeded. Un test Helm est un Pod lancé à la demande, qui vérifie que l'application répond correctement.
Voyons un test échouer. Le chart permet de simuler cet échec :
helm upgrade fun podinfo/podinfo --version 6.15.0 -n fun -f values-fun.yaml \
--set faults.testFail=true
helm test fun -n fun
Au moins un test est en Failed. Consultez ses logs :
helm test fun -n fun --logs 2>&1 | tail -n 20
Revenez à l'état normal :
helm upgrade fun podinfo/podinfo --version 6.15.0 -n fun -f values-fun.yaml
helm test fun -n fun
Utilité : dans une chaîne CI/CD, on enchaînehelm upgradepuishelm test. Si un test échoue, la chaîne s'arrête et alerte l'équipe avant que les utilisateurs ne s'en aperçoivent.
Partie 5 : La mise à jour qui s'annule toute seule
5.1 Une mise à jour ratée, sans filet
Simulons une nouvelle version qui ne devient jamais prête :
helm upgrade fun podinfo/podinfo --version 6.15.0 -n fun -f values-fun.yaml \
--set faults.unready=true
helm list -n fun
kubectl get pods
Helm annonce deployed alors que les nouveaux Pods restent à 0/1 ! Par défaut, Helm n'attend pas que l'application fonctionne : il envoie les manifestes à Kubernetes et rend la main. Le site reste accessible grâce aux anciens Pods que Kubernetes garde en vie, mais la mise à jour est bloquée à moitié.
Réparez à la main :
helm rollback fun -n fun
kubectl get pods
Sans numéro de révision, helm rollback revient à la révision précédente.5.2 La même, avec filet
Relancez la même mise à jour ratée avec deux options :
--wait: attendre que les Pods soient prêts ;--atomic: si ce n'est pas le cas avant le--timeout, revenir automatiquement en arrière.
helm upgrade fun podinfo/podinfo --version 6.15.0 -n fun -f values-fun.yaml \
--set faults.unready=true --atomic --timeout 60s
Patientez une minute. Helm affiche une erreur puis annule lui-même la mise à jour :
helm history fun -n fun
kubectl get pods
L'historique contient une révision failed suivie d'un Rollback to ..., et les Pods sont revenus à l'état sain.
--atomicimplique--wait. Avec Helm 4, cette option s'appelle--rollback-on-failure.
Ce que--atomicne sait pas faire : annuler une modification de données (schéma de base de données, fichiers). Il ne revient que sur les manifestes Kubernetes.
✅ Point de contrôle 5
- Pourquoi Helm affichait-il
deployedalors que les Pods n'étaient pas prêts ? - Que fait
--atomicexactement ? - Pourquoi le site est-il resté accessible pendant la mise à jour ratée ?
Partie 6 : Son propre chart en 10 minutes
6.1 Générer un chart
helm create coucou
find coucou -type f | sort
helm create produit un chart complet et fonctionnel (Deployment nginx, Service, Ingress désactivé, tests...). C'est le meilleur point de départ pour écrire ses propres charts.
6.2 Un message d'accueil personnalisé
Remplacez le contenu de coucou/templates/NOTES.txt par :
____
/ ___|___ _ _ ___ ___ _ _
| | / _ \| | | |/ __/ _ \| | | |
| |__| (_) | |_| | (_| (_) | |_| |
\____\___/ \__,_|\___\___/ \__,_|
Release : {{ .Release.Name }} (révision {{ .Release.Revision }})
Namespace : {{ .Release.Namespace }}
Chart : {{ .Chart.Name }} {{ .Chart.Version }}
Déployé le : {{ now | date "02/01/2006 à 15:04" }}
Réplicas : {{ .Values.replicaCount }}
Mot de passe généré :
kubectl get secret {{ include "coucou.fullname" . }}-mdp -n {{ .Release.Namespace }} -o jsonpath='{.data.password}' | base64 -d; echo
Objets disponibles dans les templates :now | date "..."est un pipeline : la sortie denowest passée à la fonctiondate. Helm fournit plus de 70 fonctions (bibliothèque Sprig) :upper,b64enc,randAlphaNum,sha256sum,default...
6.3 Un mot de passe généré automatiquement
Créez coucou/templates/secret.yaml :
{{- $nom := printf "%s-mdp" (include "coucou.fullname" .) }}
apiVersion: v1
kind: Secret
metadata:
name: {{ $nom }}
type: Opaque
data:
password: {{ randAlphaNum 20 | b64enc }}
Installez :
helm lint coucou
helm install coucou ./coucou -n fun
Le message d'accueil s'affiche. Récupérez le mot de passe avec la commande indiquée, notez-le, puis faites une mise à jour anodine :
kubectl get secret coucou-mdp -o jsonpath='{.data.password}' | base64 -d; echo
helm upgrade coucou ./coucou -n fun --set replicaCount=2
kubectl get secret coucou-mdp -o jsonpath='{.data.password}' | base64 -d; echo
Le mot de passe a changé ! randAlphaNum est exécuté à chaque rendu du template, donc à chaque upgrade. Pour une base de données, ce serait une panne.
6.4 Garder le mot de passe existant avec lookup
La fonction lookup permet à un template d'interroger le cluster pendant l'installation. On l'utilise pour réutiliser le mot de passe s'il existe déjà. Remplacez coucou/templates/secret.yaml par :
{{- $nom := printf "%s-mdp" (include "coucou.fullname" .) }}
{{- $existant := lookup "v1" "Secret" .Release.Namespace $nom }}
apiVersion: v1
kind: Secret
metadata:
name: {{ $nom }}
type: Opaque
data:
{{- if $existant }}
password: {{ index $existant.data "password" }}
{{- else }}
password: {{ randAlphaNum 20 | b64enc }}
{{- end }}
helm upgrade coucou ./coucou -n fun --set replicaCount=3
kubectl get secret coucou-mdp -o jsonpath='{.data.password}' | base64 -d; echo
helm upgrade coucou ./coucou -n fun --set replicaCount=1
kubectl get secret coucou-mdp -o jsonpath='{.data.password}' | base64 -d; echo
Le mot de passe ne change plus.
Limite delookup:helm templateet--dry-runsans accès au cluster ne peuvent rien interroger.lookupy renvoie un résultat vide : le rendu affichera donc toujours un nouveau mot de passe, ce qui ne reflète pas ce que ferait la vraie mise à jour. Vérifiez :
6.5 Petits défis (facultatifs)
- Ajoutez dans
values.yamlune cléauteuret affichez-la en majuscules dans leNOTES.txt(fonctionupper). - Affichez dans le
NOTES.txtla version de Kubernetes du cluster (objet.Capabilities). - N'affichez la ligne « Mot de passe généré » qu'à la première installation (indice :
.Release.IsInstall). - Empaquetez votre chart dans une archive
.tgzavechelm package coucou. Où pourriez-vous la publier ?
Partie 7 : Nettoyage
helm uninstall fun coucou -n fun
kubectl delete namespace fun
minikube delete -p helm-fun
kubectl config use-context minikube
Annexe : Corrigés
Point de contrôle 2
helm installéchoue si la release existe déjà.helm upgrade --installinstalle la release si elle n'existe pas et la met à jour sinon : la même commande fonctionne à chaque exécution.curlouvre une nouvelle connexion à chaque appel, et le Service répartit les connexions entre les Pods prêts.
Point de contrôle 5
- Sans
--wait, Helm considère la mise à jour réussie dès que Kubernetes a accepté les manifestes, sans attendre que les Pods soient prêts. - Il attend que les ressources soient prêtes. Si le délai
--timeoutest dépassé, il marque la révision en échec et revient automatiquement à la dernière révision réussie. - La stratégie
RollingUpdatene supprime les anciens Pods que lorsque les nouveaux sont prêts. Les nouveaux ne l'étant jamais, les anciens ont continué à servir le trafic.
Défis 6.5
1. Dans coucou/values.yaml :
auteur: "votre prénom"
Dans NOTES.txt :
Auteur : {{ .Values.auteur | upper }}
2.
Kubernetes : {{ .Capabilities.KubeVersion.Version }}
3.
{{- if .Release.IsInstall }}
Mot de passe généré :
kubectl get secret {{ include "coucou.fullname" . }}-mdp -n {{ .Release.Namespace }} -o jsonpath='{.data.password}' | base64 -d; echo
{{- end }}
4.
helm package coucou
ls coucou-*.tgz
L'archive peut être publiée sur un serveur web statique avec un fichier index.yaml généré par helm repo index (c'est ainsi que fonctionne le dépôt podinfo, hébergé sur GitHub Pages), ou poussée dans un registre OCI (Harbor, GitLab, GitHub Container Registry) avec helm push.