Los pods de Kubernetes pueden fallar de muchas maneras, y cada estado de fallo cuenta una historia diferente. Ya sea que enfrentes CrashLoopBackOff, ImagePullBackOff, Pending, OOMKilled u otros estados de error, saber cómo diagnosticar y corregir estos problemas de forma sistemática es esencial para cualquier operador de Kubernetes. Esta guía te lleva a través de los estados de fallo más comunes, los comandos kubectl para diagnosticarlos y estrategias probadas para resolver cada uno.

Requisitos Previos

  • Un clúster de Kubernetes en ejecución (v1.24 o posterior recomendado)
  • kubectl instalado y configurado con acceso al clúster
  • Conocimiento básico de objetos de Kubernetes (pods, deployments, services)
  • Permisos para leer pods, eventos y recursos de nodos en tu namespace objetivo
  • Familiaridad con conceptos de contenedores (imágenes, registros, límites de recursos)

Entendiendo el Ciclo de Vida de los Pods y sus Estados

Antes de profundizar en la solución de problemas, es útil entender el ciclo de vida de un pod en Kubernetes. Un pod pasa por varias fases:

FaseDescripción
PendingEl pod fue aceptado por el clúster pero uno o más contenedores aún no están ejecutándose
RunningEl pod está asignado a un nodo y todos los contenedores iniciaron
SucceededTodos los contenedores terminaron exitosamente (código de salida 0)
FailedTodos los contenedores terminaron y al menos uno salió con error
UnknownNo se puede determinar el estado del pod, generalmente por fallo en la comunicación con el nodo

Dentro de estas fases, los contenedores pueden entrar en estados de espera específicos que indican qué salió mal. Estos son los mensajes de estado que ves en la salida de kubectl get pods — y son tu primera pista diagnóstica.

Estados Comunes de Fallo de Pods

CrashLoopBackOff

CrashLoopBackOff es el fallo de pod más común que encontrarás. Significa que el contenedor inicia, falla y Kubernetes sigue reiniciándolo con retrasos crecientes (10s, 20s, 40s, hasta 5 minutos).

Causas comunes:

  • Error de aplicación que causa salida inmediata (configuración faltante, excepción no manejada)
  • Variables de entorno o secrets montados faltantes
  • Comando o entrypoint incorrecto en el spec del contenedor
  • Health check (liveness probe) fallando demasiado agresivamente
  • Dependencia de un servicio que no está disponible

Comandos de diagnóstico:

# Verificar el estado del pod y conteo de reinicios
kubectl get pods -o wide

# Ver los logs del último contenedor
kubectl logs <nombre-pod> --previous

# Verificar eventos del pod
kubectl describe pod <nombre-pod>

El flag --previous es crítico — sin él, puedes obtener logs vacíos o parciales porque la nueva instancia del contenedor acaba de iniciar. La salida de describe muestra el Last State con el código de salida, que te indica si el proceso falló (código de salida 1) o fue terminado (código de salida 137 para OOM, 143 para SIGTERM).

ImagePullBackOff

ImagePullBackOff significa que Kubernetes no puede descargar la imagen del contenedor desde el registro. El pod permanece en este estado, reintentando con backoff exponencial.

Causas comunes:

  • Error tipográfico en el nombre o tag de la imagen
  • El tag de la imagen no existe (ej: referenciando latest cuando solo se publican tags versionados)
  • imagePullSecrets faltantes o expirados
  • Registro privado sin credenciales configuradas
  • Network policy o firewall bloqueando acceso al registro
  • Límites de tasa del registro (throttling de Docker Hub)

Comandos de diagnóstico:

# Verificar la referencia exacta de la imagen
kubectl describe pod <nombre-pod> | grep -A 5 "Image:"

# Buscar errores de pull en eventos
kubectl get events --field-selector involvedObject.name=<nombre-pod>

# Verificar que imagePullSecrets existan
kubectl get secrets -n <namespace>

Pending

Un pod en estado Pending significa que el scheduler aún no lo ha asignado a un nodo. Esto puede persistir indefinidamente si el problema subyacente no se resuelve.

Causas comunes:

  • CPU o memoria insuficiente en todos los nodos
  • Node selectors, taints o affinities que ningún nodo satisface
  • PersistentVolumeClaim (PVC) no enlazado — no hay PersistentVolume coincidente disponible
  • ResourceQuota excedido en el namespace
  • Demasiados pods en el clúster (límite max-pods en nodos)

Comandos de diagnóstico:

# Verificar por qué el pod está pendiente
kubectl describe pod <nombre-pod>

# Ver eventos del scheduler
kubectl get events --sort-by='.lastTimestamp' -n <namespace>

# Verificar recursos del nodo
kubectl describe nodes | grep -A 5 "Allocated resources"

# Verificar estado del PVC si el pod usa volúmenes
kubectl get pvc -n <namespace>

OOMKilled

OOMKilled (código de salida 137) significa que el OOM killer del kernel de Linux terminó el contenedor porque excedió su límite de memoria.

Causas comunes:

  • Límite de memoria configurado demasiado bajo para la aplicación
  • Fuga de memoria (memory leak) en la aplicación
  • Tamaño del heap de JVM no alineado con el límite de memoria del contenedor
  • Contenedores sidecar consumiendo memoria compartida
  • Carga de grandes conjuntos de datos en memoria

Comandos de diagnóstico:

# Verificar la razón de terminación
kubectl describe pod <nombre-pod> | grep -A 3 "Last State"

# Ver uso actual de memoria (requiere metrics-server)
kubectl top pod <nombre-pod>

# Verificar límites configurados
kubectl get pod <nombre-pod> -o jsonpath='{.spec.containers[*].resources}'

Otros Estados de Fallo

EstadoSignificadoSolución Típica
CreateContainerConfigErrorConfigMap o Secret faltanteVerifica que los ConfigMaps y Secrets referenciados existan
RunContainerErrorFallo del runtime del contenedorRevisa el security context, montajes de volúmenes y logs del runtime en el nodo
EvictedNodo bajo presión de recursosVerifica las condiciones de presión de disco/memoria del nodo y establece requests adecuados
Init:ErrorInit container fallóRevisa los logs del init container con kubectl logs <pod> -c <init-container>
Terminating (atascado)Finalizers bloqueando eliminaciónVerifica los finalizers con kubectl get pod -o json y elimínalos si es seguro

Diagnosticando Problemas con kubectl

El flujo de diagnóstico sigue un patrón consistente sin importar el tipo de fallo:

Paso 1: Obtener la Vista General

# Todos los pods en el namespace con estado
kubectl get pods -n <namespace> -o wide

# Eventos recientes ordenados por tiempo
kubectl get events --sort-by='.lastTimestamp' -n <namespace> | tail -20

Paso 2: Profundizar en el Pod

# Descripción completa del pod con eventos
kubectl describe pod <nombre-pod> -n <namespace>

# Logs del contenedor (instancia actual)
kubectl logs <nombre-pod> -n <namespace>

# Logs del contenedor (instancia previa que falló)
kubectl logs <nombre-pod> -n <namespace> --previous

# Logs de un contenedor específico en un pod multi-contenedor
kubectl logs <nombre-pod> -c <nombre-contenedor> -n <namespace>

Paso 3: Verificar el Nodo

# Condiciones del nodo (presión de disco, memoria, PIDs)
kubectl describe node <nombre-nodo> | grep -A 10 "Conditions"

# Asignación de recursos en el nodo
kubectl describe node <nombre-nodo> | grep -A 20 "Allocated resources"

Paso 4: Depuración Interactiva

# Exec en un contenedor en ejecución
kubectl exec -it <nombre-pod> -- /bin/sh

# Usar contenedor de depuración efímero (K8s 1.23+)
kubectl debug -it <nombre-pod> --image=busybox --target=<nombre-contenedor>

# Ejecutar un pod de depuración en el mismo namespace de red
kubectl run debug --rm -it --image=busybox -- /bin/sh

Comparativa de Comandos kubectl

Estado de FalloPrimer ComandoInformación Clave
CrashLoopBackOffkubectl logs <pod> --previousSalida de error de la aplicación antes del fallo
ImagePullBackOffkubectl describe pod <pod>Nombre de imagen, errores de pull, referencias de secrets
Pendingkubectl describe pod <pod>Razón de fallo del scheduler en la sección Events
OOMKilledkubectl describe pod <pod>Razón de terminación en Last State y código de salida
CreateContainerConfigErrorkubectl get configmap,secret -n <ns>Recursos referenciados faltantes
Evictedkubectl describe node <node>Condiciones de presión de recursos del nodo
Init:Errorkubectl logs <pod> -c <init-container>Logs de fallo del init container

Escenario del Mundo Real

Administras un clúster de producción ejecutando una aplicación de microservicios. Después de un despliegue, el pod payment-service sigue reiniciándose y muestra CrashLoopBackOff. Así es como lo diagnosticas:

$ kubectl get pods -n production
NAME                              READY   STATUS             RESTARTS   AGE
payment-service-7d4f8b9c6-x2k9m  0/1     CrashLoopBackOff   5          8m
api-gateway-5c8f7d6b4-h3j7n      1/1     Running            0          2d
user-service-6b7c8d9e5-m4n8p     1/1     Running            0          2d

Revisas los logs del contenedor anterior:

$ kubectl logs payment-service-7d4f8b9c6-x2k9m --previous
2026-02-28 10:15:03 ERROR: Failed to connect to database
  ConnectionRefused: tcp://db-service:5432
2026-02-28 10:15:03 FATAL: Cannot start without database connection. Exiting.

La aplicación requiere una conexión a la base de datos al iniciar, pero db-service no es alcanzable. Verificas el servicio:

$ kubectl get svc db-service -n production
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
db-service   ClusterIP   10.96.45.123   <none>        5432/TCP   30d

$ kubectl get endpoints db-service -n production
NAME         ENDPOINTS   AGE
db-service   <none>      30d

Sin endpoints — el pod de la base de datos está caído. Descubres que fue desalojado por presión de disco en el nodo:

$ kubectl get pods -n production | grep db
db-postgresql-0   0/1     Evicted   0   30d

La solución: liberar espacio en disco del nodo (o agregar más nodos), reiniciar el pod de la base de datos, y el servicio de pagos se recupera automáticamente. También agregas un startupProbe con un timeout generoso para que el servicio de pagos espere a la base de datos en lugar de fallar inmediatamente.

Errores Comunes y Casos Especiales

  • Código de salida 137 vs 143: El código de salida 137 significa que el contenedor fue terminado por SIGKILL (generalmente OOMKilled). El código 143 significa SIGTERM (apagado graceful). No los confundas — 137 requiere investigación de memoria, 143 es generalmente normal durante rollouts.

  • Retardo de CrashLoopBackOff: Kubernetes usa backoff exponencial hasta 5 minutos entre reinicios. Si corriges el problema, puede que necesites esperar o eliminar el pod para reiniciarlo inmediatamente.

  • ImagePullPolicy: Always: Si tu spec del pod usa imagePullPolicy: Always (el valor predeterminado para tags latest), cada reinicio del pod desencadena un pull de imagen. Esto puede causar ImagePullBackOff si el registro está temporalmente inalcanzable, aunque la imagen estuviera previamente cacheada en el nodo.

  • Resource requests vs limits: Los pods se programan basándose en requests, no en limits. Un pod que solicita 100Mi pero tiene límite de 500Mi puede recibir OOMKilled a 500Mi incluso si el nodo tiene 2Gi libres — el límite se aplica sin importar la capacidad del nodo.

  • Pods multi-contenedor: En un pod con sidecars, kubectl logs usa por defecto el primer contenedor. Siempre especifica -c <nombre-contenedor> al depurar pods multi-contenedor.

  • Desalojos por almacenamiento efímero: Incluso si CPU y memoria están bien, un alto uso de almacenamiento efímero (logs, archivos temporales) puede desencadenar desalojo. Verifica con kubectl describe node en la sección Conditions.

  • PVC en zona de disponibilidad incorrecta: En entornos cloud, un PVC enlazado a un volumen en us-east-1a no puede ser montado por un pod programado en un nodo en us-east-1b. El pod queda en Pending sin un error obvio.

  • Retardo en resolución DNS: Los servicios recién creados pueden no resolver inmediatamente. Si tu contenedor falla porque no puede resolver un nombre de servicio, agrega un retardo de inicio o lógica de reintentos en lugar de depender de la propagación instantánea de DNS.

Resumen

  • CrashLoopBackOff significa que tu contenedor sigue fallando — revisa los logs con --previous para ver el error antes del fallo
  • ImagePullBackOff indica un fallo al descargar la imagen — verifica el nombre de imagen, tag y credenciales del registro
  • Pending significa que el scheduler no puede colocar el pod — revisa la disponibilidad de recursos, estado del PVC y reglas de afinidad
  • OOMKilled (código de salida 137) significa que el contenedor excedió su límite de memoria — aumenta los límites u optimiza el uso de memoria
  • Siempre comienza con kubectl describe pod y kubectl get events para entender el contexto del fallo
  • Usa kubectl debug para contenedores efímeros cuando necesites depuración interactiva sin modificar el spec del pod
  • Configura requests, limits y probes adecuados para prevenir muchos fallos comunes de pods antes de que ocurran

Artículos Relacionados