Un PodDisruptionBudget, o PDB, limita cuántas réplicas de una aplicación pueden quedar simultáneamente fuera de servicio durante interrupciones voluntarias gestionadas mediante la Eviction API.
El caso habitual es el mantenimiento de un nodo:
kubectl drain → Eviction API → PodDisruptionBudget → PodSi una nueva eviction reduce la cantidad de pods saludables por debajo del presupuesto, Kubernetes la rechaza temporalmente. Cuando un reemplazo queda Ready, el presupuesto se recupera y el mantenimiento puede continuar.
Qué protege realmente un PDB
Un PDB puede regular acciones como:
kubectl drain.- Mantenimiento planificado de nodos.
- Reducción de nodos realizada por herramientas que respetan la Eviction API.
- Otras evictions iniciadas mediante la API correspondiente.
No puede impedir:
- Fallos físicos de un nodo.
- Pérdida de una máquina virtual.
- Particiones de red.
- Evictions por presión de recursos.
- Eliminaciones directas con
kubectl delete pod. - Eliminación o reducción de réplicas de un Deployment.
- Rollouts iniciados por el controlador del workload.
Las interrupciones involuntarias no pueden ser bloqueadas, pero los pods que dejan de estar disponibles sí reducen el presupuesto restante. Documentación de Kubernetes.
Un PDB no garantiza disponibilidad por sí solo. La aplicación también necesita réplicas, readiness probes correctas, capacidad libre y distribución entre nodos o zonas.

Escenario de ejemplo
Se desplegarán tres réplicas distribuidas en nodos diferentes y un PDB con:
minAvailable: 2Cuando los tres pods están saludables:
currentHealthy: 3
desiredHealthy: 2
disruptionsAllowed: 1Después de una eviction:
currentHealthy: 2
desiredHealthy: 2
disruptionsAllowed: 0La siguiente eviction queda bloqueada hasta que un reemplazo esté Ready.
Requisitos previos
- Cluster Kubernetes con soporte para
policy/v1. - Al menos tres nodos worker para el ejemplo distribuido.
- Capacidad suficiente para reprogramar pods durante el mantenimiento.
kubectlconfigurado con el contexto correcto.- Permisos para crear PDB, Deployment y evictions.
- Aplicación administrada mediante Deployment, StatefulSet u otro controlador compatible.
- Métricas y alertas para comprobar la disponibilidad real.
Paso 1: comprobar contexto, versión y permisos
kubectl config current-context
kubectl version
kubectl get nodes -o wideComprueba los permisos:
kubectl auth can-i create poddisruptionbudgets.policy \
--namespace donweb-lab
kubectl auth can-i create deployments.apps \
--namespace donweb-lab
kubectl auth can-i create pods/eviction \
--namespace donweb-labConfirma también que el campo para pods no saludables esté disponible:
kubectl explain \
poddisruptionbudget.spec.unhealthyPodEvictionPolicyPaso 2: crear un namespace de ensayo
kubectl create namespace donweb-lab \
--dry-run=client \
-o yaml \
| kubectl apply -f -Comprueba:
kubectl get namespace donweb-labNo pruebes un drain en un nodo de producción compartido. Utiliza un cluster de laboratorio o nodos dedicados a la prueba.
Paso 3: preparar una aplicación replicada
Guarda este manifiesto como deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-pdb-demo
namespace: donweb-lab
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
selector:
matchLabels:
app: web-pdb-demo
template:
metadata:
labels:
app: web-pdb-demo
spec:
terminationGracePeriodSeconds: 30
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: web-pdb-demo
topologyKey: kubernetes.io/hostname
containers:
- name: web
image: nginx:1.27-alpine
ports:
- name: http
containerPort: 80
readinessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 3
periodSeconds: 5
timeoutSeconds: 2
failureThreshold: 3
resources:
requests:
cpu: 50m
memory: 32Mi
limits:
memory: 128MiEn producción, utiliza una imagen aprobada y fijada por digest.
La anti-afinidad requerida evita colocar dos réplicas de este ejemplo en el mismo nodo. Si no existen tres nodos elegibles, algunos pods permanecerán Pending, lo que demuestra que un PDB no crea capacidad adicional.
La estrategia del Deployment se configura por separado porque los rollouts no están limitados por el PDB.
Paso 4: crear el PodDisruptionBudget
Guarda como pdb.yaml:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: web-pdb-demo
namespace: donweb-lab
spec:
minAvailable: 2
unhealthyPodEvictionPolicy: AlwaysAllow
selector:
matchLabels:
app: web-pdb-demoselector.matchLabels debe coincidir con las etiquetas de la plantilla del Deployment.
AlwaysAllow permite retirar durante un drain los pods que están ejecutándose pero no están saludables. Los pods saludables continúan protegidos por el presupuesto. Kubernetes recomienda esta política para evitar que una aplicación defectuosa bloquee indefinidamente el mantenimiento de un nodo. Referencia de la API.
Si necesitas impedir la eviction de pods no saludables mientras la aplicación intenta recuperarse, conserva el valor predeterminado IfHealthyBudget.
Paso 5: validar los manifiestos
Realiza una validación en el API server sin persistir:
kubectl apply \
--dry-run=server \
-f deployment.yaml
kubectl apply \
--dry-run=server \
-f pdb.yamlComprueba las diferencias:
kubectl diff -f deployment.yaml
kubectl diff -f pdb.yamlkubectl diff devuelve código 1 cuando existen diferencias y un código mayor ante un error. No ocultes indiscriminadamente el resultado con || true.
Valida específicamente el selector:
kubectl get -f deployment.yaml \
-o jsonpath='{.spec.selector.matchLabels}{"\n"}'
kubectl get -f pdb.yaml \
-o jsonpath='{.spec.selector.matchLabels}{"\n"}'Paso 6: aplicar y esperar disponibilidad
kubectl apply -f deployment.yaml
kubectl rollout status \
deployment/web-pdb-demo \
-n donweb-lab \
--timeout=5m
kubectl apply -f pdb.yamlComprueba la distribución:
kubectl get pods \
-n donweb-lab \
-l app=web-pdb-demo \
-o wideLos tres pods deben aparecer Ready y, en este ejemplo, ubicados en nodos diferentes.
Paso 7: comprobar el estado del presupuesto
kubectl get pdb -n donweb-lab
kubectl describe pdb web-pdb-demo -n donweb-labObtén los campos principales:
kubectl get pdb web-pdb-demo \
-n donweb-lab \
-o jsonpath='{.status.currentHealthy}{" healthy / "}{.status.desiredHealthy}{" desired; disruptionsAllowed="}{.status.disruptionsAllowed}{"\n"}'Con tres pods saludables y minAvailable: 2, el resultado esperado es:
3 healthy / 2 desired; disruptionsAllowed=1El PDB considera saludable únicamente un pod cuya condición Ready sea True.
Comprueba también que el controlador haya procesado la generación actual:
kubectl get pdb web-pdb-demo \
-n donweb-lab \
-o jsonpath='{.metadata.generation}{" generation / "}{.status.observedGeneration}{" observed\n"}'Ambos valores deben coincidir antes de confiar en disruptionsAllowed.
Paso 8: demostrar el cálculo sin realizar un drain
Reduce temporalmente el Deployment a dos réplicas en el laboratorio:
kubectl scale deployment/web-pdb-demo \
--replicas=2 \
-n donweb-lab
kubectl rollout status \
deployment/web-pdb-demo \
-n donweb-labConsulta el PDB:
kubectl get pdb web-pdb-demo \
-n donweb-lab \
-o jsonpath='{.status.currentHealthy}{" healthy / "}{.status.desiredHealthy}{" desired; disruptionsAllowed="}{.status.disruptionsAllowed}{"\n"}'El resultado esperado es:
2 healthy / 2 desired; disruptionsAllowed=0Esta prueba también demuestra una limitación: el escalado directo del Deployment puede reducir réplicas aunque exista un PDB, porque el controlador no utiliza la Eviction API para esa operación.
Restaura el estado:
kubectl scale deployment/web-pdb-demo \
--replicas=3 \
-n donweb-lab
kubectl rollout status \
deployment/web-pdb-demo \
-n donweb-lab \
--timeout=5mPaso 9: probar con kubectl drain
kubectl drain marca el nodo como no programable y puede afectar a todas sus cargas. Ejecuta esta prueba únicamente en un cluster de laboratorio o en un nodo dedicado y revisado.Identifica el nodo de una réplica:
NODE=$(kubectl get pods \
-n donweb-lab \
-l app=web-pdb-demo \
-o jsonpath='{.items[0].spec.nodeName}')
printf 'Nodo seleccionado: %s\n' "$NODE"Revisa todas las cargas del nodo:
kubectl get pods -A \
--field-selector "spec.nodeName=$NODE" \
-o wideInicia el drain controlado:
kubectl drain "$NODE" \
--ignore-daemonsets \
--pod-selector=app=web-pdb-demo \
--timeout=5mEn otra terminal, observa:
kubectl get pods -n donweb-lab -wwatch -n 2 \
kubectl get pdb web-pdb-demo -n donweb-labEl PDB debe permitir una eviction. Si el cluster no tiene otro nodo elegible, el reemplazo quedará Pending y disruptionsAllowed permanecerá en cero.
Devuelve el nodo al scheduler:
kubectl uncordon "$NODE"
kubectl get nodesEspera la recuperación:
kubectl rollout status \
deployment/web-pdb-demo \
-n donweb-lab \
--timeout=5m
kubectl get pdb web-pdb-demo \
-n donweb-labNo utilices kubectl drain --disable-eviction: esa opción cambia a eliminaciones directas y evita la comprobación de los PDB. Referencia de kubectl drain.
minAvailable o maxUnavailable
Solo debes configurar uno de los dos campos.
minAvailable
Define cuántos pods deben permanecer saludables:
spec:
minAvailable: 2Es apropiado cuando existe un mínimo funcional conocido, como el quorum de un sistema distribuido.
maxUnavailable
Define cuántos pods pueden estar simultáneamente no disponibles:
spec:
maxUnavailable: 1Suele adaptarse mejor cuando el Deployment cambia frecuentemente de escala.
Porcentajes y redondeo
También se admiten porcentajes:
minAvailable: "75%"Kubernetes redondea hacia arriba. Con siete réplicas y minAvailable: "50%", exige cuatro disponibles.
maxUnavailable porcentual también se redondea hacia arriba. Esto puede permitir una interrupción superior al porcentaje exacto: con una sola réplica, maxUnavailable: "30%" permite que un pod quede no disponible. Guía oficial del PDB.
Para workloads pequeños o con quorum, los valores enteros suelen ser más previsibles.
Cuidado con los selectores
En policy/v1:
- Un selector omitido o
nullno selecciona pods. - Un selector vacío
{}selecciona todos los pods del namespace. - Un selector con etiquetas incorrectas puede seleccionar cero pods.
Evita:
selector: {}Comprueba siempre:
kubectl get pods \
-n donweb-lab \
-l app=web-pdb-demo \
--show-labelsProblemas frecuentes
disruptionsAllowed permanece en cero
Comprueba:
kubectl describe pdb web-pdb-demo -n donweb-lab
kubectl get pods -n donweb-lab -l app=web-pdb-demo
kubectl get events -n donweb-lab --sort-by=.metadata.creationTimestampPuede faltar un pod Ready, no existir capacidad para el reemplazo o ser demasiado estricto el presupuesto.
El PDB no selecciona ningún pod
Compara el selector del PDB con las etiquetas reales:
kubectl get pdb web-pdb-demo \
-n donweb-lab \
-o jsonpath='{.spec.selector}{"\n"}'
kubectl get pods \
-n donweb-lab \
--show-labelsEl drain queda bloqueado por un pod no saludable
El valor predeterminado IfHealthyBudget puede impedir la eviction mientras la aplicación no recupere la salud suficiente. Evalúa AlwaysAllow después de entender el impacto.
El rollout provoca indisponibilidad
Los controladores Deployment y StatefulSet no están limitados por los PDB durante sus propios rollouts. Configura maxUnavailable, maxSurge, probes y terminación ordenada en el workload.
Un borrado directo ignora el PDB
Es esperado:
kubectl delete pod PODno equivale a una eviction. Los operadores y automatizaciones deben utilizar la Eviction API.
Un PDB con una réplica bloquea el mantenimiento
Con una réplica y minAvailable: 1 o maxUnavailable: 0, no se permite ninguna eviction voluntaria. Para mantener el servicio durante un drain, la aplicación necesita más de una réplica.
Los pods están distribuidos, pero no existe capacidad de reemplazo
La distribución evita una pérdida simultánea, pero no crea recursos libres. Conserva capacidad adicional o permite que el autoscaler incorpore nodos antes del mantenimiento.
Monitoreo recomendado
Supervisa al menos:
currentHealthy.desiredHealthy.disruptionsAllowed.- Pods
Pendingo noReady. - Duración de los drains.
- Evictions rechazadas.
- Distribución por nodo y zona.
- Capacidad libre para reemplazos.
Un PDB permanentemente en cero puede ser una protección válida o una señal de que el cluster no tiene margen operativo.
Cómo revertir el PDB
El rollback del PDB no se realiza con kubectl rollout undo. Ese comando revierte el historial de un Deployment.
Para volver a una versión anterior, aplica el manifiesto previo:
kubectl apply -f pdb-anterior.yamlPara retirar la protección:
kubectl delete -f pdb.yamlComprueba:
kubectl get pdb -n donweb-labEliminar el PDB permite que nuevas evictions continúen sin esa restricción. Coordina la acción con quienes estén realizando mantenimiento.
Para retirar todo el laboratorio:
kubectl delete namespace donweb-lab
Buenas prácticas
- Usa un PDB por aplicación o conjunto coherente de réplicas.
- Evita selectores vacíos o superpuestos.
- Distribuye réplicas entre nodos y, cuando corresponda, zonas.
- Configura readiness probes que representen capacidad real de servicio.
- Mantén recursos suficientes para programar reemplazos.
- Prueba el PDB mediante la Eviction API, no borrando pods.
- Configura por separado la estrategia de rollout.
- No utilices
--disable-evictionpara acelerar mantenimientos. - Documenta quién puede modificar o eliminar el presupuesto.
- Alerta cuando
disruptionsAllowedpermanezca en cero.
Conclusión
Un PodDisruptionBudget no evita que los pods fallen. Su función es regular cuántos pods saludables pueden retirarse simultáneamente mediante interrupciones voluntarias que respetan la Eviction API.
La implementación queda validada cuando el selector coincide con el workload, los pods están realmente distribuidos, disruptionsAllowed refleja el cálculo esperado y un drain controlado permite una eviction sin reducir la aplicación por debajo de su mínimo saludable.