El Comportamiento Esperado y el Error CrashLoopBackOff

Al implementar una aplicación en Kubernetes (K8s), el comportamiento esperado es que el Pod pase de Pending directamente a Running, y que los contenedores en su interior permanezcan activos. Sin embargo, uno de los errores más comunes y notorios que se encuentran es CrashLoopBackOff.

Este estado significa que un contenedor dentro del Pod se inicia, falla instantáneamente, y Kubernetes intenta reiniciarlo continuamente—con retrasos crecientes (backoffs) entre cada intento (10s, 20s, 40s, hasta 5 minutos).

A diferencia de un estado Pending (que implica un problema de infraestructura o programación), un CrashLoopBackOff te dice explícitamente que el nodo ha programado el Pod, extraído la imagen y ejecutado el contenedor, pero la aplicación en su interior salió voluntariamente o fue eliminada a la fuerza.

Requisitos Previos

Antes de sumergirte en los pasos de solución de problemas, asegúrate de tener:

  • Un Cluster Kubernetes en ejecución (Minikube, EKS, GKE, AKS o bare metal).
  • La herramienta de línea de comandos kubectl instalada y configurada para apuntar a tu cluster.
  • El nombre exacto del Pod que muestra el error y el espacio de nombres en el que reside.

Causa del Problema del CrashLoopBackOff

Dado que CrashLoopBackOff es un síntoma más que la causa exacta, debemos buscar la razón subyacente. Los culpables más comunes incluyen:

  1. Pánico de la Aplicación/Error Fatal: El código encuentra una excepción no controlada o falta una dependencia inmediatamente al iniciarse (por ejemplo, fallo al conectar a una base de datos) y sale explícitamente.
  2. Configuración/Secretos Faltantes: La aplicación espera una variable de entorno mapeada desde un ConfigMap o Secret que no existe o tiene un error tipográfico.
  3. Fallos del Liveness Probe: Tu livenessProbe falla consecutivamente, causando que Kubernetes interprete que el contenedor no está saludable y lo mate repetidamente para reiniciarlo.
  4. OOMKilled (Sin Memoria): El contenedor intenta asignar más memoria de la que le permite su limits.memory, lo que provoca que el kernel de Linux termine el proceso.
  5. Entrypoint/Comando Inválido: El comando especificado en el YAML o en el CMD/ENTRYPOINT del Dockerfile es sintácticamente incorrecto, carece de permisos o sale inmediatamente porque no es un proceso en primer plano.

Solución Paso a Paso

1. Ver la Descripción del Pod

El primer comando que debes ejecutar es describe. Esto te dará el Exit Code exacto del fallo.

kubectl describe pod <pod-name> -n <namespace>

Desplázate hacia abajo hasta la sección Containers y mira el bloque Last State:

    Last State:     Terminated
      Reason:       Error
      Exit Code:    1
      Started:      Fri, 27 Feb 2026 10:00:00 GMT
      Finished:     Fri, 27 Feb 2026 10:00:02 GMT

Códigos de Salida Comunes:

  • Exit Code 1: Error general de la aplicación (mira los logs de tu código).
  • Exit Code 2: Mal uso de los comandos integrados de la shell (revisa tu comando de Dockerfile).
  • Exit Code 126: El comando invocado no se puede ejecutar (error de permisos).
  • Exit Code 128: Argumento de salida no válido.
  • Exit Code 137: OOMKilled (Out of Memory - el contenedor alcanzó su límite).
  • Exit Code 255: Estado de salida fuera de rango (usualmente un fallo de inicialización fatal).

2. Revisar los Logs Anteriores

Dado que el contenedor está fallando y reiniciándose activamente, el comando normal kubectl logs podría devolver un resultado vacío si consultas justo cuando el contenedor se inicia de nuevo. En su lugar, usa la bandera --previous para obtener los logs del contenedor muerto justo antes de ser eliminado:

kubectl logs <pod-name> -n <namespace> --previous

Esto es muy efectivo para capturar los trazos de la pila, conexiones faltantes a bases de datos o errores “File Not Found” que causaron el pánico.

3. Comprobar OOMKilled

Si el Código de Salida es 137 o la salida de describe dice literalmente OOMKilled, la solución es aumentar los límites de memoria de tu contenedor.

Edita tu YAML de implementación para aumentar el límite de memoria:

resources:
  requests:
    memory: "256Mi"
    cpu: "250m"
  limits:
    memory: "512Mi"  # <-- Aumenta este valor
    cpu: "500m"

Aplica los cambios usando kubectl apply -f deployment.yaml.

4. Sobrescribir el Entrypoint para la Depuración (Solución Alternativa)

Si la salida del registro es críptica o inexistente (por ejemplo, salida inmediata del script debido a permisos), una excelente manera de depurar con seguridad es sobrescribir el command con un proceso de suspensión en la terminal. Esto obliga al contenedor a mantenerse activo el tiempo suficiente para que puedas ingresar a él.

Modifica temporalmente tu manifiesto de Deployment:

containers:
  - name: my-crashing-app
    image: my-registry/my-app:v1
    command: ["sleep", "3600"] # Obliga al pod a mantenerse vivo

Aplica el manifiesto. El Pod ahora pasará a Running y se quedará ahí. Entonces, ejecuta con seguridad (exec) para entrar en él:

kubectl exec -it <pod-name> -- /bin/sh

Una vez dentro, puedes ejecutar el script de tu aplicación manualmente (npm start, python app.py, etc.) y observar el error en tiempo real, inspeccionar archivos o verificar las variables de entorno.


Prevención

Para reducir la incidencia de CrashLoopBackOff en tus clusters de producción, implementa las siguientes mejores prácticas:

  • Implementar Correctamente Pruebas de Preparación y Actividad (Liveness/Readiness Probes): Asegúrate de que tu livenessProbe le da a la aplicación suficiente initialDelaySeconds para que arranque antes de empezar a hacerle ping.
  • Uso de Contenedores de Inicio (Init Containers): Si tu aplicación depende de una base de datos o un servicio externo que debe estar disponible, usa un initContainer para esperar a ese servicio antes de iniciar la aplicación principal, previniendo efectivamente el pánico.
  • Validar las Dependencias de Configuración: Garantiza que tu pipeline de CI/CD valide que los ConfigMaps y Secrets referenciados en tus Deployments realmente existen antes de aplicar los manifiestos.

Resumen

  • CrashLoopBackOff indica que un contenedor arrancó pero se detuvo/murió repetidamente.
  • Usa kubectl describe pod para localizar el Exide Code.
  • Usa kubectl logs --previous para leer el trazo de la pila de la última iteración fallada.
  • El código 137 significa que se han alcanzado los límites de memoria (OOMKilled).
  • Sobrescribe el comando del deployment a sleep si necesitas acceder por consola vía SSH manualmente en el pod para depurar el sistema de archivos o temas de permiso.

Artículos Relacionados