
Une application déployée sur Kubernetes n’est pas automatiquement hautement
disponible. Sans PDB, un kubectl drain peut rendre indisponibles
plusieurs pods d’une même application pendant une disruption volontaire,
sans garde-fou applicatif explicite. Un nœud défaillant peut emporter toutes
vos répliques, et des probes mal calibrées peuvent redémarrer des pods sains
sous charge. Ce guide couvre les quatre mécanismes à combiner pour garantir
la continuité de service : réplicas, PodDisruptionBudget, règles
de placement et probes de production.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Distinguer les disruptions volontaires et involontaires
- Protéger vos applications avec un PodDisruptionBudget (PDB)
- Répartir vos pods avec l’anti-affinité et les topology spread constraints
- Dimensionner vos probes pour la production (pas pour le développement)
- Combiner ces mécanismes dans une configuration de référence
Prérequis
Section intitulée « Prérequis »- Un cluster Kubernetes fonctionnel (v1.28+)
kubectlconfiguré avec des droits pour créer des déploiements et des PDB- Connaissances des Deployments et des Pods
- Avoir lu le guide Préparer une maintenance pour le contexte drain/cordon
Disruptions volontaires et involontaires
Section intitulée « Disruptions volontaires et involontaires »Kubernetes distingue deux types de disruptions qui affectent vos pods :
| Type | Exemples | Contrôlable ? |
|---|---|---|
| Involontaire | Panne de nœud, kernel panic, perte réseau, OOM kill | Non — seule la réplication protège |
| Volontaire | kubectl drain, suppression manuelle via éviction, opérations d’administration | Oui — les PDB limitent l’impact quand la disruption passe par l’API d’éviction |
Les PDB ne protègent que contre les disruptions volontaires. Pour les involontaires, la seule parade est d’avoir plusieurs répliques distribuées sur plusieurs nœuds (voire plusieurs zones).
Première couche : réplicas suffisants
Section intitulée « Première couche : réplicas suffisants »Avant toute configuration avancée, assurez-vous que votre Deployment (ou StatefulSet) dispose d’au moins 2 répliques en production. Un pod unique est un SPOF (Single Point of Failure) — aucun PDB, aucune probe ne peut compenser l’absence de réplication.
apiVersion: apps/v1kind: Deploymentmetadata: name: api-serverspec: replicas: 3 selector: matchLabels: app: api-server template: metadata: labels: app: api-server spec: containers: - name: api image: api-server:1.4.0 ports: - containerPort: 8080Trois répliques permettent de tolérer la perte d’un nœud tout en gardant deux pods actifs. Pour les applications stateful à quorum (etcd, ZooKeeper, Consul), visez un nombre impair (3 ou 5).
Deuxième couche : PodDisruptionBudget
Section intitulée « Deuxième couche : PodDisruptionBudget »Un PDB indique à Kubernetes combien de pods d’une application doivent rester disponibles (ou combien peuvent être indisponibles) pendant une disruption volontaire. L’API d’éviction refuse d’évincer un pod si cela violerait le budget.
Syntaxe et options
Section intitulée « Syntaxe et options »Un PDB se configure avec l’un de ces deux champs (pas les deux) :
| Champ | Signification | Exemple |
|---|---|---|
minAvailable | Nombre ou pourcentage de pods qui doivent rester disponibles | 2 ou "50%" |
maxUnavailable | Nombre ou pourcentage de pods qui peuvent être indisponibles | 1 ou "25%" |
maxUnavailable est souvent pratique pour les applications stateless : si
vous passez de 3 à 5 répliques, maxUnavailable: 1 reste valide sans
modification. En revanche, minAvailable est parfois plus lisible pour les
systèmes à quorum (etcd, ZooKeeper) où l’on raisonne en nombre minimum
de membres actifs. Le bon choix dépend du comportement attendu de
l’application.
Exemple pour un Deployment de 3 répliques
Section intitulée « Exemple pour un Deployment de 3 répliques »apiVersion: policy/v1kind: PodDisruptionBudgetmetadata: name: api-server-pdbspec: maxUnavailable: 1 selector: matchLabels: app: api-serverCe PDB autorise 1 pod indisponible à la fois. Pendant un kubectl drain,
Kubernetes évince un pod, attend qu’il soit remplacé et sain, puis évince
le suivant.
Vérifier l’état d’un PDB
Section intitulée « Vérifier l’état d’un PDB »kubectl get pdbNAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGEapi-server-pdb N/A 1 1 5mLa colonne ALLOWED DISRUPTIONS indique combien de pods peuvent encore être
évincés sans violer le budget. Si elle affiche 0, le drain sera bloqué
jusqu’à ce qu’un pod redevienne sain.
Patterns par type d’application
Section intitulée « Patterns par type d’application »| Application | Configuration PDB | Justification |
|---|---|---|
| Frontend stateless (3+ répliques) | maxUnavailable: 1 | Tolérant aux redémarrages, un pod suffit |
| API critique (3 répliques) | maxUnavailable: 1 | Toujours 2 pods actifs minimum |
| Stateful à quorum (3 répliques) | minAvailable: 2 | Maintient le quorum |
| Worker de jobs (5 répliques) | maxUnavailable: "40%" | Absorbe un drain rapide |
Singleton (replicas: 1) | Pas de PDB | Un PDB bloquerait le drain indéfiniment |
Unhealthy Pod Eviction Policy
Section intitulée « Unhealthy Pod Eviction Policy »Le champ spec.unhealthyPodEvictionPolicy contrôle le comportement des
PDB face aux pods défaillants :
IfHealthyBudget(défaut) : les pods enCrashLoopBackOffne sont évincés que si le budget est respecté. Cela peut bloquer un drain si l’application est déjà en échec.AlwaysAllow: les pods non sains sont toujours éligibles à l’éviction, même si le budget est épuisé.
La documentation Kubernetes recommande d’envisager AlwaysAllow pour que
des pods déjà défaillants n’empêchent pas une opération de drain :
apiVersion: policy/v1kind: PodDisruptionBudgetmetadata: name: api-server-pdbspec: maxUnavailable: 1 unhealthyPodEvictionPolicy: AlwaysAllow selector: matchLabels: app: api-serverTroisième couche : répartition des pods
Section intitulée « Troisième couche : répartition des pods »Avoir 3 répliques ne sert à rien si elles tournent toutes sur le même nœud. Deux mécanismes complémentaires forcent la distribution.
Anti-affinité de pod
Section intitulée « Anti-affinité de pod »L’anti-affinité interdit (ou déconseille) de placer deux pods avec le même label sur le même nœud ou dans la même zone.
affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchLabels: app: api-server topologyKey: kubernetes.io/hostnameLe scheduler refuse de placer deux pods api-server sur le même nœud.
Si vous n’avez que 2 nœuds et 3 répliques, la troisième restera Pending.
affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchLabels: app: api-server topologyKey: kubernetes.io/hostnameLe scheduler tente de répartir les pods, mais accepte de co-localiser si nécessaire. C’est le choix pragmatique quand le nombre de nœuds est limité.
Topology spread constraints
Section intitulée « Topology spread constraints »Les topology spread constraints offrent un contrôle plus fin que l’anti-affinité. Elles garantissent une distribution équilibrée des pods sur une dimension topologique (nœud, zone, région).
topologySpreadConstraints: - maxSkew: 1 topologyKey: topology.kubernetes.io/zone whenUnsatisfiable: DoNotSchedule labelSelector: matchLabels: app: api-server| Paramètre | Signification |
|---|---|
maxSkew | Écart maximum de pods entre deux domaines topologiques |
topologyKey | Label qui définit le domaine (kubernetes.io/hostname, topology.kubernetes.io/zone) |
whenUnsatisfiable | DoNotSchedule (strict) ou ScheduleAnyway (souple) |
Avec maxSkew: 1, le scheduler cherche à maintenir une répartition
équilibrée entre les zones, avec un écart maximal de 1 pod entre domaines
topologiques. Si une zone tombe, les pods restants sur les autres zones
continuent à servir le trafic.
Quatrième couche : probes de production
Section intitulée « Quatrième couche : probes de production »Les probes (liveness, readiness, startup) permettent à Kubernetes de savoir si un pod est sain et prêt à recevoir du trafic. Mal configurées, elles deviennent un vecteur de disruption — redémarrages en boucle sous charge ou pods éjectés du Service trop vite.
Les trois types de probes
Section intitulée « Les trois types de probes »| Probe | Rôle | Conséquence d’un échec |
|---|---|---|
| startupProbe | Vérifie que le pod a fini de démarrer | Le pod est tué et recréé |
| livenessProbe | Vérifie que le pod n’est pas bloqué (deadlock) | Le pod est redémarré |
| readinessProbe | Vérifie que le pod peut recevoir du trafic | Le pod est retiré du Service (plus de trafic) |
Dimensionnement pour la production
Section intitulée « Dimensionnement pour la production »Les valeurs par défaut des probes Kubernetes (periodSeconds: 10,
timeoutSeconds: 1, failureThreshold: 3) sont souvent trop génériques
pour la production. Sous charge, un pic CPU peut faire échouer une probe
HTTP sans que le pod soit réellement en panne. Adaptez ces valeurs au
comportement réel de votre application.
startupProbe: httpGet: path: /healthz port: 8080 failureThreshold: 30 periodSeconds: 2livenessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 0 periodSeconds: 10 failureThreshold: 3 timeoutSeconds: 5readinessProbe: httpGet: path: /ready port: 8080 periodSeconds: 5 failureThreshold: 3 timeoutSeconds: 3Règles de dimensionnement
Section intitulée « Règles de dimensionnement »-
Commencer par la startupProbe :
failureThreshold × periodSecondsdoit couvrir le pire temps de démarrage de votre application (cold start + chargement cache + connexions DB). Exemple : 30 × 2 = 60 secondes. -
La livenessProbe ne doit pas tester la charge : elle détecte les deadlocks, pas la lenteur. Utilisez un endpoint
/healthzléger (pas de requête DB, pas de dépendance externe). UnperiodSeconds: 10avecfailureThreshold: 3laisse 30 secondes avant redémarrage. -
La readinessProbe peut tester les dépendances : si votre pod dépend d’une base de données ou d’un cache, la readinessProbe peut vérifier la connexion. Un échec retire le pod du Service sans le tuer. Attention : si vous rendez la readiness dépendante d’un service tiers instable, vous risquez de retirer tous les pods du Service alors que l’application elle-même fonctionne. Ciblez des dépendances stables et indispensables.
-
Séparer les endpoints :
/healthzpour liveness (toujours rapide),/readypour readiness (vérifie les dépendances).
Configuration de référence complète
Section intitulée « Configuration de référence complète »Voici un Deployment qui combine les quatre couches — réplicas, PDB, anti-affinité et probes calibrées pour la production.
apiVersion: apps/v1kind: Deploymentmetadata: name: api-serverspec: replicas: 3 selector: matchLabels: app: api-server strategy: type: RollingUpdate rollingUpdate: maxUnavailable: 1 maxSurge: 1 template: metadata: labels: app: api-server spec: affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchLabels: app: api-server topologyKey: kubernetes.io/hostname topologySpreadConstraints: - maxSkew: 1 topologyKey: topology.kubernetes.io/zone whenUnsatisfiable: ScheduleAnyway labelSelector: matchLabels: app: api-server containers: - name: api image: api-server:1.4.0 ports: - containerPort: 8080 resources: requests: cpu: 250m memory: 256Mi limits: memory: 512Mi startupProbe: httpGet: path: /healthz port: 8080 failureThreshold: 30 periodSeconds: 2 livenessProbe: httpGet: path: /healthz port: 8080 periodSeconds: 10 failureThreshold: 3 timeoutSeconds: 5 readinessProbe: httpGet: path: /ready port: 8080 periodSeconds: 5 failureThreshold: 3 timeoutSeconds: 3apiVersion: policy/v1kind: PodDisruptionBudgetmetadata: name: api-server-pdbspec: maxUnavailable: 1 unhealthyPodEvictionPolicy: AlwaysAllow selector: matchLabels: app: api-serverVérification après déploiement
Section intitulée « Vérification après déploiement »# Vérifier la distribution des pods sur les nœudskubectl get pods -l app=api-server -o wide
# Vérifier le PDBkubectl get pdb api-server-pdb
# Vérifier l'état des probeskubectl describe pod -l app=api-server | grep -A5 "Conditions:"Interaction avec les rolling updates
Section intitulée « Interaction avec les rolling updates »La stratégie RollingUpdate du Deployment interagit avec le PDB. Pendant
un rolling update, Kubernetes crée de nouveaux pods et supprime les anciens.
Les suppressions de pods pendant un rolling update ne passent pas
par l’API d’éviction — le PDB ne contrôle donc pas directement le rythme
du rollout. C’est le champ maxUnavailable de la stratégie de
déploiement qui en décide. Cependant, les pods indisponibles durant le
rollout comptent contre le budget du PDB, ce qui peut bloquer un drain
coïncidant avec une mise à jour.
Pour une cohérence maximale :
- Le
maxUnavailablede la stratégie doit être pensé en cohérence avec votre PDB et votre niveau de disponibilité cible - Utilisez
maxSurge: 1pour créer le pod de remplacement avant de supprimer l’ancien
Dépannage
Section intitulée « Dépannage »| Symptôme | Cause probable | Solution |
|---|---|---|
| Drain bloqué indéfiniment | PDB trop restrictif ou pod en CrashLoopBackOff | Vérifier kubectl get pdb (ALLOWED DISRUPTIONS = 0). Passer à unhealthyPodEvictionPolicy: AlwaysAllow |
Pod en Pending après drain | Plus assez de nœuds / ressources pour respecter l’anti-affinité | Vérifier kubectl describe pod (événements scheduling). Passer en preferred si nécessaire |
| Redémarrages en boucle sous charge | livenessProbe trop agressive | Augmenter failureThreshold ou timeoutSeconds. Séparer /healthz et /ready |
| Pod retiré du Service mais fonctionnel | readinessProbe trop sensible | Augmenter failureThreshold de la readinessProbe. Vérifier que l’endpoint ne dépend pas d’un service externe instable |
| PDB affiche ALLOWED DISRUPTIONS = 0 en permanence | Moins de répliques saines que minAvailable | Vérifier que le Deployment a assez de répliques Ready. Vérifier les probes |
| Topology spread constraint bloquante | maxSkew strict avec trop peu de zones | Passer whenUnsatisfiable: ScheduleAnyway ou augmenter maxSkew |
À retenir
Section intitulée « À retenir »- Kubernetes ne garantit aucune disponibilité par défaut — c’est à vous de configurer réplicas, PDB, placement et probes.
- Un PDB protège contre les disruptions volontaires (drain, upgrade). Il ne protège pas contre les pannes de nœud.
maxUnavailableest pratique pour les stateless,minAvailableest plus lisible pour les systèmes à quorum — le bon choix dépend de l’application.- L’anti-affinité empêche la co-localisation de pods sur un nœud. Les topology spread constraints répartissent équitablement sur les zones.
- Les probes doivent être dimensionnées pour la production : la liveness ne teste pas la charge, la readiness peut tester les dépendances.
- Un singleton (
replicas: 1) est un SPOF. Aucun PDB ne compense l’absence de réplication. - Combinez
unhealthyPodEvictionPolicy: AlwaysAllowavec vos PDB pour éviter qu’une application défaillante ne bloque la maintenance.