Liveness, Readiness e Startup Probe no Kubernetes
1. Visão geral
Ao executar uma aplicação no Kubernetes, o cluster não sabe automaticamente se seu pod está “vivo” ou “pronto para receber tráfego” — ele precisa perguntar através de probes. Este artigo explica as diferenças entre liveness, readiness e startup probe, com exemplos de configuração do Spring Boot Actuator e YAML de Deployment. Ao final, você saberá configurar probes corretamente para um serviço real como OrderService sem causar loops desnecessários de restart.
2. Por que preciso de probes
Por padrão, o Kubernetes só sabe se o processo dentro do container ainda está rodando (status Running) ou não. Ele não sabe se a aplicação dentro tem um deadlock, perdeu a conexão com o banco de dados, ou ainda está inicializando.
As consequências sem probes:
- Pod travado (deadlock) mas ainda recebendo tráfego → timeout em cascata de requisições.
- Pod que acabou de iniciar, ainda sem conexão completa com o BD, já sendo adicionado à lista de endpoints do Service → erro 500.
Três tipos de probe resolvem três problemas diferentes:
| Probe | Responde à pergunta | Quando falha |
|---|---|---|
| Liveness | O container ainda está vivo? | Kubernetes reinicia o container |
| Readiness | O container está pronto para receber tráfego? | Kubernetes remove o pod da lista de endpoints do Service (sem reiniciar) |
| Startup | O container terminou de inicializar? | Kubernetes aguarda, desabilita temporariamente liveness/readiness |
3. Liveness probe
Liveness probe responde: “este processo está em um estado irrecuperável?”. Se o probe falha mais que failureThreshold vezes, o kubelet mata e reinicia o container.
3.1. Adicionar 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
A linha management.endpoint.health.probes.enabled=true ativa dois endpoints: /actuator/health/liveness e /actuator/health/readiness, conforme esperado pelo Kubernetes.
3.2. Declarar no 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: aguardar 20s após iniciar o container antes de começar o probe.periodSeconds: 10: chamar o probe a cada 10s.failureThreshold: 3: considerar morto após 3 falhas consecutivas → reiniciar.
Nota: Liveness probe deve apenas verificar “a aplicação ainda está rodando?”, não deve verificar dependências externas (BD, Redis…). Se o BD cair e liveness falha com ele, Kubernetes reiniciará o pod continuamente sem resolver nada — este é um erro muito comum.
4. Readiness probe
Readiness probe responde: “o pod está pronto para processar requisições?”. Quando falha, o pod é removido da lista de endpoints do Service — o tráfego não é mais enviado para ele, mas o container não é reiniciado.
4.1. Indicador de readiness customizado
Suponha que OrderService precisa se conectar ao PaymentGateway antes de estar pronto:
@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 no grupo readiness (sem afetar liveness), declare o grupo:
management.endpoint.health.group.readiness.include=readinessState,paymentGateway
health()retornaHealth.down()quandoPaymentGatewayClientnão consegue conectar — o readiness probe falhará, o pod não receberá tráfego até que o gateway esteja pronto.- Spring automaticamente mapeia o bean
paymentGatewayReadinessIndicatorpara a chavepaymentGatewayna resposta de health.
4.2. Teste para o 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 a dependência externa, sem precisar de uma conexão real.assertThat(health.getStatus())verifica se o status éDOWNquando o gateway não conecta — o comportamento esperado do readiness.
4.3. Declarar readiness no Deployment
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 3
successThreshold: 1
successThreshold: 1: apenas 1 sucesso do probe é suficiente para considerar o pod pronto novamente (diferente de liveness, geralmente não precisa configurar isso pois o padrão já é 1).periodSeconds: 5é mais curto que liveness porque readiness precisa reagir rapidamente às mudanças de estado das dependências.
5. Startup probe
Para aplicações que inicializam lentamente (carregando cache grande, executando migração de banco de dados), se usar apenas liveness com initialDelaySeconds fixo, você precisa adivinhar um valor grande o suficiente — fácil errar e desperdiçar tempo aguardando em cada reinicialização subsequente.
Startup probe resolve isso: enquanto startup probe não passa, Kubernetes desabilita temporariamente liveness e readiness probe, evitando matar o aplicativo enquanto ele está inicializando corretamente.
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: 30eperiodSeconds: 10→ permite que o app inicie por no máximo 300s (30 × 10s) antes de ser considerado falhado.- Depois que
startupProbepassa pela primeira vez, Kubernetes muda para monitorarlivenessProbecom um threshold menor (3 vezes × 10s = 30s) — reagindo rapidamente a problemas reais durante a execução estável.
Nota: Se sua aplicação inicia rapidamente (em poucos segundos, como uma REST API simples sem cache grande), não precisa declarar
startupProbe— adicioná-lo apenas torna o YAML mais longo sem benefício evidente.
6. Combinando os três em um 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
Fluxo de operação quando um novo pod é criado:
- Container inicia, Kubernetes começa a chamar
startupProbeimediatamente (seminitialDelaySecondspois startup lida com a espera internamente). - Neste momento
livenessProbeereadinessProbeainda não estão em execução. - Quando
startupProberetorna HTTP 200 pela primeira vez, Kubernetes para completamente o startup probe e começa a executar liveness e readiness em paralelo. - Readiness falha → pod é removido do Service, o tráfego não é mais enviado, mas o container continua vivo.
- Liveness falha → o container é eliminado, Kubernetes cria um novo container (mesmo pod), o processo se repete a partir do passo 1.
Verifique o status real do probe usando 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
A linha Readiness probe failed indica que readiness está falhando — verifique o log da aplicação ou chame diretamente /actuator/health/readiness para ver qual detalhe está DOWN.
7. Erros comuns
- Usar o mesmo endpoint para liveness e readiness: quando uma dependência externa (BD, cache) falha, ambos os probes falham → pod reinicia continuamente (crash loop) mesmo que a aplicação principal esteja saudável. Separe claramente
/actuator/health/liveness(verifica apenas internamente) e/actuator/health/readiness(verifica também dependências). initialDelaySecondsmuito pequeno para app que inicia lentamente: liveness probe falha enquanto o app ainda está carregando, causando loop infinito de reinicializações. Solução: usestartupProbeao invés de aumentarinitialDelaySecondsarbitrariamente.- Esquecer de manter
periodSeconds×failureThresholdmuito grande: um pod realmente com erro leva muitos minutos para ser detectado, afetando o SLA. - Readiness probe chama um serviço downstream não crítico: por exemplo, chamar um serviço de logging, se esse serviço cair então todo
OrderServiceé considerado não-pronto injustamente, mesmo que a funcionalidade principal esteja funcionando bem.
8. Conclusão
Os três tipos de probe servem a três propósitos distintos: startupProbe protege a aplicação durante a fase de inicialização lenta, livenessProbe detecta deadlocks para que o Kubernetes reinicie automaticamente, e readinessProbe orquestra o tráfego com base no estado real das dependências. O princípio mais importante: não deixe liveness probe depender de sistemas externos — isso transforma uma falha em um serviço a jusante em um crash loop do seu próprio serviço. Configurar corretamente esses três probes ajuda o cluster a se recuperar suavemente, reduzindo significativamente o tempo de downtime durante deploy ou em caso de falha de infraestrutura.