infra / kubernetes
Health probes : liveness, readiness, startup
Explication
Ce que vous allez apprendre
- Comprendre la limite du critère par défaut de Kubernetes ("le processus n'a pas planté")
- Distinguer précisément la liveness probe (redémarrer) de la readiness probe (débrancher du trafic)
- Utiliser une startup probe pour protéger le démarrage lent d'une application
- Reconnaître l'erreur classique qui provoque des redémarrages en boucle inutiles
- Comprendre les conséquences d'une readiness probe absente sur les premiers utilisateurs
Dans quel contexte ?
Une API Java met 45 secondes à démarrer, le temps de charger son cache en mémoire. Avec une livenessProbe classique de 10 secondes sans startupProbe, Kubernetes tue le conteneur avant même qu'il ait fini de démarrer, le recrée, qui remet 45 secondes à démarrer, qui se fait tuer à nouveau : le Pod reste bloqué en CrashLoopBackOff indéfiniment alors que l'application n'a jamais eu de vrai problème. Cette leçon explique comment distinguer les trois types de probes pour éviter ce genre de boucle absurde et détecter les vraies pannes.
Ce que Kubernetes considère "aller bien" par défaut
D'abord, un fait à connaître : par défaut, Kubernetes considère qu'un Pod va bien tant que son processus principal n'a pas planté.
Ce que ce critère ne détecte pas
Mais une application peut très bien tourner sans planter tout en étant complètement bloquée, comme un deadlock ou une connexion réseau figée, ou en train de démarrer sans être encore prête à recevoir du trafic.
La solution : poser régulièrement une question
Sans mécanisme dédié, Kubernetes ne peut rien détecter de tout cela. C'est le rôle des probes (sondes), qui posent régulièrement une question simple à l'application, "es-tu vivante ?", "es-tu prête ?", et réagissent selon la réponse.
La distinction la plus importante de cette leçon
Il faut absolument distinguer deux probes qui se ressemblent mais ont des conséquences opposées. La liveness probe répond à "dois-je redémarrer ce conteneur ?" : en cas d'échec répété, Kubernetes tue et recrée le conteneur.
| Probe | Question posée | Conséquence en cas d'échec |
|---|---|---|
livenessProbe | "Dois-je redémarrer ce conteneur ?" | Le conteneur est tué et recréé |
readinessProbe | "Dois-je lui envoyer du trafic ?" | Le Pod est retiré du Service, sans redémarrage |
startupProbe | "A-t-il fini de démarrer ?" | Les autres probes sont mises en pause |
L'autre probe, aux conséquences différentes
La readiness probe répond à "dois-je envoyer du trafic vers ce Pod ?" : en cas d'échec, le Pod est simplement retiré de la liste des cibles du Service, sans redémarrage. Il reste vivant, juste débranché temporairement du trafic.
Piège fréquent
Mettre une logique de "readiness" (comme vérifier la connexion à la base de données) dans une livenessProbe est l'erreur numéro un des débutants : la moindre coupure réseau temporaire vers la base provoque alors un redémarrage complet du conteneur, alors qu'un simple retrait temporaire du trafic (readiness) aurait suffi.
L'erreur numéro un des débutants
Confondre les deux est l'erreur la plus fréquente : mettre une logique de "readiness" dans une liveness probe peut provoquer des redémarrages en boucle totalement inutiles.
Le rôle particulier de la startup probe
Certaines applications, comme une grosse base de données ou un service Java avec un long temps de chargement, mettent du temps à démarrer. Sans startupProbe, la livenessProbe pourrait la tuer avant même qu'elle ait fini de démarrer.
Bonne pratique
Pour toute application dont le démarrage dépasse quelques secondes, ajoute systématiquement une startupProbe avec un nombre de tentatives généreux. C'est la protection la plus simple et la plus efficace contre les CrashLoopBackOff causés par un démarrage lent plutôt qu'une vraie panne.
Ce que fait concrètement la startup probe
La startupProbe met en pause les autres probes tant que l'application démarre, évitant ce redémarrage prématuré et absurde.
Le danger d'une readiness absente
Enfin, sans readinessProbe définie, un Pod tout juste créé est considéré prêt immédiatement, et reçoit du trafic dès sa création, même s'il n'a pas fini de se connecter à sa base de données. Résultat : des erreurs pour les premiers utilisateurs qui tombent sur ce Pod.
Maintenant que Kubernetes sait détecter un Pod défaillant, la leçon suivante s'appuie directement là-dessus pour déployer une nouvelle version sans jamais couper le service.
Commandes & code
Liveness, Readiness, Startup probes
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-deployment
spec:
replicas: 3
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: myorg/api:1.4.2
ports:
- containerPort: 3000
# startupProbe : laisse le temps à une app lente à démarrer avant d'activer les autres probes
startupProbe:
httpGet:
path: /health/startup
port: 3000
failureThreshold: 30 # 30 x 2s = 60s de marge avant d'être considéré comme bloqué
periodSeconds: 2
# livenessProbe : redémarre le conteneur s'il ne répond plus (deadlock, fuite mémoire...)
livenessProbe:
httpGet:
path: /health/live
port: 3000
initialDelaySeconds: 0 # startupProbe a déjà géré le délai initial
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
# readinessProbe : retire le Pod des Endpoints du Service s'il n'est pas prêt à servir
readinessProbe:
httpGet:
path: /health/ready
port: 3000
periodSeconds: 5
timeoutSeconds: 2
failureThreshold: 2# Variante avec une commande exécutée dans le conteneur plutôt qu'un endpoint HTTP
livenessProbe:
exec:
command: ["pg_isready", "-U", "postgres"]
periodSeconds: 10
# Variante TCP simple (juste vérifier qu'un port répond)
readinessProbe:
tcpSocket:
port: 6379
periodSeconds: 5kubectl describe pod <pod-name> # section "Conditions" et "Events" montre les échecs de probes
kubectl get pods -o wide # colonne READY : x/y conteneurs prêts
# Simuler une panne pour observer le comportement
kubectl exec <pod-name> -- kill 1 # (selon l'image) déclenche un redémarrage via liveness| Probe | Échec -> conséquence | But |
|---|---|---|
startupProbe | Bloque les autres probes tant qu'elle échoue | Laisser du temps au démarrage lent |
livenessProbe | Redémarre le conteneur | Détecter un process bloqué/deadlock |
readinessProbe | Retire le Pod des Endpoints (pas de redémarrage) | Ne recevoir du trafic que si vraiment prêt |
Résumé
- Confondre
livenessetreadinessest l'erreur n°1 : liveness = tue, readiness = débranche du trafic. startupProbeévite qu'une app lente à démarrer soit tuée en boucle par la liveness.- Toujours définir un
failureThresholdetperiodSecondsréalistes pour éviter le flapping. - Une
readinessProbeabsente = le Pod reçoit du trafic dès sa création, même pas prêt.
Exercices pratiques
Mission : sortir une API Java du CrashLoopBackOff
Objectif : Configurer les trois probes correctement pour une application à démarrage lent, en évitant de confondre liveness et readiness.
Contexte
Une API Java met 45 secondes à démarrer, le temps de charger son cache en mémoire. Elle est actuellement configurée avec seulement une livenessProbe HTTP toutes les 10 secondes, failureThreshold: 3. Le Pod reste bloqué en CrashLoopBackOff en boucle : Kubernetes le tue avant même qu'il ait fini de démarrer, le recrée, et recommence indéfiniment.