Aller au contenu
English
Conteneurs & Orchestration medium

Garantir la disponibilité applicative sur Kubernetes

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

Attention à ce qu'un PDB ne protège pas. Il n'agit que sur l'API d'éviction : un kubectl delete pod, un scale à zéro ou une panne de nœud passent outre sans rien consulter. Le budget encadre les disruptions volontaires et propres, pas tout ce qui peut arriver à vos pods.

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: mon-api
spec:
replicas: 3
selector:
matchLabels:
app: mon-api
template:
metadata:
labels:
app: mon-api
spec:
containers:
- name: api
image: mon-api: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 à état et à quorum, etcd, ZooKeeper ou Consul, visez un nombre impair, 3 ou 5.

Un PodDisruptionBudget indique à Kubernetes combien de Pods d'une application doivent rester disponibles, ou combien peuvent manquer, pendant une interruption 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 pratique pour les applications sans état : si vous passez de 3 à 5 répliques, maxUnavailable: 1 reste valide sans modification. minAvailable est plus lisible pour les systèmes à quorum, etcd ou 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: mon-api-pdb
spec:
maxUnavailable: 1
selector:
matchLabels:
app: mon-api

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
mon-api-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.

Une précision utile quand vous relisez un PDB existant : la valeur par défaut n'est pas inscrite dans l'objet. Un budget qui ne déclare rien affiche un champ vide, pas IfHealthyBudget. Ne concluez donc pas d'un kubectl get pdb -o yaml silencieux que la politique n'existe pas, elle s'applique quand même. Une valeur inventée est en revanche rejetée sans ambiguïté :

spec.unhealthyPodEvictionPolicy: Unsupported value: "NImporteQuoi":
supported values: "AlwaysAllow", "IfHealthyBudget"
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: mon-api-pdb
spec:
maxUnavailable: 1
unhealthyPodEvictionPolicy: AlwaysAllow
selector:
matchLabels:
app: mon-api

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: mon-api
topologyKey: kubernetes.io/hostname

Le scheduler refuse de placer deux pods mon-api sur le même nœud. Si vous n'avez que 2 nœuds et 3 répliques, la troisième restera Pending, et le message nomme précisément la règle en cause :

0/2 nodes are available: 1 node(s) didn't match pod anti-affinity rules,
1 node(s) had untolerated taint(s).

Le choix entre les deux se décide sur un seul critère, le rapport entre le nombre de nœuds et le nombre de répliques. Prenez required quand le cluster a toujours au moins autant de nœuds ou de zones que de répliques, typiquement un environnement cloud avec autoscaling : la garantie est alors réelle et sans coût. Prenez preferred dès que ce nombre est variable ou inférieur : vous obtenez la répartition quand elle est possible, sans jamais laisser un pod bloqué en Pending.

Un mot sur le coût : l'anti-affinité entre Pods oblige le scheduler à comparer chaque candidat à tous les Pods déjà placés, et cette charge croît avec la taille du cluster. Pour un besoin de répartition simple, les topologySpreadConstraints sont à la fois plus lisibles et moins coûteux.

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: mon-api
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 maintient une répartition équilibrée entre zones, avec un écart maximal d'un Pod entre domaines topologiques. Si une zone tombe, les Pods des autres zones continuent de servir le trafic.

Les deux mécanismes répondent à la même question avec des moyens différents : l'anti-affinité raisonne en interdiction, « pas deux ici », là où les topologySpreadConstraints raisonnent en équilibre, avec un écart toléré entre domaines. Le second exprime plus naturellement une répartition sur plusieurs zones, et se lit mieux six mois plus tard.

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 répondent à trois questions distinctes, et les confondre est l'erreur la plus coûteuse de cette section : « le conteneur est-il vivant », « peut-il recevoir du trafic », « a-t-il fini de démarrer ». Une seule des trois tue.

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 sondes, periodSeconds: 10, timeoutSeconds: 1 et failureThreshold: 3, sont trop génériques pour la production. Sous charge, un pic de CPU fait échouer une sonde 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: mon-api
spec:
replicas: 3
selector:
matchLabels:
app: mon-api
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
template:
metadata:
labels:
app: mon-api
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: mon-api
topologyKey: kubernetes.io/hostname
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: mon-api
containers:
- name: api
image: mon-api: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

Une configuration de disponibilité ne se vérifie pas en relisant le manifeste, mais en regardant où les pods ont réellement atterri et ce que le budget autorise. Les trois commandes ci-dessous répondent à cela en quelques secondes.

Fenêtre de terminal
# Vérifier la distribution des pods sur les nœuds
kubectl get pods -l app=mon-api -o wide
# Vérifier le PDB
kubectl get pdb mon-api-pdb
# Vérifier l'état des probes
kubectl describe pod -l app=mon-api | 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

Les symptômes ci-dessous ont une cause commune : une protection qui fait ce qu'on lui a demandé, mais pas ce qu'on croyait lui demander. Une contrainte de répartition trop stricte, un budget trop serré, une probe mal dimensionnée : ces réglages se retournent contre la disponibilité qu'ils devaient garantir.

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.

Sept questions sur ce que chaque couche protège vraiment : un budget qu'une suppression directe ignore, une anti-affinité qui laisse des Pods en attente, et deux sondes qu'il ne faut surtout pas confondre.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

7 questions
5 min.
80% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

  • Routine d'exploitation : Les contrôles quotidiens qui repèrent une dégradation avant qu'elle ne devienne une indisponibilité.
  • CIS Benchmark : L'audit de configuration du cluster, volet sécurité du même travail de fiabilité.
  • Pod Security Standards : Le garde-fou qui empêche un Pod mal configuré de déstabiliser un nœud entier.

Ce site vous est utile ?

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

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn