Pending significa que el pod existe como objeto pero ningún nodo lo aceptó. No hay contenedor, no hay imagen que bajar y no hay aplicación que falle. Es una negociación entre lo que el pod pide y lo que el cluster tiene, y el planificador te dice exactamente en qué no se pusieron de acuerdo.
`kubectl describe pod <nombre>` y directo a Events. El planificador deja un mensaje del tipo «0/12 nodes are available: 8 Insufficient memory, 4 node(s) had untolerated taint».
Ese mensaje es una cuenta completa: de doce nodos, ocho no tienen memoria suficiente y cuatro tienen una marca que este pod no tolera. No hay que adivinar nada, y cada categoría apunta a un arreglo distinto.
Si el mensaje dice «0/0 nodes are available», el cluster no tiene nodos disponibles en absoluto y el problema es del autoescalador, no del pod.
**Insufficient cpu / Insufficient memory.** Ningún nodo tiene libre lo que el pod pide en `requests`. Ojo con la trampa: lo que se compara es el request, no el consumo real. Un nodo puede estar al 30% de uso real y estar «lleno» para el planificador porque los pods que ya tiene reservaron todo.
**Untolerated taint.** Los nodos que quedan tienen una marca —dedicados a otra carga, con GPU, en modo de mantenimiento— y este pod no trae la tolerancia. Se resuelve agregando la tolerancia o quitando la marca, y conviene entender por qué estaba antes de quitarla.
**Node affinity / selector no coincide.** El pod pide una etiqueta que ningún nodo tiene. Suele ser una zona que ya no existe, un tipo de instancia que se dejó de usar, o una etiqueta escrita con un guion de diferencia.
**PVC sin enlazar.** El pod espera un volumen que no se pudo crear. `kubectl describe pvc` lo explica: puede ser una StorageClass que no existe, o un volumen en una zona distinta a la del único nodo con espacio. Un pod con un volumen de una zona no puede correr en otra.
**Topology spread o anti-afinidad demasiado estrictas.** Una regla que exige un pod por zona en un cluster con dos zonas y tres réplicas deja la tercera en Pending para siempre. Con `requiredDuringScheduling` es un bloqueo permanente; con `preferred` el planificador lo relaja.
**El autoescalador no puede agregar nodos.** Cuota de la cuenta agotada, el tipo de instancia sin capacidad en esa zona, o un límite máximo en el grupo. Los eventos del autoescalador lo dicen y hay que mirarlos aparte de los del pod: tanto el Cluster Autoscaler como Karpenter registran por qué no pudieron aprovisionar, y ese mensaje no aparece en los eventos del pod.
Cuando alguien pone `requests` muy por encima del consumo real —«por si acaso», o copiando el manifiesto de otro servicio— pasan dos cosas malas a la vez.
La primera es que aparecen pods en Pending sin motivo real: el planificador cree que los nodos están llenos porque suma reservas, no uso. Un cluster al 30% de utilización efectiva puede rechazar pods.
La segunda es que el autoescalador reacciona a esa presión ficticia y **agrega nodos que nadie va a usar**. Terminas pagando capacidad para sostener reservas que no corresponden a trabajo real.
Es una de las fuentes de sobregasto más frecuentes en trabajos de FinOps, y tiene la propiedad incómoda de verse como un problema de capacidad: el equipo pide más nodos y el gasto sube, cuando lo que había que revisar era el manifiesto.
La forma de detectarlo es comparar reservado contra usado por namespace. Si la brecha es grande y sostenida, el arreglo es dimensionar los requests y no agregar nodos.
Ese dimensionamiento —el rightsizing— se puede automatizar en parte con el Vertical Pod Autoscaler, que recomienda requests a partir del consumo observado. Conviene usarlo primero en modo recomendación y no en modo automático: un VPA que ajusta solo puede reiniciar pods en el peor momento, y el objetivo era estabilidad, no otra fuente de reinicios.
Con autoescaladores que eligen el tipo de instancia según precio y disponibilidad, como Karpenter, el Pending se vuelve más dinámico y a veces más confuso.
El comportamiento normal es que un pod quede unos segundos en Pending mientras se aprovisiona un nodo que le calce. Eso no es una falla: es el sistema funcionando.
Se vuelve falla cuando el pod pide algo que ningún tipo de instancia disponible ofrece —una combinación de CPU y memoria que no existe en el catálogo, o una arquitectura para la que no hay capacidad—, y ahí el autoescalador intenta y no consigue. Sus propios eventos lo explican.
Y un efecto secundario: nodos que aparecen y desaparecen a mayor velocidad hacen que el cobro por host de las herramientas de observabilidad crezca mucho más rápido, porque cada nodo que existió cuenta.
Un servicio que se desplegaba bien y ahora queda en Pending suele haber cambiado en una de tres cosas: subieron sus requests en un commit, se agregó una regla de afinidad o de distribución, o el cluster perdió capacidad porque otro equipo desplegó algo grande.
Las dos primeras están en el manifiesto y tienen fecha. La tercera no está en tu servicio en absoluto, y es la más difícil de ver desde adentro: tu pod no calza por algo que hizo otro.
Distinguir «cambió mi manifiesto» de «cambió el cluster» es lo que evita perseguir el problema en el lugar equivocado.
Moonin mantiene el inventario continuo de recursos del cluster y el historial de revisiones de cada servicio, incluidos los requests y las reglas de programación. Ver que las requests de tu servicio subieron en la revisión de ayer, o que otro namespace creció, son dos datos que están en el mismo lugar.
Eso responde la pregunta de si el cambio fue tuyo o del cluster, que es la bifurcación donde se pierde más tiempo.
Lo que no hace: no ajusta requests, no agrega nodos y no modifica reglas de programación. La única mutación posible en todo el producto es el ajuste de HPA y réplicas por el Scaling Rules Agent, que es opcional y viene desactivado.
La demo está abierta y sin formulario: puedes revisar cómo se ve el inventario de recursos con lo reservado contra lo usado, antes de instalar nada.
ConócenosLicenciamientoSeguridad y permisosPrograma de partnersvs Datadogvs Prometheus y Grafanavs New Relicvs Dynatracevs ELK y ElasticsearchCrashLoopBackOffOOMKilledImagePullBackOffDocumentaciónDemoRegístrateContáctanosArguz, la consultora
El planificador lo escribe. `kubectl describe pod <nombre>` y la sección Events trae un mensaje del tipo «0/12 nodes are available: 8 Insufficient memory, 4 node(s) had untolerated taint». Eso es una cuenta completa por categoría, y cada categoría apunta a un arreglo distinto.
Porque el planificador compara `requests`, no consumo real. Si los pods que ya están en el nodo reservaron toda la memoria, el nodo está lleno para el planificador aunque esté casi vacío en la práctica. El arreglo es dimensionar las requests al consumo real, no agregar nodos.
Sí, sobre todo con autoescalado. Un pod que espera mientras se aprovisiona un nodo que le calce está en Pending por diseño. Se vuelve un problema cuando se queda ahí: si pasa minutos, los eventos del pod y del autoescalador dicen qué no se pudo conseguir.
Suele ser una regla de distribución o anti-afinidad demasiado estricta: por ejemplo exigir un pod por zona en un cluster de dos zonas con tres réplicas. Con `requiredDuringSchedulingIgnoredDuringExecution` el bloqueo es permanente. Cambiarla a `preferred` deja que el planificador la relaje cuando no puede cumplirla.
Sí, y de forma indirecta. Requests infladas hacen que el planificador vea nodos llenos que están vacíos, el autoescalador agrega nodos para aliviar una presión que no existe, y terminas pagando capacidad que nadie usa. Es una de las fuentes de sobregasto más frecuentes, y se disfraza de problema de capacidad.