Liveness probe failed: por qué Kubernetes reinicia tu Pod que sí funciona
La columna RESTARTS sube sola, a veces termina en CrashLoopBackOff, y cuando
entras al contenedor la aplicación responde bien. Casi nunca es la aplicación: es la sonda de vida, que la mata
antes de tiempo o le pregunta lo que no debe.
Paso 1. Confirmar que el reinicio lo pidió la sonda
$ kubectl get pods -n <ns>
NAME READY STATUS RESTARTS AGE
tienda-7c9f8d6b5d-x2kqp 1/1 Running 4 (40s ago) 6m
$ kubectl describe pod tienda-7c9f8d6b5d-x2kqp -n <ns> | grep -A3 "Last State"
$ kubectl get events -n <ns> --field-selector reason=Unhealthy
Si en Events aparece Liveness probe failed seguido de "failed liveness probe, will be
restarted", el reinicio lo ordenó el kubelet por la sonda. Si en cambio Last State dice
OOMKilled, el problema es de memoria y la sonda no tiene la culpa.
Paso 2. Leer el texto exacto del fallo
El final del mensaje dice cuál de las causas tienes:
| Lo que dice el evento | Causa más probable | Corrección |
|---|---|---|
connection refused solo en los primeros segundos | La aplicación tarda en arrancar más de lo que la sonda espera | Una startupProbe que proteja el arranque (YAML corregido, abajo) |
context deadline exceeded o Client.Timeout exceeded | La respuesta tarda más de timeoutSeconds, que por defecto es 1 segundo; pasa con carga o con el límite de CPU muy bajo | Sube timeoutSeconds y revisa el límite de CPU del contenedor |
connection refused siempre | Puerto equivocado, o la aplicación escucha en 127.0.0.1 | Usa el puerto por nombre (port: http) y escucha en 0.0.0.0 |
HTTP probe failed with statuscode: 404 | La ruta de salud no existe; solo cuentan como éxito los códigos de 200 a 399 | Corrige path o crea la ruta |
statuscode: 500 o 503 cuando falla la base de datos | La sonda de vida revisa dependencias externas, y una caída ajena reinicia todos tus Pods a la vez | La sonda de vida solo mira el proceso; las dependencias van en la readinessProbe |
Paso 3. Medir antes de cambiar números
$ kubectl logs tienda-7c9f8d6b5d-x2kqp -n <ns> --previous | tail -20
$ kubectl get pod tienda-7c9f8d6b5d-x2kqp -n <ns> -o jsonpath='{.spec.containers[0].livenessProbe}'
--previous muestra los registros del contenedor que mataron, no del que acaba de nacer: ahí ves cuánto
tardó en quedar listo. Con los valores por defecto (periodSeconds: 10, failureThreshold: 3,
timeoutSeconds: 1), una aplicación que tarda más de unos 30 segundos en arrancar entra en un ciclo de
reinicios sin fin.
El YAML corregido
containers:
- name: tienda
ports:
- name: http
containerPort: 8080
startupProbe: # protege el arranque: hasta 30 x 5 = 150 s
httpGet: { path: /healthz, port: http }
periodSeconds: 5
failureThreshold: 30
livenessProbe: # empieza cuando la de arranque pasa
httpGet: { path: /healthz, port: http }
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
readinessProbe: # aquí sí van las dependencias
httpGet: { path: /ready, port: http }
periodSeconds: 5
Mientras la startupProbe no pase, Kubernetes no ejecuta la de vida ni la de disponibilidad. Así el
arranque lento deja de contar como fallo, y la sonda de vida puede ser estricta el resto del tiempo.
¿Quieres verlo pasar sin romper nada tuyo? El laboratorio 03 del kit rompe una sonda a propósito, te muestra la versión vieja sirviendo mientras el rollout se congela, y en el reto final rompe la sonda de vida para comparar los dos síntomas.
Por qué pasa tanto
Porque la sonda de vida se copia de un tutorial con la misma ruta que la de disponibilidad, y las dos hacen cosas opuestas: la de disponibilidad saca al Pod del reparto, la de vida reinicia el contenedor. Reiniciar arregla un proceso colgado y no arregla una base de datos caída: por eso una sonda de vida que mira dependencias convierte un problema ajeno en una caída tuya.
Practica Kubernetes en tu propio clúster, gratis
Te envío al correo dos laboratorios completos con su solución: el 01 (namespace y deployment) y el 08 (ResourceQuota). Cada uno termina con un comando de verificación, no con "parece que funcionó". Las sondas y el rollout son el laboratorio 03 del kit.