Kubernetes PodDisruptionBudgets: beschikbaarheid beschermen tijdens onderhoud

Een PodDisruptionBudget vertelt Kubernetes hoeveel pods van je applicatie tegelijk offline mogen zijn bij gepland onderhoud. Zonder PDB kan een enkele kubectl drain al je replica's in een keer verwijderen. Deze gids behandelt de keuze tussen minAvailable en maxUnavailable, het omgaan met unhealthy pods, en de veelgemaakte fouten die clusterupgrades blokkeren.

Wat je aan het eind hebt

Een PodDisruptionBudget (PDB) op je workload die voorkomt dat kubectl drain, Cluster Autoscaler scale-down en cloudprovider-nodeupgrades te veel pods tegelijk verwijderen. Je weet welk veld je moet kiezen per workloadtype en hoe je de misconfiguraties vermijdt die drainoperaties op slot zetten.

Vereisten

Voluntary vs. involuntary disruptions

PDBs beschermen alleen tegen voluntary disruptions. Dat onderscheid is belangrijk om te snappen voordat je YAML gaat schrijven.

Voluntary disruptions zijn acties waarbij een operator of controller bewust pods verwijdert:

  • kubectl drain voor nodeonderhoud
  • Cluster Autoscaler of Karpenter die onderbenutte nodes consolideert
  • Cloudprovider node pool upgrades (AKS, GKE, EKS)
  • Handmatig kubectl delete pod (al gaat dit wel buiten de Eviction API om, dus PDBs worden hierbij overgeslagen)

Involuntary disruptions zijn ongeplande storingen waar Kubernetes geen controle over heeft: hardwarefalen, kernel panics, verdwijnende VM's, spot instance-interruptions, out-of-memory kills. PDBs hebben hier geen zeggenschap over. Een nodecrash die drie pods meeneemt vraagt geen toestemming.

Het subtiele deel: involuntary disruptions tellen wel mee voor het budget. Als een nodefalen je healthy pods al onder desiredHealthy heeft gebracht, wordt een gelijktijdige kubectl drain op een andere node geblokkeerd totdat er vervangende pods draaien.

PDB aanmaken: minAvailable vs. maxUnavailable

Een PDB-spec vereist precies een van twee wederzijds exclusieve velden:

Veld Betekenis Afronding (percentage)
minAvailable Minimum pods dat beschikbaar moet blijven na eviction Rondt omhoog af (conservatief: beschermt meer)
maxUnavailable Maximum pods dat unavailable mag zijn na eviction Rondt omhoog af (soepeler: staat meer disruptions toe)

Beide accepteren een integer of een percentagestring zoals "25%".

Stap 1: kies je veld

Voor stateless services (web-API's, workers, microservices) gebruik je maxUnavailable. De officiele Kubernetes-docs raden dit aan omdat het automatisch meeschaalt met veranderingen in replica-aantallen. Statsig beschreef hoe ze overstapten van minAvailable naar maxUnavailable nadat services met minder dan vijf pods permanent op disruptionsAllowed: 0 bleven hangen.

Voor quorum-gebaseerde stateful systemen (etcd, ZooKeeper, Consul) gebruik je minAvailable ingesteld op de quorumgrootte. Een 3-node etcd-cluster heeft minAvailable: 2 nodig (quorum = floor(3/2) + 1 = 2). Het vereiste aantal gezonde members is vast, ongeacht het totale aantal replica's.

Stap 2: schrijf het PDB-manifest

Stateless API met maxUnavailable:

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: web-api-pdb
  namespace: production
spec:
  maxUnavailable: 1                         # een pod tegelijk
  unhealthyPodEvictionPolicy: AlwaysAllow   # voorkomt CrashLoopBackOff-deadlock (k8s 1.26+)
  selector:
    matchLabels:
      app: web-api

Quorum-gebaseerde StatefulSet met minAvailable:

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: zookeeper-pdb
  namespace: production
spec:
  minAvailable: 2                            # quorum voor een 3-node ensemble
  unhealthyPodEvictionPolicy: IfHealthyBudget # conservatief voor stateful workloads
  selector:
    matchLabels:
      app: zookeeper

Stap 3: toepassen en verifieren

kubectl apply -f pdb.yaml

kubectl get pdb -n production
# NAME            MIN AVAILABLE   MAX UNAVAILABLE   ALLOWED DISRUPTIONS   AGE
# web-api-pdb     N/A             1                 2                     5s

De kolom ALLOWED DISRUPTIONS is het getal dat de Eviction API controleert voor het goedkeuren of afwijzen van een eviction-verzoek. Als er 0 staat, kan geen enkele voluntary disruption doorgaan.

Voor de volledige status:

kubectl get pdb web-api-pdb -n production -o jsonpath='{.status}' | jq .
# {
#   "currentHealthy": 3,
#   "desiredHealthy": 2,
#   "disruptionsAllowed": 1,
#   "expectedPods": 3
# }

Unhealthy pods afhandelen met unhealthyPodEvictionPolicy

Voor Kubernetes 1.26 werd een pod die vastzat in CrashLoopBackOff nog steeds beschermd door z'n PDB. De pod was kapot en serveerde geen verkeer, maar het evicten ervan zou het aantal onder desiredHealthy brengen. Het gevolg: kubectl drain hangt oneindig wachtend op een pod die nooit gezond wordt.

Er bestaan twee policies:

  • IfHealthyBudget (standaard): unhealthy pods mogen alleen ge-evict worden als de applicatie als geheel niet verstoord is (currentHealthy >= desiredHealthy). Conservatief, maar risico op deadlock wanneer alle pods unhealthy zijn.
  • AlwaysAllow: unhealthy pods (zonder Ready-conditie) mogen altijd ge-evict worden, ongeacht het budget. De Kubernetes-docs raden dit nu aan voor de meeste workloads.

Stel AlwaysAllow in, tenzij je een stateful systeem draait waar zelfs een gedeeltelijk kapotte pod bijdraagt aan databeschikbaarheid.

Hoe PDBs samenwerken met kubectl drain

Wanneer je kubectl drain uitvoert, gebeurt het volgende stap voor stap:

  1. De node wordt gecordoned (gemarkeerd als Unschedulable).
  2. Voor elke pod stuurt kubectl een eviction-verzoek naar de Kubernetes Eviction API.
  3. De Eviction API controleert de PDB. Als het evicten van de pod het budget zou schenden, komt er HTTP 429 (Too Many Requests) terug.
  4. kubectl drain probeert afgewezen verzoeken opnieuw totdat het lukt of de timeout bereikt is.

Als ALLOWED DISRUPTIONS op 0 staat, blokkeert drain oneindig. Cloudproviders hanteren hun eigen timeouts: AKS stopt na 1 uur met UpgradeFailed / PodDrainFailure, GKE force-draint na 1 uur, en EKS faalt de upgrade na 50 minuten.

Controleer voor een clusterupgrade op geblokkeerde PDBs:

kubectl get pdb --all-namespaces -o wide | grep ' 0 '
# Elke rij met ALLOWED DISRUPTIONS = 0 blokkeert de upgrade

Noodgrepen bij vastgelopen drains

# PDBs volledig omzeilen (voorzichtig)
kubectl drain <node> --ignore-daemonsets --disable-eviction
# --disable-eviction (k8s 1.18+) forceert directe verwijdering in plaats van eviction

# Drain met timeout zodat het niet eindeloos blokkeert
kubectl drain <node> --ignore-daemonsets --timeout=300s

--disable-eviction slaat PDB-controles helemaal over. Gebruik het alleen als je de impact op beschikbaarheid begrijpt en andere opties uitgeput zijn.

Hoe PDBs samenwerken met Cluster Autoscaler

De Cluster Autoscaler respecteert PDBs bij scale-down. Voordat een node wordt aangemerkt voor verwijdering, controleert de autoscaler of het evicten van de pods geen PDB zou schenden. Zo ja, dan wordt de node als "niet verwijderbaar" gemarkeerd en slaat de scale-down die over.

Configuraties die scale-down blokkeren:

  • maxUnavailable: 0 op een PDB die een pod op de onderbenutte node matcht
  • minAvailable gelijk aan het huidige replica-aantal, waardoor disruptionsAllowed: 0 ontstaat
  • Een single-replica Deployment met een restrictieve PDB

Als Cluster Autoscaler niet omlaag schaalt en je PDB-interferentie vermoedt:

kubectl get pdb --all-namespaces -o wide
# Zoek naar ALLOWED DISRUPTIONS = 0 bij workloads op de vastzittende node

Voor Karpenter-gebruikers: Karpenters voluntary disruption-methoden (consolidation, drift, expiration) respecteren ook PDBs. Als een PDB op welke pod dan ook op een node blocking is, consolideert Karpenter die node niet. Karpenter NodePool Disruption Budgets zijn trouwens een apart, aanvullend systeem dat node-level disruptions beperkt, niet pod-level beschikbaarheid.

PDBs en rolling updates: gescheiden lagen

Een veelvoorkomend misverstand: PDBs beperken Deployment rolling updates niet. De officiele docs zeggen het expliciet: "workload resources (such as Deployment and StatefulSet) are not limited by PodDisruptionBudgets when doing rolling updates."

De Deployment-controller z'n .spec.strategy.rollingUpdate.maxUnavailable en maxSurge bepalen het rollout-gedrag. PDBs regelen voluntary evictions vanuit externe operaties (drain, autoscaler). Het zijn gescheiden, aanvullende lagen:

Deployment strategy → regelt rolling update-gedrag (nieuwe versie uitrollen)
PDB                 → regelt voluntary eviction-gedrag (drain, autoscaler)

Een goede combinatie voor een productieservice:

# Deployment: zero-downtime rollout
spec:
  replicas: 4
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1           # draai 1 nieuwe pod op voor je een oude verwijdert
      maxUnavailable: 0     # nooit onder het gewenste aantal tijdens rollout
---
# PDB: veilig infrastructuuronderhoud
apiVersion: policy/v1
kind: PodDisruptionBudget
spec:
  maxUnavailable: 1          # sta toe dat 1 pod tegelijk gedrained wordt
  unhealthyPodEvictionPolicy: AlwaysAllow
  selector:
    matchLabels:
      app: myapp

Als een kubectl drain en een rolling update tegelijk plaatsvinden, wordt de gecombineerde unavailability van beide getoetst aan de PDB. De PDB voorkomt de rolling update zelf niet, maar voorkomt wel dat drain de situatie erger maakt.

Veelgemaakte fouten

PDB op een single-replica Deployment

Een PDB met minAvailable: 1 (of maxUnavailable: 0) op een single-replica Deployment levert permanent disruptionsAllowed: 0 op. Drain blokkeert eindeloos, Cluster Autoscaler kan niet omlaag schalen, Karpenter kan niet consolideren. Draai 2+ replica's, of sla de PDB voor die workload gewoon over.

maxUnavailable: 0 of minAvailable: 100%

Beide configuraties blokkeren alle voluntary evictions. Clusterupgrades op AKS, GKE en EKS lopen dan vast en falen. Gebruik dit alleen als je gecoordineerde out-of-band onderhoudsprocedures hebt en begrijpt dat het cluster z'n nodepools niet zelf kan herstellen.

Overlappende selectors tussen meerdere PDBs

Als twee PDBs dezelfde pod selecteren, retourneert de Eviction API HTTP 500 in plaats van 429. Drain faalt op een onverwachte manier. Elke PDB moet een unieke set pods dekken.

Lege selector in policy/v1

In policy/v1beta1 (verwijderd in Kubernetes 1.25) matchte een lege selector {} nul pods. In policy/v1 matcht een lege selector elke pod in de namespace. Een PDB-manifest migreren zonder de selector bij te werken kan onbedoeld de hele namespace op slot zetten.

De HPA-interactie vergeten

De Horizontal Pod Autoscaler raadpleegt geen PDBs bij het omlaag schalen van replica's. HPA kan het replica-aantal onder het PDB-minimum van minAvailable brengen, waardoor disruptionsAllowed op 0 komt en elke gelijktijdige drainoperatie blokkeert totdat HPA weer opschaalt. Monitor ALLOWED DISRUPTIONS na HPA scale-down events.

Resultaat verifieren

Controleer na het toepassen van je PDB of alles werkt:

# Huidige status bekijken
kubectl get pdb -n production -o wide
# ALLOWED DISRUPTIONS moet >= 1 zijn

# Simuleer een drain op een niet-kritieke node (cordon eerst, bekijk, drain daarna)
kubectl cordon <node-name>
kubectl drain <node-name> --ignore-daemonsets --dry-run=client
# De dry run toont welke pods ge-evict zouden worden en of PDBs dat blokkeren

# Tevreden? Voer de echte drain uit
kubectl drain <node-name> --ignore-daemonsets

Controleer na de drain dat de ge-evicte pods opnieuw ingepland zijn en de service de hele tijd beschikbaar bleef.

Wanneer escaleren

Als drain vastloopt ondanks je PDB-configuratie, verzamel dan het volgende voor je hulp vraagt:

  • kubectl get pdb --all-namespaces -o wide (volledige PDB-status)
  • kubectl describe pdb <name> -n <namespace> (events en conditions)
  • kubectl get events --field-selector reason=EvictionBlocked (eviction-specifieke events)
  • Kubernetes-versie (kubectl version --short)
  • Cloudprovider en managed service tier (AKS, GKE, EKS)
  • Of unhealthyPodEvictionPolicy is ingesteld en op welke waarde
  • Aantal replica's en hun Ready-status (kubectl get pods -l app=<label> -o wide)

Terugkerende server- of deploymentproblemen?

Ik help teams productie betrouwbaar maken met CI/CD, Kubernetes en cloud—zodat fixes blijven en deploys geen stress meer zijn.

Bekijk DevOps consultancy

Doorzoek deze site

Begin met typen om te zoeken, of blader door de kennisbank en blog.