Solución de problemas de fallos de Pods en Kubernetes: Una guía completa

Navega por las complejidades de los fallos de Pods en Kubernetes con esta guía completa. Aprende el proceso estructurado para diagnosticar problemas comunes como CrashLoopBackOff, ImagePullBackOff y agotamiento de recursos. Detallamos cómo aprovechar herramientas cruciales como `kubectl describe` y `kubectl logs --previous` para identificar la causa raíz, interpretar los estados de salida de los contenedores e implementar soluciones prácticas para mantener un tiempo de actividad y estabilidad confiables de la aplicación.

Solución de problemas de fallos de Pods en Kubernetes: Una guía completa

La solución de problemas de fallos de Pods en Kubernetes se trata menos de memorizar cada estado y más de aprender dónde Kubernetes deja pistas. Un Pod casi nunca falla "en silencio". El scheduler, kubelet, runtime del contenedor, registro de imágenes, plugin de volúmenes y tu aplicación dejan rastros en diferentes lugares. El truco está en verificarlos en el orden correcto para no pasar veinte minutos leyendo registros de aplicación de un Pod que nunca descargó su imagen.

Normalmente empiezo con una pregunta: ¿falló el Pod antes de que el contenedor se iniciara, mientras el contenedor se iniciaba o después de que la aplicación comenzara a ejecutarse? Esa única división mantiene la investigación fundamentada. Pending generalmente apunta a problemas de programación, almacenamiento o configuración de imagen. ImagePullBackOff apunta a la ruta del registro, etiqueta, credenciales o salida del nodo. CrashLoopBackOff generalmente significa que el proceso se inicia y luego sale, aunque la razón podría ser configuración, un archivo faltante, un comando incorrecto, una dependencia fallida o presión de memoria.


Los Tres Pilares del Diagnóstico de Pods

La solución de problemas comienza utilizando tres comandos principales de kubectl para recopilar toda la información disponible sobre el Pod con fallos.

1. Verificación de Estado Inicial (kubectl get pods)

El primer paso siempre es determinar el estado actual del Pod y sus contenedores. Presta mucha atención a las columnas STATUS y READY.

kubectl get pods -n my-namespace

Interpretación del Estado del Pod

Estado Significado Acción Inicial
Running Al menos un contenedor se está ejecutando; esto no siempre significa que la aplicación esté sirviendo tráfico. Verificar READY, reinicios y eventos de readiness.
Pending El Pod ha sido aceptado por Kubernetes pero los contenedores aún no se han creado. Verificar eventos de programación o estado de descarga de imagen.
CrashLoopBackOff El contenedor se inicia, falla y Kubelet intenta reiniciarlo repetidamente. Verificar registros de la aplicación (kubectl logs --previous).
ImagePullBackOff Kubelet no puede descargar la imagen del contenedor requerida. Verificar nombre de imagen, etiqueta y credenciales del registro.
Error El Pod salió debido a un error de runtime o un comando de inicio fallido. Verificar registros y eventos de describe.
Terminating/Unknown El Pod se está apagando o el host de Kubelet no responde. Verificar salud del nodo.

2. Inspección Profunda (kubectl describe pod)

Si el estado es diferente de Running, el comando describe proporciona contexto crucial, detallando decisiones de programación, asignación de recursos y estados de los contenedores.

kubectl describe pod [NOMBRE_DEL_POD] -n my-namespace

Concéntrate en estas secciones de la salida:

  • Containers/Init Containers: Verifica el State (especialmente Waiting o Terminated) y el Last State (donde a menudo se registra la razón del fallo, por ejemplo, Reason: OOMKilled).
  • Resource Limits: Verifica que Limits y Requests estén configurados correctamente.
  • Events: Esta es la sección más crítica. Los eventos muestran el historial del ciclo de vida, incluyendo fallos de programación, problemas de montaje de volúmenes e intentos de descarga de imágenes.

Consejo: Si la sección Events muestra un mensaje como "0/N nodes available", es probable que el Pod no se pueda programar debido a recursos insuficientes (CPU, memoria) o reglas de afinidad no cumplidas.

Lee los eventos de abajo hacia arriba cuando quieras la pista más reciente, pero no ignores los eventos más antiguos. Un Pod puede tener más de un problema. Por ejemplo, un deployment puede comenzar con FailedScheduling porque la memoria solicitada es demasiado alta, y luego pasar a ImagePullBackOff después de agregar un nodo. Si solo miras el estado final, puedes perderte el cambio que hizo avanzar el problema.

3. Revisión de Registros (kubectl logs)

Para problemas de runtime de la aplicación, los registros proporcionan el stack trace o mensajes de error que explican por qué terminó el proceso.

# Verificar registros actuales del contenedor
kubectl logs [NOMBRE_DEL_POD] -n my-namespace

# Verificar registros de un contenedor específico dentro del Pod
kubectl logs [NOMBRE_DEL_POD] -c [NOMBRE_DEL_CONTENEDOR] -n my-namespace

# Crucial para CrashLoopBackOff: Verificar los registros de la ejecución fallida *anterior*
kubectl logs [NOMBRE_DEL_POD] --previous -n my-namespace

Si el Pod tiene sidecars, siempre incluye -c. Muchas investigaciones frustrantes provienen de leer los registros del sidecar saludable en lugar del contenedor de la aplicación con fallos. Para fallos de init containers, usa el nombre del init container con -c también:

kubectl logs [NOMBRE_DEL_POD] -c [NOMBRE_DEL_INIT_CONTAINER] -n my-namespace

Escenarios Comunes de Fallos de Pods y Soluciones

La mayoría de los fallos de Pods caen en unos pocos patrones reconocibles, cada uno requiere un enfoque de diagnóstico específico.

Escenario 1: CrashLoopBackOff

Este es el fallo de Pod más frecuente. Significa que el contenedor se está iniciando correctamente, pero la aplicación dentro del contenedor está saliendo inmediatamente (con un código de salida distinto de cero).

Diagnóstico:

  1. Usa kubectl logs --previous para ver el traceback o la razón de salida.
  2. Usa kubectl describe para verificar el Exit Code en la sección Last State.

Causas y Soluciones Comunes:

  • Código de Salida 1/2: Error general de la aplicación, archivo de configuración faltante, fallo de conectividad de base de datos o fallo de la aplicación debido a entrada incorrecta.
    • Solución: Depura el código de la aplicación o verifica los ConfigMaps/Secrets montados.
  • Dependencias Faltantes: El script de punto de entrada requiere archivos o entornos que no están presentes.
    • Solución: Verifica el Dockerfile y el proceso de construcción de la imagen.
  • Comando o args incorrectos: La imagen del contenedor es válida, pero el comando en la especificación del Pod anula incorrectamente el entrypoint de la imagen.
    • Solución: Compara el command y args del Deployment con el comando de inicio esperado de la imagen. Prueba la misma imagen localmente si es posible.
  • Reinicios inducidos por sondas: Una sonda de liveness puede matar una aplicación de inicio lento antes de que termine de calentar.
    • Solución: Aumenta initialDelaySeconds, usa un startupProbe, o apunta la sonda a un endpoint de salud más ligero.

Un patrón práctico es desplegar una copia temporal con la misma imagen pero un comando inofensivo, luego inspeccionar el sistema de archivos y el entorno:

kubectl run debug-image \
  --image=registry.example.com/app:tag \
  --restart=Never \
  --command -- sleep 3600

kubectl exec -it debug-image -- /bin/sh

Esto no reemplaza la corrección del Deployment, pero ayuda a responder preguntas simples rápidamente: ¿el archivo de configuración está realmente en la imagen?, ¿existe el binario?, ¿tiene el contenedor el shell esperado?, ¿están presentes las variables de entorno?

Escenario 2: ImagePullBackOff / ErrImagePull

Esto ocurre cuando Kubelet no puede obtener la imagen del contenedor especificada en la definición del Pod.

Diagnóstico:

  1. Verifica la sección Events de kubectl describe para el mensaje de error específico (por ejemplo, 404 Not Found o authentication required).

Causas y Soluciones Comunes:

  • Error tipográfico o Etiqueta Incorrecta: El nombre o la etiqueta de la imagen son incorrectos.
    • Solución: Corrige el nombre de la imagen en la especificación del Deployment o del Pod.
  • Acceso a Registro Privado: El clúster no tiene credenciales para descargar desde un registro privado (como Docker Hub, GCR, ECR).
    • Solución: Asegúrate de que se haga referencia a un imagePullSecret apropiado en la especificación del Pod y que el Secret exista en el namespace.
# Ejemplo de fragmento de especificación de Pod para usar un pull secret
spec:
  containers:
  ...
  imagePullSecrets:
  - name: my-registry-secret

También verifica dónde reside el pull secret. Los secrets de Kubernetes tienen ámbito de namespace. Un secret llamado regcred en default no ayudará a un Pod en payments. Si la misma imagen funciona en un namespace pero falla en otro, compara las service accounts y los image pull secrets antes de asumir que el registro está roto:

kubectl get serviceaccount default -n payments -o yaml
kubectl get secret regcred -n payments

Escenario 3: Estado Pending (Atascado)

Un Pod permanece en estado Pending, lo que generalmente indica que no se puede programar en un Nodo o que está esperando recursos (como un PersistentVolume).

Diagnóstico:

  1. Ejecuta kubectl describe y mira la sección Events.

Causas y Soluciones Comunes:

  • Agotamiento de Recursos: El clúster carece de Nodos con suficiente CPU o Memoria disponible para satisfacer las requests del Pod.
    • Solución: Aumenta el tamaño del clúster, o reduce las solicitudes de recursos del Pod (si es factible).
    • Ejemplo de Mensaje de Evento: 0/4 nodes are available: 4 Insufficient cpu.
  • Problemas de Vinculación de Volúmenes: El Pod requiere un PersistentVolumeClaim (PVC) que no se puede vincular a un PersistentVolume (PV) subyacente.
    • Solución: Verifica el estado del PVC (kubectl get pvc) y asegúrate de que el StorageClass esté funcionando.
  • Desajuste de selector o afinidad: El Pod solicita una etiqueta de nodo que no existe, o una regla de afinidad requerida excluye todos los nodos.
    • Solución: Compara nodeSelector, nodeAffinity y las etiquetas de los nodos con kubectl get nodes --show-labels.
  • Taints no tolerados: Los nodos están disponibles, pero repelen este Pod porque carece de una tolerancia coincidente.
    • Solución: Agrega la tolerancia prevista al Pod, o elimina el taint si ya no representa una regla de ubicación real.

Escenario 4: OOMKilled (Muerto por Falta de Memoria)

Aunque esto generalmente resulta en un estado CrashLoopBackOff, la causa subyacente es específica: el contenedor usó más memoria que el límite definido en su especificación, lo que provocó que el sistema operativo host (a través de Kubelet) lo terminara forzosamente.

Diagnóstico:

  1. Verifica kubectl describe -> Last State -> Reason: OOMKilled.

Soluciones:

  1. Aumentar Límites: Aumenta el limit de memoria en la especificación del Pod, proporcionando más margen.
  2. Optimizar la Aplicación: Perfila la aplicación para reducir su huella de memoria.
  3. Establecer Requests: Asegúrate de que las requests estén cerca del uso real en estado estable para mejorar la confiabilidad de la programación.
resources:
  limits:
    memory: "512Mi" # Aumenta este valor
  requests:
    memory: "256Mi"

Ten cuidado con las "soluciones" de memoria que solo aumentan el límite. Si la aplicación tiene una fuga, un límite más alto solo puede retrasar el siguiente fallo y hacer que el nodo asuma más riesgo. Observa la memoria a lo largo del tiempo en tu sistema de métricas. Un patrón de dientes de sierra que vuelve a la línea base después de la recolección de basura es diferente de un aumento constante hasta OOM.

Escenario 5: CreateContainerConfigError y CreateContainerError

Estos estados son fáciles de pasar por alto porque no suenan como fallos de aplicación. Generalmente significan que kubelet no pudo ensamblar la configuración del contenedor.

Las causas comunes incluyen:

  • Un ConfigMap o Secret referenciado no existe en el namespace.
  • Una clave dentro de un ConfigMap o Secret está mal escrita.
  • Una ruta de montaje de volumen entra en conflicto con otro montaje.
  • El contenedor hace referencia a un contexto de seguridad no válido.

La verificación más rápida sigue siendo describe:

kubectl describe pod [NOMBRE_DEL_POD] -n my-namespace

Busca mensajes de evento como secret "app-config" not found o configmap "settings" not found. Luego verifica el objeto:

kubectl get secret app-config -n my-namespace
kubectl get configmap settings -n my-namespace -o yaml

Este es un error común en los pipelines de despliegue. El manifiesto de la aplicación se aplica, pero el paso de creación del secret falló o se ejecutó contra el namespace incorrecto.

Escenario 6: Running pero No Listo

Un Pod puede mostrar Running mientras sigue siendo inutilizable. La columna READY te dice cuántos contenedores están listos según sus sondas de readiness. Un Pod con 1/2 o 0/1 puede estar vivo pero eliminado de los endpoints del Service.

Verifica los endpoints cuando el tráfico falla pero los Pods parecen vivos:

kubectl get endpoints [NOMBRE_DEL_SERVICE] -n my-namespace
kubectl describe pod [NOMBRE_DEL_POD] -n my-namespace

Si la lista de endpoints está vacía, el problema puede ser una sonda de readiness, un desajuste del selector del Service, o la aplicación escuchando en un puerto diferente al que espera el Service. En incidentes reales, aquí es donde la gente pierde tiempo: siguen reiniciando Pods aunque los Pods no son la razón por la que falta el tráfico.


Previniendo Fallos Futuros: Mejores Prácticas

Las aplicaciones robustas requieren una configuración cuidadosa para prevenir errores comunes de despliegue.

Usa Sondas de Liveness y Readiness

La implementación adecuada de sondas permite a Kubernetes gestionar inteligentemente la salud del contenedor:

  • Sondas de Liveness: Determinan si el contenedor está lo suficientemente saludable para seguir ejecutándose. Si la sonda de liveness falla, Kubelet reiniciará el contenedor (resolviendo bloqueos suaves).
  • Sondas de Readiness: Determinan si el contenedor está listo para servir tráfico. Si la sonda de readiness falla, el Pod se elimina de los endpoints del servicio, evitando solicitudes fallidas mientras el contenedor se recupera.

No apuntes las sondas de liveness a verificaciones de dependencias profundas a menos que realmente quieras que Kubernetes reinicie el contenedor cada vez que esa dependencia tenga una interrupción corta. Una base de datos que no está disponible durante treinta segundos generalmente no es prueba de que el proceso esté muerto. Readiness es el mejor lugar para decir "no envíes tráfico a este Pod ahora mismo".

Aplica Límites y Solicitudes de Recursos

Siempre define los requisitos de recursos para los contenedores. Esto evita que los Pods consuman recursos excesivos (lo que lleva a inestabilidad del nodo) y asegura que el scheduler pueda colocar el Pod en un Nodo con capacidad suficiente.

Utiliza Init Containers para la Configuración

Si un Pod requiere una verificación de dependencia o configuración de datos antes de que la aplicación principal se inicie (por ejemplo, esperar a que se complete una migración de base de datos), usa un Init Container. Si el Init Container falla, el Pod lo reiniciará repetidamente, aislando limpiamente los errores de configuración de los errores de runtime de la aplicación.

Un Flujo de Triage Práctico

Cuando estés de guardia, usa un camino repetible:

  1. Verifica kubectl get pods -n <namespace> -o wide para ver el estado, reinicios, antigüedad y ubicación del nodo.
  2. Ejecuta kubectl describe pod y lee Events, State, Last State, montajes y configuraciones de recursos.
  3. Obtén los registros con kubectl logs, agregando --previous para contenedores reiniciados y -c para Pods con múltiples contenedores.
  4. Si el Pod está Pending, inspecciona la programación, taints, etiquetas de nodo y PVCs antes de leer los registros de la aplicación.
  5. Si el Pod está Running pero no recibe tráfico, inspecciona las sondas de readiness, selectores de Service y endpoints.
  6. Si el Pod fue OOMKilled, compara los límites con los gráficos de memoria reales antes de simplemente aumentar el número.

Este orden evita que saltes directamente a la aplicación cuando Kubernetes ni siquiera la ha iniciado todavía.

Verificación Final

El hábito más útil es separar los síntomas de las causas. CrashLoopBackOff es un síntoma. La causa podría ser un secret faltante, una mala migración, una sonda de liveness o un límite de memoria. Pending es un síntoma. La causa podría ser solicitudes de CPU, un PVC, un taint o un selector de nodo. Deja que el estado del Pod te diga dónde mirar, luego deja que los eventos y los registros te digan qué cambió.