Un Pod puede estar en estado Running y aun así no estar disponible para recibir tráfico.
También puede estar ejecutando una aplicación completamente funcional y comenzar a reiniciarse porque un health check está mal configurado.
En Kubernetes existen tres tipos principales de probes:
Startup
Liveness
Readiness
Aunque su configuración es similar, responden preguntas completamente diferentes.
Startup
|
v
¿La aplicación ya terminó de iniciar?
Liveness
|
v
¿La aplicación sigue funcionando
o necesita ser reiniciada?
Readiness
|
v
¿La aplicación está preparada
para recibir tráfico?
Confundir estas responsabilidades puede transformar una falla pequeña en un incidente mucho mayor.
En este cuarto artículo de nuestra serie de Kubernetes Troubleshooting vamos a investigar:
readiness probes;
liveness probes;
startup probes;
HTTP, TCP, exec y gRPC probes;
timeouts y thresholds;
Pods
RunningperoNotReady;restart loops provocados por probes;
aplicaciones que tardan demasiado en iniciar;
dependencias externas en health checks;
cascading failures;
cómo diseñar probes que ayuden en lugar de empeorar un incidente.
Primero: las tres probes no hacen lo mismo
Esta es la diferencia más importante de todo el artículo.
Container
|
+----------------+----------------+
| | |
v v v
Startup Liveness Readiness
| | |
¿Terminó de ¿Sigue vivo? ¿Puede recibir
iniciar? tráfico?
| | |
FAIL FAIL FAIL
| | |
v v v
Restart Restart Remove from
normal Service
traffic
Una readiness failure no debería reiniciar el contenedor.
Una liveness failure sí puede provocar que el kubelet termine el contenedor y aplique su restart policy.
Una startup probe protege aplicaciones que necesitan tiempo adicional para iniciar.
1. Readiness probe
La readiness probe responde:
¿Este contenedor está preparado para recibir tráfico ahora?
Supongamos que tenemos tres Pods:
Service
|
+--------+--------+
| | |
v v v
Pod A Pod B Pod C
Ready Ready NotReady
El tráfico normal del Service debe dirigirse hacia los endpoints considerados ready.
Pod C puede continuar:
Running
pero no estar:
Ready
Esto es completamente válido.
Un ejemplo de readiness
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 2
failureThreshold: 3
Conceptualmente:
GET /ready
|
+--- Success
| |
| v
| Ready
|
+--- Failure
|
v
NotReady
|
v
No normal Service traffic
El proceso continúa ejecutándose.
Pod Running pero 0/1 Ready
Un escenario común:
kubectl get pods -n production
muestra:
NAME READY STATUS RESTARTS
api-7bd95fcb85-rx7mw 0/1 Running 0
Esto nos dice algo importante:
Container
|
+--- Running
|
+--- Not Ready
Comenzamos con:
kubectl describe pod \
api-7bd95fcb85-rx7mw \
-n production
Busca Events como:
Readiness probe failed:
HTTP probe failed with statuscode: 503
o:
dial tcp 10.244.2.15:8080:
connect: connection refused
¿Qué está comprobando realmente la readiness probe?
Revisa el workload:
kubectl get deployment api \
-n production \
-o yaml
Busca:
readinessProbe:
Y responde:
¿Path correcto?
¿Port correcto?
¿Protocol correcto?
¿La aplicación ya escucha?
¿El timeout es suficiente?
¿La respuesta HTTP es válida?
¿El endpoint depende de otros sistemas?
También podemos probar el endpoint manualmente.
Desde el Pod:
kubectl exec \
-n production \
<pod> -- \
curl -v http://127.0.0.1:8080/ready
Si la imagen no contiene curl, podemos utilizar un debugging container o Pod.
Readiness y EndpointSlices
Cuando un Pod deja de estar ready, Kubernetes refleja este estado en los backends utilizados por Services.
Podemos investigarlo:
kubectl get endpointslices \
-n production \
-l kubernetes.io/service-name=api-service \
-o yaml
Busca condiciones como:
conditions:
ready: false
Esto conecta dos partes importantes de nuestra serie:
Readiness Probe
|
v
Pod NotReady
|
v
EndpointSlice condition changes
|
v
Normal Service traffic stops
2. Liveness probe
La liveness probe responde una pregunta diferente:
¿La aplicación sigue suficientemente viva como para continuar ejecutándose?
Un ejemplo:
livenessProbe:
httpGet:
path: /live
port: 8080
periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 3
Si falla repetidamente hasta alcanzar failureThreshold:
Liveness fails
|
v
Failure threshold reached
|
v
kubelet stops container
|
v
Restart policy applies
|
v
Container starts again
Esto puede ser útil para aplicaciones que entran en estados irrecuperables, como determinados deadlocks.
Pero también puede ser peligroso.
Cuando liveness se convierte en el problema
Imaginemos que una aplicación está bajo carga elevada.
Normalmente:
/ live -> 20 ms
Pero durante el pico:
/live -> 1.5 seconds
Nuestra configuración tiene:
timeoutSeconds: 1
failureThreshold: 3
Entonces:
Traffic increases
|
v
Application slows
|
v
Liveness times out
|
v
Pod restarted
|
v
Less capacity
|
v
Remaining Pods receive more traffic
|
v
They become slower
|
v
More liveness failures
|
v
More restarts
Acabamos de crear una cascading failure.
La aplicación estaba degradada.
La configuración de health checks convirtió la degradación en una pérdida progresiva de capacidad.
3. Startup probe
La startup probe existe especialmente para aplicaciones que necesitan tiempo significativo para inicializarse.
Por ejemplo:
Container starts
|
v
Load configuration
|
v
Initialize runtime
|
v
Warm caches
|
v
Application ready
Esto podría tardar:
90 seconds
Si una liveness probe comienza demasiado pronto, Kubernetes podría matar el proceso mientras todavía está iniciando.
Sin startup probe
Container starts
|
v
Application starting...
|
v
Liveness begins
|
v
FAIL
|
v
Restart
|
v
Application starting...
|
v
FAIL
Resultado:
CrashLoopBackOff
aunque la aplicación podría haber iniciado correctamente si hubiera tenido tiempo suficiente.
Con startup probe
startupProbe:
httpGet:
path: /startup
port: 8080
periodSeconds: 10
failureThreshold: 30
Aquí tenemos aproximadamente:
10 seconds × 30 failures = 300 seconds
de ventana antes de considerar que startup falló.
Mientras la startup probe no haya tenido éxito:
Startup probe
|
+--- checking
|
Liveness disabled
Readiness disabled
Una vez que startup pasa:
Startup succeeds
|
+------------+
| |
v v
Liveness Readiness
begins begins
initialDelaySeconds vs startupProbe
Podríamos simplemente configurar:
livenessProbe:
initialDelaySeconds: 120
pero una startup probe expresa mejor nuestra intención.
Con un delay fijo:
Always wait 120 seconds
Con startupProbe:
Check startup
|
+--- ready after 20 seconds
| |
| v
| continue immediately
|
+--- still starting
|
v
keep checking
Esto permite proteger aplicaciones lentas sin necesariamente esperar el máximo en cada inicio.
4. Investigar restart loops provocados por probes
Supongamos:
kubectl get pods -n production
muestra:
NAME READY STATUS RESTARTS
api-6b8cff746c-r27ft 0/1 CrashLoopBackOff 8
No debemos asumir inmediatamente que la aplicación está haciendo crash por sí sola.
Ejecutamos:
kubectl describe pod \
api-6b8cff746c-r27ft \
-n production
y encontramos:
Warning Unhealthy
Liveness probe failed
Ahora nuestra hipótesis cambia.
Revisa:
kubectl logs \
api-6b8cff746c-r27ft \
-n production \
--previous
y la configuración:
kubectl get deployment api \
-n production \
-o yaml
Investiga la timeline
Queremos entender:
Container starts
|
v
When does probe start?
|
v
How long does app need?
|
v
How often does probe run?
|
v
How long until failure?
|
v
Why does endpoint fail?
Una configuración como:
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 3
periodSeconds: 2
timeoutSeconds: 1
failureThreshold: 2
puede ser extremadamente agresiva para determinadas aplicaciones.
5. Los parámetros que debemos entender
initialDelaySeconds
Tiempo antes de comenzar determinadas probes después de iniciar el contenedor.
initialDelaySeconds: 10
No debería utilizarse como sustituto automático de una startup probe para workloads con tiempos de arranque variables.
periodSeconds
Frecuencia de ejecución.
periodSeconds: 10
Una frecuencia demasiado agresiva puede generar overhead innecesario.
timeoutSeconds
Cuánto esperamos por el resultado de una probe.
timeoutSeconds: 2
Un timeout demasiado pequeño puede generar falsos negativos durante períodos de carga.
failureThreshold
Número de fallas consecutivas necesarias para considerar que la probe falló.
failureThreshold: 3
Con:
periodSeconds = 10
failureThreshold = 3
el fallo no ocurre simplemente después de exactamente 30 segundos en todos los casos, pero esos valores nos ayudan a entender la tolerancia aproximada de la configuración.
successThreshold
Número de éxitos consecutivos requeridos para volver al estado exitoso.
Para readiness puede ser mayor que uno.
Para liveness y startup debe permanecer en:
1
6. HTTP probes
Una HTTP probe puede verse así:
readinessProbe:
httpGet:
path: /ready
port: 8080
scheme: HTTP
Es útil cuando nuestra aplicación expone health endpoints específicos.
Podemos reproducir la comprobación:
curl -v http://<pod-ip>:8080/ready
Investiga:
HTTP status
response time
connection errors
TLS
redirect behavior
application logs
7. TCP probes
Una TCP probe comprueba si puede establecerse una conexión al puerto.
livenessProbe:
tcpSocket:
port: 8080
Conceptualmente:
Can kubelet establish TCP connection?
|
+----+----+
| |
YES NO
Pero esto no significa necesariamente que la aplicación esté funcionando correctamente a nivel lógico.
Un proceso puede aceptar TCP:
TCP connection -> OK
pero estar devolviendo:
HTTP 500
a todas las peticiones.
Por eso debemos elegir la probe según lo que realmente queremos comprobar.
8. Exec probes
También podemos ejecutar un comando dentro del contenedor:
livenessProbe:
exec:
command:
- /bin/sh
- -c
- test -f /tmp/healthy
Esto permite comprobaciones específicas del proceso.
Pero tiene trade-offs.
Cada ejecución requiere ejecutar procesos dentro del contenedor, por lo que probes exec demasiado frecuentes pueden generar overhead, especialmente en clusters densos.
Utilízalas cuando realmente aporten valor.
9. gRPC probes
Kubernetes también soporta probes nativas para workloads gRPC.
Por ejemplo:
livenessProbe:
grpc:
port: 50051
initialDelaySeconds: 10
Esto evita tener que crear obligatoriamente un endpoint HTTP únicamente para comprobar un servicio gRPC.
Sin embargo, existen diferencias de configuración respecto a HTTP/TCP probes.
Por ejemplo, las probes gRPC no utilizan named ports de la misma manera que HTTP/TCP.
Siempre debemos validar la configuración correspondiente al protocolo utilizado.
10. ¿Debe liveness comprobar la base de datos?
Este es uno de los errores de diseño más importantes.
Supongamos que:
/live
|
+--- application
+--- PostgreSQL
+--- Redis
+--- payment API
Ahora PostgreSQL tiene un incidente.
PostgreSQL unavailable
|
v
/live fails
|
v
Every application Pod
fails liveness
|
v
Every Pod restarts
|
v
Application capacity collapses
Reiniciar los Pods no repara PostgreSQL.
Estamos utilizando liveness para reaccionar ante una dependencia externa que el restart del proceso no puede solucionar.
Una mejor separación
Podríamos diseñar:
/live
|
+--- Is my process internally healthy?
/ready
|
+--- Can I currently serve useful traffic?
Por ejemplo:
Database unavailable
|
v
Readiness fails
|
v
Pod removed from traffic
|
v
Container remains alive
Pero incluso aquí debemos ser cuidadosos.
11. El peligro de incluir todas las dependencias en readiness
Supongamos que tenemos:
API
|
+-- PostgreSQL
+-- Redis
+-- External Payment API
y readiness requiere que las tres estén disponibles.
El Payment API tiene un pequeño incidente.
Payment API down
|
v
Every API Pod becomes NotReady
|
v
Service has zero ready backends
|
v
Entire API unavailable
Pero quizá:
GET /products
GET /profile
GET /orders
podrían continuar funcionando.
Al diseñar readiness debemos preguntarnos:
¿Esta dependencia realmente hace que todo el Pod sea incapaz de servir tráfico útil?
La respuesta depende de la arquitectura de la aplicación.
12. Probes deben ser baratas
Un health endpoint no debería ejecutar una consulta costosa cada pocos segundos.
Evita algo como:
/readiness
|
v
Complex DB query
|
v
Multiple downstream calls
|
v
Storage operation
multiplicado por:
500 Pods
×
probe every 5 seconds
Podemos terminar creando tráfico significativo simplemente para comprobar la salud del sistema.
Una buena probe debería normalmente ser:
fast
cheap
predictable
purpose-specific
13. Observar métricas de probes
Los Events son un excelente punto de partida:
kubectl get events \
-n production \
--sort-by='.lastTimestamp'
Busca:
Unhealthy
Readiness probe failed
Liveness probe failed
Startup probe failed
Pero los Events no sustituyen métricas históricas.
Queremos correlacionar:
Probe failures
+
Restart count
+
CPU
+
Memory
+
Latency
+
Traffic
+
Deployment
Por ejemplo:
10:00 Deployment
10:03 Traffic increases
10:05 CPU saturation
10:06 Liveness failures
10:07 Restarts increase
10:08 Available replicas decrease
10:09 5xx spike
Eso cuenta una historia mucho más completa.
14. Logs
Cuando una probe falla:
kubectl logs <pod> \
-n production
y si el contenedor reinició:
kubectl logs <pod> \
-n production \
--previous
Correlaciona el timestamp del Event con los logs de aplicación.
Podemos descubrir:
probe timeout
|
v
same timestamp
|
v
long GC pause
o:
readiness 503
|
v
connection pool exhausted
o:
startup failure
|
v
database migration still running
15. Metrics + traces
Las probes nos dicen si una instancia está disponible.
No necesariamente nos dicen por qué está degradada.
Supongamos:
Readiness failures increasing
y las métricas muestran:
CPU normal
Memory normal
Latency high
Un trace podría mostrar:
API 20 ms
|
+-- Inventory 15 ms
|
+-- Database 4.2 sec
Ahora sabemos qué dependencia está degradando la aplicación.
Health checks son una señal.
Logs, métricas y traces proporcionan contexto.
16. Rolling updates y readiness
Readiness es especialmente importante durante deployments.
Old Pods
|
+--- Ready
New Pod starts
|
v
NotReady
|
v
Initialize
|
v
Ready
|
v
Receives traffic
Sin una readiness probe útil, Kubernetes puede considerar disponible una aplicación antes de que realmente esté preparada para procesar requests correctamente.
Esto puede provocar errores únicamente durante deployments.
17. Readiness Gates
En escenarios más avanzados Kubernetes permite añadir condiciones adicionales a la evaluación de readiness mediante:
readinessGates:
Conceptualmente:
Containers Ready
+
Custom condition Ready
|
v
Pod Ready
Esto permite integrar señales externas o controllers dentro de la decisión de readiness.
No es necesario para la mayoría de workloads, pero puede ser útil cuando la disponibilidad depende de algo que una probe dentro del contenedor no representa adecuadamente.
18. Cuando una probe falla solo en un nodo
Supongamos:
node-01
Pods -> probes OK
node-02
Pods -> probes FAIL
Ahora debemos considerar que quizá la aplicación no es la causa.
Compara:
kubectl get pods \
-n production \
-o wide
Después:
kubectl describe node node-02
Posibles causas:
node CPU saturation
networking issue
CNI issue
kubelet problem
disk pressure
packet loss
El blast radius ayuda a identificar la capa.
19. Qué revisar en kubectl describe
Durante troubleshooting:
kubectl describe pod <pod> \
-n <namespace>
revisa:
State
Last State
Ready
Restart Count
Conditions
Liveness
Readiness
Startup
Events
También:
kubectl get pod <pod> \
-n <namespace> \
-o yaml
permite inspeccionar la configuración completa de las probes.
20. Un diseño práctico
Una configuración podría verse así:
startupProbe:
httpGet:
path: /startup
port: 8080
periodSeconds: 5
failureThreshold: 30
livenessProbe:
httpGet:
path: /live
port: 8080
periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 3
readinessProbe:
httpGet:
path: /ready
port: 8080
periodSeconds: 5
timeoutSeconds: 2
failureThreshold: 3
Pero estos valores no son una receta universal.
Deben elegirse utilizando datos reales sobre:
startup time
normal response time
load behavior
recovery time
dependency behavior
traffic patterns
Copiar una configuración de otro servicio puede ser peor que no entender el problema.
Troubleshooting flow
Podemos resumir el proceso:
Pod problem
|
v
Is container Running?
|
v
Is Pod Ready?
|
+----------+----------+
| |
NO YES
| |
v v
Readiness Events Restarting?
| |
v v
Probe endpoint works? Liveness / Startup?
| |
v v
Path / port / timeout Probe timing
dependency / overload startup duration
| |
+----------+----------+
|
v
Metrics + Logs + Events
|
v
Check dependencies
|
v
Root cause
|
v
Change
|
v
Validate
Command reference
# Pod state
kubectl get pods \
-n <namespace>
kubectl get pods \
-n <namespace> \
-o wide
# Pod details and probe Events
kubectl describe pod <pod> \
-n <namespace>
# Full configuration
kubectl get pod <pod> \
-n <namespace> \
-o yaml
kubectl get deployment <deployment> \
-n <namespace> \
-o yaml
# Logs
kubectl logs <pod> \
-n <namespace>
kubectl logs <pod> \
-n <namespace> \
--previous
# Events
kubectl get events \
-n <namespace> \
--sort-by='.lastTimestamp'
# EndpointSlices
kubectl get endpointslices \
-n <namespace> \
-l kubernetes.io/service-name=<service> \
-o yaml
# Metrics
kubectl top pods \
-n <namespace>
kubectl top pod <pod> \
-n <namespace> \
--containers
# Deployment history
kubectl rollout history \
deployment/<deployment> \
-n <namespace>
# Test endpoint
curl -v http://<pod-ip>:<port>/ready
curl -v http://<pod-ip>:<port>/live
# Debug when the image lacks tools
kubectl debug <pod> \
-n <namespace> \
-it \
--image=ubuntu
Qué no hacer
No respondas automáticamente:
Readiness failing
|
v
Restart Pod
Ni:
Liveness failing
|
v
Increase timeout
Ni:
Slow startup
|
v
Set initialDelaySeconds = 300
Primero debemos entender qué ocurre.
Failure
|
v
Understand probe purpose
|
v
Reproduce probe
|
v
Correlate Events
|
v
Check metrics + logs
|
v
Identify application/dependency issue
|
v
Adjust probe or application
|
v
Validate under real load
Conclusión
Las probes no son simplemente health checks.
Son mecanismos que cambian el comportamiento del cluster.
Readiness failure
|
v
Traffic changes
Liveness failure
|
v
Container lifecycle changes
Startup failure
|
v
Container lifecycle changes
Por eso una probe incorrecta puede ser más peligrosa que no tener una probe bien entendida.
El objetivo es que cada probe responda una pregunta específica:
Startup:
¿terminó de iniciar?
Liveness:
¿reiniciar este proceso puede recuperarlo?
Readiness:
¿puede esta instancia recibir tráfico útil ahora?
Y antes de agregar una dependencia a una probe, debemos preguntarnos:
¿Qué acción ejecutará Kubernetes cuando esta dependencia falle, y esa acción realmente ayuda?
Esta pregunta evita muchos health checks peligrosos.
En el siguiente artículo investigaremos Kubernetes Nodes:
NotReady;MemoryPressure;DiskPressure;PIDPressure;kubelet;
container runtime;
node networking;
Pods concentrando fallas en un solo nodo;
cómo determinar cuándo el problema dejó de ser del workload y pasó a ser del host.
Comments