K8s by Example: Pod Disruption Budgets

PDBs protect against voluntary disruptions: node drains, cluster upgrades, autoscaler scale-downs. Ensures minimum availability during maintenance operations.

pdb.yaml

PDBs use the policy/v1 API. The selector matches Pods to protect. minAvailable specifies how many Pods must remain running during disruptions.

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: my-app-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: my-app
pdb-options.yaml

minAvailable: minimum Pods that must stay running. maxUnavailable: maximum Pods that can be down. Use one or the other, not both. Percentages work too.

spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: my-app
---
spec:
  maxUnavailable: 1
  selector:
    matchLabels:
      app: my-app
---
spec:
  minAvailable: "50%"
  selector:
    matchLabels:
      app: my-app
terminal

PDBs only protect against voluntary disruptions like drains (first command shows PDB blocking eviction). They can’t prevent involuntary disruptions: node failures, OOMKill, crashes. kubectl delete pod also ignores PDB (second command).

$ kubectl drain node-1 --ignore-daemonsets
node/node-1 cordoned
evicting pod default/my-app-1
evicting pod default/my-app-2
error when evicting pods: Cannot evict pod, would violate PDB

$ kubectl delete pod my-app-1 --grace-period=0
pod "my-app-1" deleted
deployment-pdb.yaml

Match PDB to your replica count. Don’t set minAvailable equal to replicas or drains will hang forever. Leave room for at least one Pod to be disrupted. minAvailable: 2 and maxUnavailable: 1 are equivalent for 3 replicas.

apiVersion: apps/v1
kind: Deployment
spec:
  replicas: 3
---
apiVersion: policy/v1
kind: PodDisruptionBudget
spec:
  minAvailable: 2
---
spec:
  maxUnavailable: 1
pdb-unhealthy.yaml

unhealthyPodEvictionPolicy controls eviction of unhealthy Pods. AlwaysAllow evicts unhealthy Pods regardless of budget. Helps with stuck rollouts where Pods are crashlooping.

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: my-app-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: my-app
  unhealthyPodEvictionPolicy: AlwaysAllow
terminal

PDB status shows allowed disruptions and current Pod count. ALLOWED shows how many Pods can be safely disrupted right now. Zero means operations will block.

$ kubectl get pdb
NAME         MIN AVAILABLE   MAX UNAVAILABLE   ALLOWED DISRUPTIONS   AGE
my-app-pdb   2               N/A               1                     5m

$ kubectl describe pdb my-app-pdb
Status:
  Current Healthy:   3
  Desired Healthy:   2
  Disruptions Allowed: 1
  Expected Pods:     3
  Observed Generation: 1
deployment-pdb-strategy.yaml

During rolling updates, PDB interacts with Deployment strategy. Both constraints must be satisfied. Can cause slow rollouts if both are too restrictive.

spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 1
---
spec:
  minAvailable: 2
terminal

Debug PDB issues by checking allowed disruptions and eviction events. Force drain bypasses PDB but risks availability. Use —delete-emptydir-data for Pods with emptyDir volumes.

$ kubectl get pdb -A
NAMESPACE   NAME         MIN AVAILABLE   ALLOWED DISRUPTIONS
default     my-app-pdb   2               1
kube-system coredns      1               1

$ kubectl get pods -l app=my-app
NAME         READY   STATUS    AGE
my-app-1     1/1     Running   10m
my-app-2     1/1     Running   10m
my-app-3     1/1     Running   10m

$ kubectl drain node-1 --force --ignore-daemonsets \
    --delete-emptydir-data
node/node-1 drained

$ kubectl get events --field-selector reason=Eviction

Index | GitHub | Use arrow keys to navigate |