Retour au cours

infra / kubernetes

Health probes : liveness, readiness, startup

Leçon 121 exercice

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.

ProbeQuestion poséeConsé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

yaml
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
yaml
# 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: 5
bash
kubectl 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équenceBut
startupProbeBloque les autres probes tant qu'elle échoueLaisser du temps au démarrage lent
livenessProbeRedémarre le conteneurDétecter un process bloqué/deadlock
readinessProbeRetire le Pod des Endpoints (pas de redémarrage)Ne recevoir du trafic que si vraiment prêt

Résumé

  • Confondre liveness et readiness est 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 failureThreshold et periodSeconds réalistes pour éviter le flapping.
  • Une readinessProbe absente = le Pod reçoit du trafic dès sa création, même pas prêt.

Exercices pratiques

1 disponible
1

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.

Résoudre l’exercice →