¿Cómo funcionan realmente los health checks y las sondas de readiness/liveness, y por qué son distintas?
"¿Debería reiniciarse este contenedor?" y "¿debería enrutarse tráfico aquí ahora mismo?" son preguntas distintas con remedios distintos, y confundirlas es cómo un bache de base de datos se convierte en un bucle de reinicio de toda la flota.
APRENDE HACIENDOPon a prueba tu intuición3 pasos
¿Cómo funcionan realmente los health checks y las sondas de readiness/liveness, y por qué son distintas?
Usa los ejemplos y la explicación de la lección para comprobar tu idea.
Reflexión personal. No se envía a ningún servicio de IA.#Dos preguntas distintas, dos sondas distintas
"¿Esta instancia está rota y debería reiniciarse?" y "¿esta instancia puede actualmente servir tráfico?" suenan parecidas pero exigen acciones opuestas, que es precisamente por qué Kubernetes (y la mayoría de las plataformas de orquestación) exponen dos sondas separadas en lugar de una sola. Una sonda de liveness (vitalidad) responde la primera pregunta: si falla repetidamente, el orquestador mata y reinicia el contenedor, bajo el supuesto de que el proceso está atascado de una forma que solo un reinicio arregla (un deadlock, un thread pool colgado, una fuga de memoria más allá del punto de recuperación). Una sonda de readiness (disponibilidad) responde la segunda: si falla, el orquestador simplemente deja de enrutar tráfico nuevo a ese pod (lo quita de la lista de endpoints del Service) sin tocar el contenedor en absoluto — el proceso sigue corriendo, y el tráfico se reanuda automáticamente en cuanto la sonda vuelve a pasar. Una tercera, menos conocida universalmente, la sonda de startup, existe puramente para dar a las aplicaciones de arranque lento (una JVM calentando, una caché grande que precargar) un periodo de gracia antes de que empiecen siquiera las comprobaciones de liveness, de modo que una app que legítimamente aún está arrancando no se mate por fallar una comprobación de liveness que nunca iba a pasar todavía.
#Por qué confundirlas provoca bucles de reinicio
El error más común con diferencia — y una gran trampa de entrevista — es conectar la sonda de liveness a dependencias downstream (una conexión a base de datos, una API downstream). Parece razonable ("si no puedo llegar a la base de datos, ¡algo está mal!"), pero es exactamente al revés: si la base de datos tiene un pequeño bache, todos los pods del deployment fallan su comprobación de liveness simultáneamente, el orquestador los reinicia todos a la vez, y ahora convertiste un problema transitorio de base de datos en una caída autoinfligida de toda la flota — los pods vuelven a levantarse, inmediatamente vuelven a fallar liveness porque la base de datos todavía no se ha recuperado, y estás en un bucle de reinicio que empeora el problema original y lo hace más difícil de diagnosticar. Que la base de datos esté brevemente inalcanzable es un asunto de readiness (deja de enviar tráfico aquí hasta que vuelva), no de liveness (este proceso en sí no está roto, simplemente no puede alcanzar algo externo ahora mismo).
#Configuración de Kubernetes
apiVersion: apps/v1kind: Deploymentmetadata:name: checkout-servicespec:replicas: 4selector:matchLabels:app: checkout-servicetemplate:metadata:labels:app: checkout-servicespec:containers:- name: checkout-serviceimage: registry.example.com/checkout-service:1.14.2ports:- containerPort: 8080startupProbe:httpGet:path: /healthzport: 8080failureThreshold: 30 # permite hasta 30 * 5s = 150s para arrancarperiodSeconds: 5livenessProbe:httpGet:path: /healthzport: 8080initialDelaySeconds: 0 # el startupProbe ya cubrió el tiempo de arranqueperiodSeconds: 10timeoutSeconds: 2failureThreshold: 3 # 3 fallos consecutivos -> reinicioreadinessProbe:httpGet:path: /readyzport: 8080initialDelaySeconds: 0periodSeconds: 5timeoutSeconds: 2failureThreshold: 2 # 2 fallos consecutivos -> se retira del ServicesuccessThreshold: 1 # 1 éxito -> se vuelve a añadir al Service
Fíjate en que las dos sondas apuntan a endpoints distintos — /healthz y /readyz — a propósito; compartir un único endpoint para ambas es cómo los equipos reintroducen sin querer el problema de bucle de reinicio de arriba.
#La implementación de los endpoints
package mainimport ("encoding/json""net/http""sync/atomic")// draining pasa a true durante el apagado ordenado, para que readiness// falle de inmediato y el balanceador deje de enviar tráfico nuevo,// mientras las peticiones en curso terminan con normalidad.var draining int32func healthzHandler(w http.ResponseWriter, r *http.Request) {// Liveness: solo comprueba que el bucle principal de ESTE proceso responde.// Deliberadamente no hay llamadas downstream aquí -- una dependencia// lenta nunca debe poder hacer que maten este pod.w.WriteHeader(http.StatusOK)json.NewEncoder(w).Encode(map[string]string{"status": "alive"})}func readyzHandler(w http.ResponseWriter, r *http.Request) {if atomic.LoadInt32(&draining) == 1 {http.Error(w, "draining", http.StatusServiceUnavailable)return}if err := pingDatabase(); err != nil {http.Error(w, "base de datos inalcanzable: "+err.Error(), http.StatusServiceUnavailable)return}if err := pingDownstreamPaymentsAPI(); err != nil {http.Error(w, "API de pagos inalcanzable: "+err.Error(), http.StatusServiceUnavailable)return}w.WriteHeader(http.StatusOK)json.NewEncoder(w).Encode(map[string]string{"status": "ready"})}func pingDatabase() error {// Una comprobación de conectividad barata contra el pool de conexiones,// no una consulta completa -- p.ej. db.PingContext(ctx) con timeout corto.return nil}func pingDownstreamPaymentsAPI() error {// Una comprobación de salud ligera contra una dependencia dura crítica.return nil}func main() {http.HandleFunc("/healthz", healthzHandler)http.HandleFunc("/readyz", readyzHandler)http.ListenAndServe(":8080", nil)}
La bandera draining merece mención aparte: al recibir SIGTERM, un servicio bien comportado la pone en true de inmediato, lo que hace que /readyz empiece a fallar enseguida para que el balanceador deje de enrutar peticiones nuevas, mientras el proceso sigue vivo el tiempo suficiente para terminar las peticiones ya en curso antes de salir de verdad — esto es lo que permite que los despliegues rolling eviten peticiones perdidas, y no tiene nada que ver con la sonda de liveness.
🧠 Check your understanding
0/2 · 0/2 answered1. ¿Por qué es un error hacer que la sonda de liveness compruebe la conexión a la base de datos?
2. ¿Cuál es el efecto práctico de que falle una sonda de readiness, en contraste con que falle una de liveness?