Sondes et cycle de vie - ii02735/learn-k8 GitHub Wiki

Les sondes et le cycle de vie d'un Pod

Un Pod naît, démarre, travaille, puis meurt — et il est presque toujours remplacé plutôt que réparé. Comprendre son cycle de vie et savoir sonder ses containers, c'est savoir quand Kubernetes considère une application comme prête, et comment il réagit quand elle ne l'est plus.

Les phases d'un Pod et la vie d'un container : démarrage, sondes, arrêt gracieux

Le cycle de vie d'un Pod

Les phases (status.phase)

À tout moment, un Pod se trouve dans une phase :

  • Pending : le Pod est accepté par l'API, mais les containers ne sont pas encore tous démarrés (planification en cours, image en cours de téléchargement, volume pas encore prêt…).
  • Running : le Pod est planifié sur un noeud et au moins un container tourne (ou est en cours de démarrage).
  • Succeeded : tous les containers se sont terminés avec succès — typique d'un Job mené à bien.
  • Failed : au moins un container s'est terminé en erreur.
  • Unknown : l'état ne peut pas être déterminé (problème de communication avec le noeud).

Un Pod est Running longtemps avant d'être Ready : ces deux états ne disent pas la même chose.

Les conditions (status.conditions)

À côté des phases, chaque Pod expose des conditions — plus fines, elles décrivent sa progression :

  • PodScheduled : le Pod a été affecté à un noeud (le scheduler a fait son travail).
  • Initialized : tous les init containers ont réussi.
  • ContainersReady : tous les containers sont prêts (leurs readinessProbe passent).
  • Ready : le Pod peut servir du trafic — c'est la condition qui compte pour les Services.

[!NOTE] Un Pod n'est Ready que lorsque tous ses containers le sont. Un seul container dont la readinessProbe échoue suffit à retirer le Pod des endpoints d'un Service.

Les états d'un container (status.containerStatuses)

Chaque container possède son propre état :

  • Waiting : le container attend (téléchargement de l'image, attente d'un volume…). Le champ reason est parlant : ImagePullBackOff, CrashLoopBackOff…
  • Running : le container s'exécute.
  • Terminated : le container s'est arrêté, avec un exitCode et une reason (Completed, Error, OOMKilled…).

C'est là que se cachent la plupart des diagnostics : un Pod Running dont un container est en CrashLoopBackOff ne sert aucun trafic.

La politique de redémarrage (restartPolicy)

Elle définit si et quand un container est relancé :

  • Always (défaut) : le container est toujours redémarré. C'est le comportement attendu pour un service.
  • OnFailure : redémarré uniquement en cas d'échec — adapté aux Jobs.
  • Never : jamais redémarré.

:warning: Le restartPolicy s'applique au niveau du Pod, mais concerne le redémarrage des containers : le Pod lui-même n'est pas ressuscité, il reste sur le même noeud.

Les containers d'initialisation (init containers)

Un init container s'exécute avant les containers applicatifs. Il doit se terminer avec succès pour que le Pod poursuive son démarrage.

  • Ils tournent séquentiellement, dans l'ordre de déclaration.
  • Si l'un échoue, le Pod le relance (selon le restartPolicy) et ne passe jamais à l'étape suivante.
  • Ils ont leurs propres images et peuvent monter les mêmes volumes que les containers applicatifs.

Ils servent à préparer l'environnement : attendre qu'une dépendance soit disponible, télécharger des données, appliquer des permissions, initialiser un volume…

[!IMPORTANT] Un init container n'est pas un sidecar : il finit avant le démarrage de l'application, alors qu'un sidecar tourne en même temps que le container principal.

Les sondes (probes)

Les sondes sont des vérifications périodiques de l'état d'un container. Petite précision : elles se définissent au niveau de chaque container (spec.containers[].livenessProbe, etc.), et non au niveau du Pod. C'est le kubelet du noeud qui les exécute.

Trois sondes répondent à trois questions différentes :

  • livenessProbe (vitalité) : « le container est-il encore vivant ? ». Après plusieurs échecs, le kubelet redémarre le container. À réserver aux blocages (deadlock) qu'un redémarrage peut résoudre.
  • readinessProbe (disponibilité) : « le container est-il prêt à recevoir du trafic ? ». En cas d'échec, le Pod est retiré des endpoints des Services mais n'est pas redémarré. Pratique pendant un démarrage lent ou une surcharge temporaire.
  • startupProbe (démarrage) : pour les applications lentes à démarrer. Tant qu'elle n'a pas réussi, les sondes liveness et readiness sont désactivées, ce qui évite un redémarrage prématuré.

Comparaison entre livenessProbe et readinessProbe

Comment sonder un container ?

  • httpGet : une requête HTTP GET sur un path et un port ; un code 200 à 399 est un succès.
  • tcpSocket : tente d'ouvrir une connexion TCP sur un port.
  • exec : exécute une commande dans le container ; un code de sortie 0 est un succès.
  • grpc : appelle un endpoint gRPC de health checking.

Les paramètres de réglage

  • initialDelaySeconds : délai avant la première sonde (défaut : 0).
  • periodSeconds : intervalle entre deux sondes (défaut : 10).
  • timeoutSeconds : délai maximal d'une sonde (défaut : 1).
  • successThreshold : nombre de succès consécutifs pour repasser à Ready (défaut : 1 ; forcé à 1 pour liveness et startup).
  • failureThreshold : nombre d'échecs consécutifs avant de considérer la sonde en échec (défaut : 3).

Un manifeste complet

containers:
  - name: app
    image: nginx:1.27
    ports:
      - name: http
        containerPort: 80
    startupProbe:          # le temps du démarrage lent
      httpGet:
        path: /
        port: http
      failureThreshold: 30
      periodSeconds: 5
    livenessProbe:         # « es-tu encore vivant ? »
      httpGet:
        path: /healthz
        port: http
      periodSeconds: 10
    readinessProbe:        # « peux-tu recevoir du trafic ? »
      httpGet:
        path: /ready
        port: http
      periodSeconds: 5
      failureThreshold: 2

Les bonnes pratiques

  • Toujours définir une readinessProbe pour une application exposée par un Service.
  • Ne jamais mettre une dépendance externe (base de données, API tierce) dans une livenessProbe : une panne externe ferait redémarrer tous les Pods en boucle. Utiliser une readinessProbe à la place.
  • Utiliser une startupProbe pour les applications lentes, afin de réserver la liveness aux containers réellement bloqués.
  • Éviter les seuils trop agressifs (timeoutSeconds trop court, failureThreshold trop bas) qui provoquent des redémarrages inutiles.

:warning: Une livenessProbe mal réglée peut provoquer une boucle de redémarrage (CrashLoopBackOff).

Le démarrage et l'arrêt d'un container

Les hooks de cycle de vie

Deux hooks permettent d'exécuter du code à des moments précis :

  • postStart : lancé juste après la création du container. Attention, il n'est pas garanti de s'exécuter avant l'ENTRYPOINT — il ne faut pas en dépendre pour initialiser l'application.
  • preStop : appelé avant l'arrêt du container, pour un arrêt propre (vider les files, fermer les connexions, signaler à un load balancer…). Le kubelet attend la fin du hook.

L'arrêt gracieux

Quand un Pod est supprimé, Kubernetes orchestre un arrêt ordonné :

  1. Le Pod est retiré des endpoints des Services (il ne reçoit plus de nouveau trafic).
  2. Le hook preStop est exécuté, s'il existe.
  3. Le kubelet envoie SIGTERM au processus principal (PID 1).
  4. L'application dispose de terminationGracePeriodSeconds (défaut : 30 s) pour se terminer proprement.
  5. Passé ce délai, le kubelet envoie SIGKILL : le container est tué de force.

[!NOTE] Une application bien élevée doit intercepter SIGTERM et finir les requêtes en cours avant de s'arrêter. Un preStop qui attend quelques secondes est aussi un moyen simple de laisser le temps au load balancer de retirer le Pod.

Les commandes principales

  • Suivre l'évolution des Pods en direct : kubectl get pods -w
  • Voir les conditions, les états et les événements d'un Pod : kubectl describe pod app
  • Consulter les logs du container précédent (celui qui a planté) : kubectl logs app --previous
  • Ouvrir un shell dans un container : kubectl exec -it app -- sh
  • Simuler une suppression et observer l'arrêt gracieux : kubectl delete pod app
  • Lire la phase courante : kubectl get pod app -o jsonpath='{.status.phase}'
  • Filtrer les événements d'un Pod : kubectl get events --field-selector involvedObject.name=app

Pour aller plus loin

  • Les fondations sur les Pods (réseau, volumes, multi-container) : Les pods.
  • Le redémarrage automatique des Pods et leur remplacement : Les ReplicaSets.
  • L'exposition du trafic vers les Pods seulement lorsqu'ils sont Ready : Les Services.