Kubernetes en producción · Guía práctica · #002

Tu contenedor se reinicia: CrashLoopBackOff no es la causa raíz

CrashLoopBackOff describe el comportamiento de reinicio. Para resolverlo, hay que averiguar qué terminó el proceso y qué ocurrió justo antes.

Por Jorge Lozano · 9 oct 2026 · Diagnóstico de Kubernetes

El incidente

Sale una nueva versión. El Deployment sigue existiendo, pero sus Pods nuevos no llegan a estar disponibles. Uno de ellos muestra:

NAME                              READY   STATUS             RESTARTS
checkout-api-6c8d7f9c6b-k4m2p   0/1     CrashLoopBackOff   7

Los usuarios reciben errores 5xx intermitentes mientras se agotan las réplicas anteriores. La respuesta tentadora es borrar el Pod o aumentar el tiempo entre reinicios. Ninguna de esas acciones explica por qué termina el proceso.

Este es un escenario ilustrativo. La aplicación, el desajuste de configuración y las salidas que aparecen abajo son ejemplos para enseñar una investigación repetible.

Qué te dice el estado

CrashLoopBackOff significa que Kubernetes está reiniciando repetidamente un contenedor que terminó y está demorando el siguiente intento. Describe el patrón de reinicio; no identifica la causa.

El proceso puede terminar por un error de la aplicación, configuración ausente o inválida, una comprobación de dependencias fallida, un OOM kill o una probe de inicio o liveness que lo termina. Cada posibilidad requiere evidencia y una corrección diferente.

Usa este método:

Observar → ubicar la capa que falla → reunir evidencia → formular una hipótesis → probar → corregir → verificar.

Empieza por el workload afectado

Confirma el contexto del clúster y el namespace antes de inspeccionar o cambiar recursos:

kubectl config current-context
kubectl get pods -n <namespace> -o wide
kubectl get deployment checkout-api -n <namespace>
kubectl rollout status deployment/checkout-api -n <namespace>

Comprueba si está afectado un Pod o todas las réplicas nuevas. Un solo Pod puede apuntar a condiciones específicas del nodo o de una instancia; si todas las réplicas fallan después de un rollout, es más probable que haya un cambio compartido de imagen o configuración. Son indicios, no pruebas.

Conserva la evidencia de terminación

Antes de recrear o modificar recursos, reúne la descripción, el estado de la última terminación, los logs previos y los eventos:

kubectl describe pod <pod> -n <namespace>
kubectl get pod <pod> -n <namespace> \
  -o jsonpath='{range .status.containerStatuses[*]}{.name}{" restarts="}{.restartCount}{" lastReason="}{.lastState.terminated.reason}{" exitCode="}{.lastState.terminated.exitCode}{" finishedAt="}{.lastState.terminated.finishedAt}{"\n"}{end}'
kubectl logs <pod> -n <namespace> -c <container> --previous --timestamps --tail=200
kubectl get events -n <namespace> \
  --field-selector involvedObject.kind=Pod,involvedObject.name=<pod> \
  --sort-by=.lastTimestamp

Los logs actuales pueden estar vacíos porque el contenedor ya se reinició. La opción --previous solicita los logs de la instancia que terminó. Guarda esa evidencia antes de recrear el Pod.

No imprimas valores de Secrets mientras revisas configuración. La especificación del Pod puede mostrar los nombres de Secrets y sus claves sin revelar credenciales; comparte la salida de diagnóstico con cuidado.

Sigue la evidencia

Rama A: la aplicación termina por sí sola

Supongamos que los logs de la instancia anterior muestran:

FATAL config: DATABASE_URL is required
process exited with code 2

Inspecciona la plantilla efectiva del Deployment y sus referencias:

kubectl get deployment checkout-api -n <namespace> -o yaml
kubectl get configmap checkout-config -n <namespace> -o yaml

Por ejemplo, la imagen espera DATABASE_URL, pero el Deployment proporciona DB_URL. El proceso inicia, valida la configuración, termina y Kubernetes lo vuelve a iniciar. Es un contrato roto entre la imagen y la configuración de ejecución, no un problema de scheduling de Kubernetes.

Rama B: el último estado es OOMKilled

Revisa lastState.terminated.reason, el request y el límite de memoria del contenedor, además de las condiciones y eventos del nodo. El código de salida 137 por sí solo no demuestra que haya sido un OOM. Distingue un kill por el límite del contenedor de la presión de memoria del nodo con el estado del Pod, los eventos, las condiciones del nodo y la evidencia disponible a nivel de nodo.

Rama C: los eventos muestran fallos de probes y luego un kill

Revisa las probes efectivas startupProbe, livenessProbe y readinessProbe de la plantilla del Pod. Un arranque en frío lento puede ser terminado por liveness antes de completar la inicialización, especialmente si una startup probe no protege ese período. Eventos como Liveness probe failed y Killing respaldan esta hipótesis.

Readiness controla si el Pod recibe tráfico del Service; no reinicia el contenedor. Los fallos de liveness y startup sí pueden causar reinicios. No cambies los umbrales hasta que los eventos y el comportamiento de arranque de la aplicación respalden esa hipótesis.

Diagnóstico y corrección del ejemplo

En el ejemplo, la evidencia señala un desajuste del contrato de configuración: la aplicación requiere DATABASE_URL, pero el workload proporciona una variable con otro nombre.

Corrige el Deployment o su plantilla de origen para mapear la variable esperada a la clave correcta de ConfigMap o Secret. Mantén las credenciales en un Secret o en un gestor externo; no pongas sus valores en un ConfigMap ni en salidas de diagnóstico. Aplica el cambio en la fuente de verdad del equipo —por ejemplo GitOps, valores de Helm o Kustomize— en vez de modificar solo el Pod activo.

Antes del rollout, revisa el cambio renderizado y valídalo contra la API:

kubectl diff -f <rendered-manifests>
kubectl apply --dry-run=server -f <rendered-manifests>

Si existe una revisión conocida como buena y el impacto a clientes está activo, un rollback controlado puede ser la mitigación más segura. Confirma primero la revisión y su configuración, y sigue el procedimiento operativo del equipo. El rollback recupera un estado conocido; no reemplaza la corrección del contrato de configuración.

Verifica la recuperación

kubectl rollout status deployment/checkout-api -n <namespace>
kubectl get pods -n <namespace> -l app=checkout-api
kubectl get endpointslice -n <namespace> \
  -l kubernetes.io/service-name=checkout-api
kubectl logs deployment/checkout-api -n <namespace> --since=10m

Confirma que las réplicas nuevas estén Ready, que el contador de reinicios deje de crecer, que los EndpointSlices incluyan los backends esperados y que se recuperen los health checks y las tasas de error de la aplicación. Que el rollout termine es una señal útil, pero también verifica el recorrido visible para el usuario y los indicadores del servicio.

Errores frecuentes

  • Tratar CrashLoopBackOff como el diagnóstico y no como un síntoma.
  • Borrar el Pod antes de conservar los logs --previous y los detalles de terminación.
  • Asumir que el código de salida 137 por sí solo prueba un OOM kill.
  • Aumentar umbrales de probes sin evidencia de que estén terminando un proceso sano que arranca lento.
  • Editar solo el objeto activo cuando GitOps o el pipeline de despliegue lo sobrescribirá.
  • Declarar recuperación porque el Pod dice Running sin revisar readiness, endpoints y salud de la aplicación.

Prevención

  • Valida en CI las variables de entorno requeridas, las claves de ConfigMap, las referencias a Secrets y los contratos de imagen/runtime.
  • Prueba el inicio con configuración similar a producción y demoras realistas de dependencias.
  • Usa startup, liveness y readiness probes con propósitos distintos y basadas en el comportamiento medido.
  • Despliega gradualmente y alerta por aumento de reinicios, réplicas no disponibles, probes fallidas y OOM kills.
  • Conserva suficientes logs previos y el historial de despliegues para diagnosticar la revisión que falla.
  • Documenta criterios de rollback seguros y mantén alineada la fuente declarativa de verdad.

La lección general

Un reinicio es un resultado, no una explicación. Pregunta qué terminó el proceso, qué ocurrió justo antes y si la evidencia apunta a la aplicación, su configuración, los límites de recursos, las probes o el nodo.

No te quedes en “el Pod se está reiniciando”. Encuentra el evento que causó el reinicio.

Referencias técnicas