Aller au contenu
Conteneurs & Orchestration medium
🔐 Alerte sécurité — Incident supply chain Trivy : lire mon analyse de l'attaque

Garantir la disponibilité applicative sur Kubernetes

16 min de lecture

logo kubernetes

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.

  • 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

Kubernetes distingue deux types de disruptions qui affectent vos pods :

TypeExemplesContrôlable ?
InvolontairePanne de nœud, kernel panic, perte réseau, OOM killNon — seule la réplication protège
Volontairekubectl drain, suppression manuelle via éviction, opérations d’administrationOui — 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).

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/v1
kind: Deployment
metadata:
name: api-server
spec:
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: 8080

Trois 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).

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.

Un PDB se configure avec l’un de ces deux champs (pas les deux) :

ChampSignificationExemple
minAvailableNombre ou pourcentage de pods qui doivent rester disponibles2 ou "50%"
maxUnavailableNombre ou pourcentage de pods qui peuvent être indisponibles1 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.

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api-server-pdb
spec:
maxUnavailable: 1
selector:
matchLabels:
app: api-server

Ce 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.

Fenêtre de terminal
kubectl get pdb
NAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGE
api-server-pdb N/A 1 1 5m

La 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.

ApplicationConfiguration PDBJustification
Frontend stateless (3+ répliques)maxUnavailable: 1Tolérant aux redémarrages, un pod suffit
API critique (3 répliques)maxUnavailable: 1Toujours 2 pods actifs minimum
Stateful à quorum (3 répliques)minAvailable: 2Maintient le quorum
Worker de jobs (5 répliques)maxUnavailable: "40%"Absorbe un drain rapide
Singleton (replicas: 1)Pas de PDBUn PDB bloquerait le drain indéfiniment

Le champ spec.unhealthyPodEvictionPolicy contrôle le comportement des PDB face aux pods défaillants :

  • IfHealthyBudget (défaut) : les pods en CrashLoopBackOff ne 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/v1
kind: PodDisruptionBudget
metadata:
name: api-server-pdb
spec:
maxUnavailable: 1
unhealthyPodEvictionPolicy: AlwaysAllow
selector:
matchLabels:
app: api-server

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.

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/hostname

Le 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.

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ètreSignification
maxSkewÉcart maximum de pods entre deux domaines topologiques
topologyKeyLabel qui définit le domaine (kubernetes.io/hostname, topology.kubernetes.io/zone)
whenUnsatisfiableDoNotSchedule (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.

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.

ProbeRôleConséquence d’un échec
startupProbeVérifie que le pod a fini de démarrerLe pod est tué et recréé
livenessProbeVérifie que le pod n’est pas bloqué (deadlock)Le pod est redémarré
readinessProbeVérifie que le pod peut recevoir du traficLe pod est retiré du Service (plus de trafic)

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: 2
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 0
periodSeconds: 10
failureThreshold: 3
timeoutSeconds: 5
readinessProbe:
httpGet:
path: /ready
port: 8080
periodSeconds: 5
failureThreshold: 3
timeoutSeconds: 3
  1. Commencer par la startupProbe : failureThreshold × periodSeconds doit couvrir le pire temps de démarrage de votre application (cold start + chargement cache + connexions DB). Exemple : 30 × 2 = 60 secondes.

  2. La livenessProbe ne doit pas tester la charge : elle détecte les deadlocks, pas la lenteur. Utilisez un endpoint /healthz léger (pas de requête DB, pas de dépendance externe). Un periodSeconds: 10 avec failureThreshold: 3 laisse 30 secondes avant redémarrage.

  3. 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.

  4. Séparer les endpoints : /healthz pour liveness (toujours rapide), /ready pour readiness (vérifie les dépendances).

Voici un Deployment qui combine les quatre couches — réplicas, PDB, anti-affinité et probes calibrées pour la production.

apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
spec:
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: 3
Fenêtre de terminal
# Vérifier la distribution des pods sur les nœuds
kubectl get pods -l app=api-server -o wide
# Vérifier le PDB
kubectl get pdb api-server-pdb
# Vérifier l'état des probes
kubectl describe pod -l app=api-server | grep -A5 "Conditions:"

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 maxUnavailable de la stratégie doit être pensé en cohérence avec votre PDB et votre niveau de disponibilité cible
  • Utilisez maxSurge: 1 pour créer le pod de remplacement avant de supprimer l’ancien
SymptômeCause probableSolution
Drain bloqué indéfinimentPDB trop restrictif ou pod en CrashLoopBackOffVérifier kubectl get pdb (ALLOWED DISRUPTIONS = 0). Passer à unhealthyPodEvictionPolicy: AlwaysAllow
Pod en Pending après drainPlus 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 chargelivenessProbe trop agressiveAugmenter failureThreshold ou timeoutSeconds. Séparer /healthz et /ready
Pod retiré du Service mais fonctionnelreadinessProbe trop sensibleAugmenter failureThreshold de la readinessProbe. Vérifier que l’endpoint ne dépend pas d’un service externe instable
PDB affiche ALLOWED DISRUPTIONS = 0 en permanenceMoins de répliques saines que minAvailableVérifier que le Deployment a assez de répliques Ready. Vérifier les probes
Topology spread constraint bloquantemaxSkew strict avec trop peu de zonesPasser whenUnsatisfiable: ScheduleAnyway ou augmenter maxSkew
  • 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.
  • maxUnavailable est pratique pour les stateless, minAvailable est 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: AlwaysAllow avec vos PDB pour éviter qu’une application défaillante ne bloque la maintenance.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn