Errores de Programación en Kubernetes Explicados: Soluciones y Mejores Prácticas
¡Domina la programación de Kubernetes! Esta guía desmitifica por qué los Pods se quedan atascados en el estado 'Pending'. Aprende a diagnosticar errores usando `kubectl describe`, resolver problemas relacionados con CPU/Memoria insuficientes, superar restricciones de Node Affinity y utilizar correctamente Taints y Tolerations para una colocación robusta de cargas de trabajo.
Errores de Programación en Kubernetes Explicados: Soluciones y Mejores Prácticas
Los errores de programación en Kubernetes suelen manifestarse como un pod atascado en Pending. Ese estado puede parecer vago, pero tiene un significado específico: Kubernetes ha aceptado el objeto pod, pero el programador no ha encontrado un nodo que satisfaga los requisitos del pod. El contenedor no se ha estrellado. La aplicación no ha iniciado. En muchos casos, la imagen ni siquiera se ha descargado aún.
La forma más rápida de resolver estos problemas es comparar lo que el pod solicita con lo que el clúster puede ofrecer. Las solicitudes de CPU y memoria, las etiquetas de nodo, las reglas de afinidad, los taints, las tolerations, los volúmenes persistentes, las reglas de distribución de topología y las cuotas de espacio de nombres pueden bloquear la colocación. El programador es estricto con las restricciones requeridas. Si una regla requerida excluye todos los nodos, el pod espera.
Diagnosticando Pods Pendientes: El Primer Paso
Antes de intentar soluciones, debes diagnosticar con precisión por qué el Programador está fallando. La herramienta principal para esta investigación es kubectl describe pod.
Cuando un Pod está atascado en Pending, la sección Events de la salida de describe contiene información crítica que detalla el proceso de decisión de programación y cualquier rechazo.
Usando kubectl describe pod
Siempre apunta al Pod problemático:
kubectl describe pod <nombre-del-pod> -n <espacio-de-nombres>
Examina la salida, mirando específicamente la sección Events al final. Los mensajes aquí generalmente indicarán la restricción que impidió la programación. Los mensajes comunes se relacionan con Insufficient cpu, Insufficient memory, discrepancias en el selector de nodo, taints no tolerados o enlace de volumen.
Categorías Comunes de Errores de Programación y Soluciones
Los fallos de programación generalmente caen en tres categorías principales: Restricciones de Recursos, Restricciones de Política (Affinity/Anti-Affinity) y Configuración de Nodo (Taints/Tolerations).
1. Restricciones de Recursos (Recursos Insuficientes)
Esta es la causa más frecuente. El Programador requiere un Nodo que pueda satisfacer las solicitudes definidas en la especificación del Pod. Si ningún nodo tiene suficiente CPU o Memoria asignable disponible, el Pod permanecerá en Pending.
Identificando el Problema
La sección Events mostrará mensajes como:
0/3 nodes are available: 3 Insufficient cpu.0/3 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate, 2 node(s) didn't match node selector.
Estos mensajes pueden combinarse. No te detengas en la primera frase. Si tres nodos fallan por tres razones diferentes, arreglar solo una razón puede dejar el pod aún pendiente.
Soluciones para la Escasez de Recursos
- Reducir Solicitudes del Pod: Si las solicitudes del Pod son excesivamente altas, intenta reducir las solicitudes de CPU o Memoria
requestsen el YAML del Pod o Deployment. - Aumentar la Capacidad del Clúster: Agrega más Nodos al clúster de Kubernetes.
- Limpiar Cargas de Trabajo Existentes: Reduce la escala de cargas de trabajo no esenciales, elimina trabajos abandonados o ajusta solicitudes sobredimensionadas en despliegues existentes. Usa
kubectl drainpara mantenimiento de nodos, no como un comando de limpieza casual. - Usar Limit Ranges: Si tu espacio de nombres carece de límites de recursos definidos, implementa objetos
LimitRangepara evitar que Pods individuales acaparen recursos.
2. Selectores de Nodo y Reglas de Affinity/Anti-Affinity
Kubernetes permite un control detallado sobre dónde pueden o deben colocarse los Pods usando nodeSelector, nodeAffinity y podAffinity/podAntiAffinity.
Discrepancia en el Selector de Nodo
Si defines un nodeSelector que no coincide con ninguna etiqueta presente en ningún Nodo disponible, el Pod no puede programarse.
Fragmento de YAML de Ejemplo (Causa de Fallo):
spec:
nodeSelector:
disktype: ssd-fast
containers: [...] # El Pod permanece en Pending si ningún nodo tiene disktype=ssd-fast
Solución: Asegúrate de que la etiqueta especificada en nodeSelector exista en al menos un Nodo (kubectl get nodes --show-labels) y que la coincidencia de mayúsculas/minúsculas sea exacta.
Usa verificaciones de etiquetas dirigidas cuando el clúster tenga muchas etiquetas:
kubectl get nodes -L disktype,topology.kubernetes.io/zone
kubectl describe node <nombre-del-nodo>
Un error común es usar una etiqueta que existía en un grupo de nodos anterior pero no en el grupo de nodos de reemplazo. Después de una actualización del clúster o una migración del grupo de autoescalado, las reglas de colocación antiguas pueden volverse imposibles silenciosamente.
Restricciones de Node Affinity
nodeAffinity ofrece reglas más flexibles (por ejemplo, requiredDuringSchedulingIgnoredDuringExecution o preferredDuringSchedulingIgnoredDuringExecution). Si una regla required no puede cumplirse, el Pod permanece en Pending.
Consejo de Diagnóstico: Al usar reglas de afinidad complejas, la sección Events a menudo indica: node(s) didn't match node selector.
Pod Affinity y Anti-Affinity
Estas reglas controlan la colocación en relación con otros Pods. Si, por ejemplo, una regla de Anti-Affinity requiere que un Pod no se ejecute en un Nodo que aloja un servicio específico, pero todos los nodos ya alojan ese servicio, la programación fallará.
Solución: Revisa cuidadosamente la clave de topología y el selector en tus reglas de afinidad. Si una regla de anti-afinidad es demasiado restrictiva, relaja el requisito o verifica que los Pods objetivo seleccionados por la regla se estén ejecutando realmente en los nodos que deseas evitar.
Prefiere preferredDuringSchedulingIgnoredDuringExecution cuando la regla exprese una preferencia en lugar de un requisito estricto. La anti-afinidad requerida es útil para distribuir réplicas de servicios críticos, pero puede bloquear despliegues en clústeres pequeños. Por ejemplo, tres réplicas con anti-afinidad estricta de una por zona no pueden programarse limpiamente en un clúster con solo dos zonas utilizables.
3. Taints y Tolerations
Los Taints se aplican directamente a los Nodos para repeler Pods, mientras que las Tolerations se agregan a las especificaciones de los Pods para permitirles ingresar a nodos con taints.
- Taint: Repela Pods a menos que tengan una toleration coincidente.
- Toleration: Permite que un Pod se programe en un nodo con un taint coincidente.
Identificando el Rechazo por Taint
Los Events indicarán explícitamente la razón del rechazo:
0/3 nodes are available: 2 node(s) had taint {dedicated: special-workload, effect: NoSchedule}, that the pod didn't tolerate.
Soluciones para Taints y Tolerations
Tienes dos caminos principales:
Modificar el Pod (Recomendado para Pods de Aplicación): Agrega las
tolerationsrequeridas a la especificación del Pod que coincidan con el taint del nodo.Ejemplo de Toleration:
spec: tolerations: - key: "dedicated" operator: "Equal" value: "special-workload" effect: "NoSchedule" containers: [...]Modificar el Nodo (Recomendado para Administradores de Clúster): Elimina el taint del Nodo si la restricción ya no es necesaria.
# Para eliminar un taint kubectl taint nodes <nombre-del-nodo> dedicated:special-workload:NoSchedule-
Alerta de Mejor Práctica: Evita tolerar el taint global
node-role.kubernetes.io/master:NoScheduleen Pods de aplicación a menos que estés programando intencionalmente componentes críticos del plano de control en los nodos maestros.
En clústeres más nuevos, los nodos del plano de control comúnmente usan el taint node-role.kubernetes.io/control-plane en lugar de, o junto con, la terminología de maestro anterior. Verifica los taints reales antes de copiar una toleration de un manifiesto antiguo:
kubectl describe node <nombre-del-nodo> | grep -i taints
Restricciones Avanzadas de Programación
Restricciones menos comunes, pero importantes, también pueden bloquear la programación:
Restricciones de Volumen de Almacenamiento
Si un Pod solicita un PersistentVolumeClaim (PVC) que actualmente no puede enlazarse a un Nodo disponible (por ejemplo, debido a requisitos específicos del proveedor de almacenamiento o falta de disponibilidad del volumen), el Pod puede permanecer en Pending.
Diagnóstico: Verifica primero el estado del PVC (kubectl describe pvc <nombre-del-pvc>). Si el PVC está atascado en Pending, la programación del Pod se detiene hasta que el volumen esté disponible.
El almacenamiento también puede retrasarse intencionalmente por volumeBindingMode: WaitForFirstConsumer en la StorageClass. En ese modo, el enlace espera hasta que el programador elija un nodo adecuado, porque el volumen puede necesitar crearse en la misma zona que el pod. Eso es normal, pero si ningún nodo satisface las restricciones del pod y del almacenamiento juntos, el pod permanece pendiente.
DaemonSets y Topology Spreads
Los DaemonSets solo se programarán en nodos que coincidan con sus criterios de selección (si los hay). Si un clúster está particionado o un nuevo nodo no coincide con el selector del DaemonSet, no se ejecutará.
Restricciones de Topology Spread (si están definidas) aseguran una distribución uniforme. Si la distribución actual impide la colocación en cualquier nodo mientras se respetan las restricciones de dispersión, la programación fallará.
Los fallos de dispersión de topología a menudo aparecen después de una interrupción parcial. Supongamos que una zona no está disponible y un despliegue tiene restricciones estrictas de dispersión entre zonas. Kubernetes puede negarse a colocar nuevas réplicas en las zonas restantes porque hacerlo violaría la regla de sesgo. Ese comportamiento protege los objetivos de distribución, pero durante una interrupción puede ser necesario relajar temporalmente la restricción para restaurar la capacidad.
Cuotas de Espacio de Nombres y LimitRanges
Un pod también puede ser bloqueado por la política del espacio de nombres. ResourceQuota controla el uso agregado en un espacio de nombres. LimitRange puede establecer valores predeterminados o mínimos y máximos de recursos.
Verifícalos cuando una especificación de pod parezca razonable pero la creación o programación aún falle:
kubectl get resourcequota -n <espacio-de-nombres>
kubectl describe resourcequota -n <espacio-de-nombres>
kubectl get limitrange -n <espacio-de-nombres>
kubectl describe limitrange -n <espacio-de-nombres>
Los problemas de cuota son comunes en clústeres de desarrollo compartidos. Un equipo puede tener suficiente capacidad física de clúster, pero su cuota de espacio de nombres está agotada por entornos de vista previa antiguos o trabajos completados que nunca se limpiaron.
Una Secuencia de Depuración Realista
Cuando un pod está pendiente, usa este orden:
- Ejecuta
kubectl describe pody copia el evento de programación más reciente. - Verifica la CPU y memoria solicitadas contra la capacidad asignable del nodo con
kubectl describe node. - Verifica las etiquetas de nodo si el pod usa
nodeSelector, node affinity o claves de topología. - Verifica los taints en los nodos candidatos y las tolerations en el pod.
- Verifica los PVCs y StorageClasses si el pod monta almacenamiento persistente.
- Verifica las cuotas de espacio de nombres y LimitRanges.
- Si se espera que Cluster Autoscaler ayude, inspecciona sus registros o eventos.
Este orden importa porque un pod pendiente no es un problema de tiempo de ejecución de la aplicación. Reiniciar el despliegue rara vez ayuda a menos que la restricción subyacente haya cambiado.
Mejores Prácticas para una Programación Exitosa
Para minimizar problemas de programación, adopta estas mejores prácticas operativas:
- Define Solicitudes de Recursos Explícitamente: Siempre establece
requests(ylimitsopcionales) razonables para CPU y memoria. Esto permite al programador evaluar con precisión la capacidad del nodo. - Usa Etiquetas de Nodo para Zonificación: Implementa un etiquetado de nodos consistente (por ejemplo,
hardware=gpu,zone=us-east-1a) y usanodeSelectoronodeAffinitypara dirigir cargas de trabajo al hardware apropiado. - Documenta Taints y Tolerations: Si los nodos tienen taints por mantenimiento o segregación de hardware, documenta estos taints centralmente. Asegúrate de que los manifiestos de aplicación que requieren acceso a recursos con taints incluyan las tolerations correspondientes.
- Monitorea el Cluster Autoscaler (si se usa): Si dependes de soluciones de escalado, asegúrate de que sean funcionales. Una falta de capacidad que debería desencadenar escalado podría estar fallando silenciosamente, dejando Pods pendientes.
- Revisa los Registros del Programador (Avanzado): Para inmersiones de diagnóstico profundas, revisa los registros del componente
kube-scheduler. En clústeres administrados, el acceso puede variar según el proveedor, así que comienza con eventos de pod y registros del plano de control específicos del proveedor.
Arregla la Restricción, No el Síntoma
La solución correcta depende de si la restricción es accidental o intencional. Si el pod solicita 8 CPUs porque alguien copió un manifiesto de producción en un pequeño clúster de staging, reduce la solicitud para ese entorno. Si el pod necesita una GPU y no existe ningún nodo GPU, agregar una toleration no ayudará; el clúster necesita el hardware adecuado. Si un taint protege los nodos de base de datos de cargas de trabajo generales, no elimines el taint solo para hacer que un pod no relacionado se programe.
Para cambios en producción, haz visible la razón en Git. Las etiquetas de nodo, taints, reglas de afinidad y solicitudes de recursos son contratos de colocación. Los operadores futuros necesitan saber si una regla existe por rendimiento, cumplimiento, acceso a hardware, control de costos o simple accidente histórico.
Ejemplos de Soluciones Rápidas Engañosas
Varias soluciones comunes hacen que el estado Pending inmediato desaparezca mientras crean un problema peor más adelante.
Reducir las solicitudes de CPU puede ayudar si la solicitud original estaba inflada, pero no es una herramienta de capacidad gratuita. Si la aplicación realmente necesita esa CPU durante el tráfico pico, el pod puede programarse y luego rendir mal bajo carga. Verifica el historial de uso y la latencia antes de recortar las solicitudes agresivamente.
Agregar una toleration amplia puede hacer que un pod se programe, pero puede aterrizar en nodos reservados para otro propósito. Una toleration dice "este pod está permitido aquí". No dice "este pod debería preferir aquí". Si necesitas tanto permiso como intención, combina tolerations con node affinity o node selectors.
Eliminar una regla de anti-afinidad puede restaurar réplicas rápidamente, pero puede colocar cada réplica en un solo nodo o una sola zona. Eso a veces es aceptable durante una interrupción, pero debería ser un cambio temporal consciente, no una deriva permanente silenciosa.
Expandir el clúster es a menudo la respuesta correcta, pero solo después de saber que el pod pendiente puede usar los nuevos nodos. Si el pod requiere una etiqueta que el grupo de nodos autoescalado no tendrá, agregar nodos solo te da más nodos inadecuados.
Verificación Final
Un pod pendiente es un fallo de negociación entre el pod y el clúster. El pod solicita recursos, etiquetas, almacenamiento, topología y permiso para aterrizar en ciertos nodos. El clúster responde con capacidad, taints, etiquetas, cuotas y volúmenes disponibles. kubectl describe pod muestra dónde falló esa negociación. Una vez que leas el evento cuidadosamente, la mayoría de las soluciones se vuelven directas: cambiar los requisitos del pod, cambiar la capacidad disponible del clúster o corregir la política que ya no coincide con la realidad.