Liveness, Readiness y Startup Probe en Kubernetes
1. Descripción general
Cuando ejecutas una aplicación en Kubernetes, el clúster no sabe automáticamente si tu pod está “vivo” o “listo para recibir tráfico” — tiene que preguntarlo a través de probes. Este artículo explica las diferencias entre liveness, readiness y startup probe, con ejemplos de configuración usando Spring Boot Actuator y YAML de Deployment. Cuando termines de leerlo, podrás configurar correctamente los probes para un servicio real como OrderService sin que los pods entren en loops infinitos de restart innecesarios.
2. Por qué necesitamos probes
Por defecto, Kubernetes solo sabe si el proceso dentro del contenedor sigue ejecutándose (estado del contenedor Running) o no. No sabe si la aplicación está en deadlock, si perdió la conexión a la base de datos, o si todavía está arrancando.
Las consecuencias sin probes son:
- Pod colgado (deadlock) pero aún recibiendo tráfico → requests se agotan en timeout constantemente.
- Pod acaba de arrancar, no ha establecido conexión a la BD, y ya el Service lo incluye en la lista de endpoints → errores 500.
Los tres tipos de probe resuelven tres problemas distintos:
| Probe | Responde la pregunta | Cuando falla |
|---|---|---|
| Liveness | ¿El contenedor sigue vivo? | Kubernetes reinicia el contenedor |
| Readiness | ¿El contenedor está listo para recibir tráfico? | Kubernetes quita el pod de los endpoints del Service (sin reiniciar) |
| Startup | ¿El contenedor ha terminado de arrancar? | Kubernetes espera, desactiva temporalmente liveness y readiness |
3. Liveness probe
El liveness probe responde: “¿Este proceso está en un estado del que no puede recuperarse por sí solo?”. Si el probe falla más de failureThreshold veces, kubelet mata y reinicia el contenedor.
3.1. Agregar Spring Boot Actuator
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
management.endpoint.health.probes.enabled=true
management.endpoints.web.exposure.include=health,info
La línea management.endpoint.health.probes.enabled=true activa dos endpoints: /actuator/health/liveness y /actuator/health/readiness, exactamente como Kubernetes los espera.
3.2. Declarar en el Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
template:
spec:
containers:
- name: order-service
image: nonladev/order-service:1.0
ports:
- containerPort: 8080
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 20
periodSeconds: 10
failureThreshold: 3
initialDelaySeconds: 20: espera 20 segundos después de que el contenedor arranque antes de comenzar a probar.periodSeconds: 10: llama al probe cada 10 segundos.failureThreshold: 3: falla 3 veces seguidas para ser considerado muerto → reinicio.
Nota importante: El liveness probe solo debe verificar “¿la aplicación sigue funcionando?”, no debe verificar dependencias externas (BD, Redis, etc.). Si la BD muere y el liveness falla por eso, Kubernetes reiniciará el pod constantemente sin resolver nada — este es un error muy común.
4. Readiness probe
El readiness probe responde: “¿el pod está listo para procesar requests?”. Cuando falla, el pod se quita de los endpoints del Service — el tráfico no se envía a este pod, pero el contenedor no se reinicia.
4.1. Indicador readiness personalizado
Supongamos que OrderService necesita conectar a PaymentGateway antes de estar listo:
@Component
public class PaymentGatewayReadinessIndicator implements HealthIndicator {
private final PaymentGatewayClient paymentGatewayClient;
public PaymentGatewayReadinessIndicator(PaymentGatewayClient paymentGatewayClient) {
this.paymentGatewayClient = paymentGatewayClient;
}
@Override
public Health health() {
if (paymentGatewayClient.isConnected()) {
return Health.up().withDetail("gateway", "connected").build();
}
return Health.down().withDetail("gateway", "unreachable").build();
}
}
Para incluir este indicador en el grupo readiness (sin afectar liveness), declara el grupo:
management.endpoint.health.group.readiness.include=readinessState,paymentGateway
health()retornaHealth.down()cuandoPaymentGatewayClientno puede conectar — el probe readiness fallará, el pod no recibirá tráfico hasta que el gateway esté disponible.- Spring automáticamente renombra el bean
paymentGatewayReadinessIndicatorcomo la clavepaymentGatewayen la respuesta health.
4.2. Test para el indicador
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
class PaymentGatewayReadinessIndicatorTest {
@Test
void shouldReportDownWhenGatewayNotConnected() {
PaymentGatewayClient client = mock(PaymentGatewayClient.class);
when(client.isConnected()).thenReturn(false);
PaymentGatewayReadinessIndicator indicator = new PaymentGatewayReadinessIndicator(client);
Health health = indicator.health();
assertThat(health.getStatus()).isEqualTo(Status.DOWN);
assertThat(health.getDetails()).containsEntry("gateway", "unreachable");
}
}
mock(PaymentGatewayClient.class)simula la dependencia externa, sin necesidad de una conexión real.assertThat(health.getStatus())verifica que el estado seaDOWNcuando gateway no está conectado — el comportamiento esperado del readiness.
4.3. Declarar readiness en el Deployment
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 3
successThreshold: 1
successThreshold: 1: solo 1 probe exitoso para que el pod sea considerado ready nuevamente (distinto a liveness, donde típicamente no necesitas este campo porque el default ya es 1).periodSeconds: 5es más corto que liveness porque readiness necesita reaccionar rápido a cambios de estado en las dependencias.
5. Startup probe
Con aplicaciones de arranque lento (cargar cachés grandes, ejecutar migraciones de BD), si solo usas liveness con initialDelaySeconds fijo, tienes que adivinar un número lo suficientemente grande — fácil de equivocarse y pierdes tiempo esperando en cada restart posterior.
El startup probe resuelve esto: mientras el startup probe no pase, Kubernetes desactiva temporalmente liveness y readiness, evitando que se mate el contenedor injustamente mientras la aplicación está arrancando legitimamente.
startupProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
failureThreshold: 30
periodSeconds: 10
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
periodSeconds: 10
failureThreshold: 3
startupProbeusafailureThreshold: 30yperiodSeconds: 10→ permite que la app arranque máximo 300s (30 × 10s) antes de ser considerada fallida.- Una vez que
startupProbepasa por primera vez, Kubernetes cambia a monitorearlivenessProbecon threshold más pequeño (3 veces × 10s = 30s) — reacción más rápida ante problemas reales durante la ejecución estable.
Nota: Si tu aplicación arranca rápido (menos de unos segundos, como una REST API simple sin cargar cachés grandes), no necesitas declarar
startupProbe— solo agrega más líneas al YAML sin beneficio claro.
6. Combinando los tres en un Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 3
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
containers:
- name: order-service
image: nonladev/order-service:1.0
ports:
- containerPort: 8080
startupProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
failureThreshold: 30
periodSeconds: 10
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
periodSeconds: 5
failureThreshold: 3
El flujo de ejecución cuando se crea un nuevo pod:
- El contenedor arranca, Kubernetes comienza a llamar a
startupProbeinmediatamente (sininitialDelaySecondsporque startup lo maneja). - Mientras tanto
livenessProbeyreadinessProbeno se ejecutan. - Cuando
startupProberetorna HTTP 200 por primera vez, Kubernetes detiene el startup probe, comienza a ejecutar liveness y readiness en paralelo. - Readiness falla → el pod se quita del Service, el tráfico no llega, pero el contenedor sigue vivo.
- Liveness falla → el contenedor se mata, Kubernetes crea un nuevo contenedor (mismo pod), el proceso se repite desde el paso 1.
Verifica el estado real de los probes con kubectl describe pod:
kubectl describe pod order-service-7d9f8c6b5-xk2pq
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Events:
Type Reason Age From Message
Warning Unhealthy 2m kubelet Readiness probe failed: HTTP probe failed with statuscode: 503
La línea Readiness probe failed indica que readiness está fallando — revisa los logs de la aplicación o llama directamente a /actuator/health/readiness para ver qué detalle está DOWN.
7. Errores comunes
- Usar el mismo endpoint para liveness y readiness: cuando una dependencia externa (BD, caché) se cae, ambos probes fallan → el pod se reinicia constantemente (crash loop) aunque la aplicación principal esté perfecta. Separa claramente
/actuator/health/liveness(solo verifica internals) de/actuator/health/readiness(incluye dependencias). initialDelaySecondsmuy pequeño para apps que arrancan lentamente: el probe liveness falla mientras la app aún carga, causando reinicio infinito. Solución: usastartupProbeen lugar de aumentarinitialDelaySecondsarbitrariamente.periodSeconds×failureThresholddemasiado grande: cuando hay un problema real, tarda muchos minutos en ser detectado, afectando el SLA.- Readiness probe llamando a un servicio downstream no crítico: por ejemplo, llamar a un servicio de logging; si ese servicio cae, todo tu
OrderServicese considera not-ready injustamente, aunque su funcionalidad principal siga en perfecto estado.
8. Conclusión
Los tres tipos de probe sirven tres propósitos distintos: startupProbe protege la aplicación durante el arranque lento, livenessProbe detecta deadlock para que Kubernetes reinicie automáticamente, y readinessProbe enruta el tráfico según el estado real de las dependencias. El principio más importante: nunca hagas que el liveness probe dependa de sistemas externos — eso convierte un problema downstream en un crash loop de tu propio servicio. Configurar correctamente estos tres probes ayuda al clúster a recuperarse automáticamente sin problemas, reduciendo significativamente el downtime durante despliegues o cuando hay incidentes de infraestructura.