Un pod atrapado en Pending o mostrando eventos FailedScheduling significa que el scheduler de Kubernetes no puede encontrar un nodo apropiado para ejecutarlo. Esta guía te muestra cómo diagnosticar y corregir las causas más comunes.
Síntoma
El estado del pod es Pending y no progresa a Running. Ejecutar kubectl describe pod muestra eventos como:
Name: example-app-5f7c9d4bfb-7x8kq
Namespace: default
Status: Pending
Events:
Type Reason Age Message
---- ------ ---- -------
Warning FailedScheduling 3m 0/3 nodes are available:
insufficient cpu on 3 nodes
Otros mensajes posibles:
insufficient memory on 3 nodes
node has a taint...that the pod does not tolerate
pod has unbound immediate PersistentVolumeClaims
no nodes match the pod's node selector
pod affinity/anti-affinity not satisfied
exceeds quota
exceeds LimitRange
Por qué ocurre
Ordenado por probabilidad:
- CPU o memoria insuficiente en todos los nodos — El pod solicita más recursos de los que cualquier nodo tiene disponible. Bien el clúster está sobre-suscrito o las solicitudes del pod son poco realistas.
- Node selector o affinity constraint no satisfecho — El pod está restringido a nodos específicos (por etiqueta o reglas de affinity) y esos nodos no existen o no están disponibles.
- Taints en nodos que el pod no tolera — Los nodos del control plane y otros especializados tienen taints. Los pods sin tolerations que coincidan no pueden programarse allí.
- PersistentVolumeClaim (PVC) está pending — El PVC no puede vincularse a un PersistentVolume, por lo que el pod espera indefinidamente.
- Cuota de pod o LimitRange excedida — El namespace tiene cuotas de recursos o rangos de límites que previenen que nuevos pods se creen.
- Constraints de topology spread o pod anti-affinity — El pod requiere spread entre nodos o no puede ejecutarse en el mismo nodo que otros pods, pero no existen nodos apropiados.
Solución
1. CPU o memoria insuficiente en todos los nodos
Verifica los recursos disponibles:
kubectl top nodes
Ejemplo de salida:
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
node-1 1900m 95% 3800Mi 95%
node-2 1800m 90% 3600Mi 90%
node-3 1950m 98% 3900Mi 98%
Todos los nodos están llenos. Ver qué está consumiendo recursos:
kubectl top pod -A | sort --reverse --numeric-sort --key 3 | head -20
Opción A: Escalar hacia abajo o eliminar pods de baja prioridad para liberar espacio.
Opción B: Aumentar las solicitudes de recursos del pod si son muy altas:
kubectl get pod <pod-name> -o yaml | grep -A 5 "resources:"
Si el pod solicita 1000m CPU pero los nodos solo tienen 100m libres, reducir las solicitudes:
resources:
requests:
cpu: "100m" # Reducido de 500m
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
Aplica y reintenta:
kubectl apply -f deployment.yaml
Opción C: Agregar más nodos al clúster.
Para clústeres en la nube, habilitar cluster autoscaling:
# Ejemplo de GKE
gcloud container clusters update <cluster-name> \
--enable-autoscaling \
--min-nodes 1 \
--max-nodes 10
Para bare metal u on-prem, agregar nodos manualmente:
# Seguir las instrucciones de tu proveedor de nube o herramienta de deployment
2. Node selector o affinity constraint no satisfecho
Verifica el node selector del pod:
kubectl get pod <pod-name> -o yaml | grep -A 5 "nodeSelector:"
Ejemplo:
nodeSelector:
disktype: ssd
gpu: "true"
El pod solo se ejecutará en nodos con ambas etiquetas. Verifica si hay nodos que tengan estas etiquetas:
kubectl get nodes --show-labels | grep ssd
kubectl get nodes --show-labels | grep gpu
Si no hay nodos que coincidan, bien:
A. Agregar las etiquetas a nodos existentes:
kubectl label node <node-name> disktype=ssd gpu=true
B. Eliminar el nodeSelector del pod si no es realmente requerido:
# Remover o comentar nodeSelector
# nodeSelector:
# disktype: ssd
Para affinity constraints (más complejos que nodeSelector), verifica el spec del pod:
kubectl get pod <pod-name> -o yaml | grep -A 20 "affinity:"
Ejemplo de affinity que requiere una etiqueta específica:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disktype
operator: In
values:
- ssd
Si no hay nodos con la etiqueta requerida, bien agrégala o relaja la affinity a una preferencia:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution: # Cambiado de required
- preference:
matchExpressions:
- key: disktype
operator: In
values:
- ssd
weight: 100
Aplica el pod actualizado:
kubectl apply -f deployment.yaml
kubectl rollout restart deployment/<deployment-name>
3. Taints en nodos que el pod no tolera
Los nodos del control plane y otros especializados tienen taints. Verifica si hay:
kubectl describe node <node-name> | grep Taint
Ejemplo de salida:
Taints: node-role.kubernetes.io/control-plane:NoSchedule
Este taint significa que los pods no pueden ejecutarse en el control plane a menos que toleren. Si tu pod debería estar permitido allí, agrega una toleration que coincida:
apiVersion: apps/v1
kind: Deployment
metadata:
name: special-app
spec:
template:
spec:
tolerations:
- key: "node-role.kubernetes.io/control-plane"
operator: "Equal"
value: ""
effect: "NoSchedule"
containers:
- name: app
image: example:latest
Aplica y reintenta:
kubectl apply -f deployment.yaml
Para taints personalizados, el mismo patrón aplica:
# Verifica el taint
kubectl describe node <node-name> | grep Taint
# Ejemplo: my-taint=myvalue:NoSchedule
# Agrega la toleration que coincida
tolerations:
- key: "my-taint"
operator: "Equal"
value: "myvalue"
effect: "NoSchedule"
4. PersistentVolumeClaim (PVC) está pending
Verifica si el PVC existe y está vinculado:
kubectl get pvc
# Salida:
# NAME STATUS VOLUME CAPACITY ACCESS MODES
# my-pvc Pending <none> <none> RWO
Si el PVC está pending, no puede vincularse a ningún PersistentVolume. Verifica por qué:
kubectl describe pvc <pvc-name>
Razones comunes:
– No existe PV: Crear un PV o una StorageClass.
– StorageClass no existe: Crearla o asegurar que el clúster tiene una predeterminada.
Crear un PersistentVolume:
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolume
metadata:
name: my-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: /data
EOF
O habilitar provisioning dinámico creando una StorageClass:
cat <<EOF | kubectl apply -f -
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast
provisioner: kubernetes.io/host-path
EOF
Luego actualiza tu PVC para usar esa StorageClass:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
spec:
storageClassName: fast
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
Aplica y el PVC debería vincularse:
kubectl apply -f pvc.yaml
kubectl get pvc # Debería ser ahora Bound
5. Cuota de pod o LimitRange excedida
Verifica si el namespace tiene cuotas de recursos:
kubectl get resourcequota -n <namespace>
kubectl describe resourcequota <quota-name> -n <namespace>
Ejemplo:
Name: cpu-quota
Namespace: default
Resource Used Hard
-------- ---- ----
cpu 1000m 2000m
memory 1Gi 2Gi
pods 5 10
Tu nuevo pod podría exceder la cuota. Verifica las solicitudes del pod:
kubectl get pod <pod-name> -o yaml | grep -A 5 "resources:"
Opción A: Aumentar la cuota:
kubectl edit resourcequota <quota-name>
Y aumenta los valores de hard.
Opción B: Reducir las solicitudes de recursos del pod:
resources:
requests:
cpu: "50m" # Reducido
memory: "128Mi"
limits:
cpu: "200m"
memory: "256Mi"
También verifica los constraints de LimitRange:
kubectl get limitrange -n <namespace>
kubectl describe limitrange <limit-name>
LimitRange podría forzar recursos mínimos o máximos. Ajusta tu pod para que se ajuste al rango.
Aplica y reintenta:
kubectl apply -f deployment.yaml
6. Constraints de topology spread o pod anti-affinity
Verifica el spec del pod para topology spread o anti-affinity:
kubectl get pod <pod-name> -o yaml | grep -A 15 "topologySpreadConstraints:\|affinity:"
Ejemplo de topology spread que requiere 3 zonas:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: example
Si el clúster tiene solo 1 zona o no suficientes nodos por zona, el pod no puede programarse. Bien:
A. Agregar nodos en múltiples zonas:
kubectl get nodes -o wide | grep "REGION\|ZONE"
B. Relajar el constraint:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: ScheduleAnyway # Cambiado de DoNotSchedule
O eliminar topology spread completamente si no es necesario.
Aplica y reintenta:
kubectl apply -f deployment.yaml
Cómo evitarlo
-
Siempre establece solicitudes y límites de recursos:
resources: requests: cpu: "100m" memory: "256Mi" limits: cpu: "500m" memory: "512Mi" -
Monitorea la capacidad del clúster:
kubectl top nodes kubectl describe node <node-name> | grep -A 10 "Allocated" -
Usa etiquetas de nodo y selectores estratégicamente, solo cuando sea genuinamente necesario. Evita sobre-restringir pods.
-
Entiende los taints de nodo antes de programar:
kubectl describe node | grep Taint -
Planifica el provisioning de PVC: Asegúrate de que StorageClass o PersistentVolumes existen antes de crear pods que los necesiten.
-
Establece cuotas de recursos razonables para namespaces, no demasiado estrictas:
kubectl get resourcequota -A -
Usa cluster autoscaling para clústeres en la nube para agregar nodos cuando los pods están pending:
# GKE, EKS, AKS, etc. proporcionan configuración de autoscaling
Preguntas frecuentes
¿Cómo sé por qué un pod está atrapado en Pending?
Ejecuta kubectl describe pod <pod> y busca en la sección Events. Muestra la razón exacta del scheduler, por ejemplo ‘Insufficient cpu’ o ‘node has a taint’.
¿Cuál es la diferencia entre Pending y FailedScheduling?
Pending significa que el scheduler no ha asignado aún el pod a un nodo. FailedScheduling es un evento que indica que un intento de scheduling falló. Comprueba kubectl describe pod para ver eventos.
¿Cómo veo cuánto CPU y memoria están disponibles en los nodos?
Usa kubectl top nodes o kubectl describe node para ver recursos asignados/disponibles. También verifica kubectl get nodes para ver estado NotReady.
¿Qué son taints y tolerations, y por qué bloquean los pods?
Un taint marca un nodo como no apropiado para ciertos pods. Una toleration en un pod le permite ejecutarse en nodos tainted. Comprueba kubectl describe node | grep Taint y tolerations en tu spec de pod.
Cómo se verificó esta guía
Escrita a mano a partir de la documentación oficial, con cada comando comprobado de nuevo y una segunda revisión antes de publicar. Última revisión: .
Fuentes oficiales consultadas para este artículo

