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 → Pod

Si 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.
Disrupciones permitidas.

Escenario de ejemplo

Se desplegarán tres réplicas distribuidas en nodos diferentes y un PDB con:

minAvailable: 2

Cuando los tres pods están saludables:

currentHealthy:      3
desiredHealthy:      2
disruptionsAllowed:  1

Después de una eviction:

currentHealthy:      2
desiredHealthy:      2
disruptionsAllowed:  0

La 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.
  • kubectl configurado 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 wide

Comprueba 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-lab

Confirma también que el campo para pods no saludables esté disponible:

kubectl explain \
  poddisruptionbudget.spec.unhealthyPodEvictionPolicy

Paso 2: crear un namespace de ensayo

kubectl create namespace donweb-lab \
  --dry-run=client \
  -o yaml \
  | kubectl apply -f -

Comprueba:

kubectl get namespace donweb-lab

No 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: 128Mi

En 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-demo

selector.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.yaml

Comprueba las diferencias:

kubectl diff -f deployment.yaml
kubectl diff -f pdb.yaml

kubectl 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.yaml

Comprueba la distribución:

kubectl get pods \
  -n donweb-lab \
  -l app=web-pdb-demo \
  -o wide

Los 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-lab

Obté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=1

El 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-lab

Consulta 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=0

Esta 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=5m

Paso 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 wide

Inicia el drain controlado:

kubectl drain "$NODE" \
  --ignore-daemonsets \
  --pod-selector=app=web-pdb-demo \
  --timeout=5m

En otra terminal, observa:

kubectl get pods -n donweb-lab -w
watch -n 2 \
  kubectl get pdb web-pdb-demo -n donweb-lab

El 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 nodes

Espera la recuperación:

kubectl rollout status \
  deployment/web-pdb-demo \
  -n donweb-lab \
  --timeout=5m

kubectl get pdb web-pdb-demo \
  -n donweb-lab

No 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: 2

Es 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: 1

Suele 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 null no 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-labels

Problemas 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.creationTimestamp

Puede 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-labels

El 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 POD

no 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 Pending o no Ready.
  • 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.yaml

Para retirar la protección:

kubectl delete -f pdb.yaml

Comprueba:

kubectl get pdb -n donweb-lab

Eliminar 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
Eviction de pods.

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-eviction para acelerar mantenimientos.
  • Documenta quién puede modificar o eliminar el presupuesto.
  • Alerta cuando disruptionsAllowed permanezca 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.

Cloud Serversby Donweb

Todo el poder de la nube a tus proyectos y aplicaciones.
Alojamiento ultra rápido, escalable y con alta disponibilidad.

  • Performance que te sorprenderá
  • Rápida escalabilidad y sin limitaciones
  • Arquitectura de alta disponibilidad
  • Soporte experto y ejecutivos de cuenta
  • Pagos en tu moneda y facturación local

Descubre la mejor solución de Cloud Hosting