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() retorna Health.down() quando PaymentGatewayClient nã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 paymentGatewayReadinessIndicator para a chave paymentGateway na 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 é DOWN quando 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
  • startupProbe usa failureThreshold: 30 e periodSeconds: 10 → permite que o app inicie por no máximo 300s (30 × 10s) antes de ser considerado falhado.
  • Depois que startupProbe passa pela primeira vez, Kubernetes muda para monitorar livenessProbe com 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:

  1. Container inicia, Kubernetes começa a chamar startupProbe imediatamente (sem initialDelaySeconds pois startup lida com a espera internamente).
  2. Neste momento livenessProbe e readinessProbe ainda não estão em execução.
  3. Quando startupProbe retorna HTTP 200 pela primeira vez, Kubernetes para completamente o startup probe e começa a executar liveness e readiness em paralelo.
  4. Readiness falha → pod é removido do Service, o tráfego não é mais enviado, mas o container continua vivo.
  5. 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).
  • initialDelaySeconds muito pequeno para app que inicia lentamente: liveness probe falha enquanto o app ainda está carregando, causando loop infinito de reinicializações. Solução: use startupProbe ao invés de aumentar initialDelaySeconds arbitrariamente.
  • Esquecer de manter periodSeconds × failureThreshold muito 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.

kubernetesspring-bootdevopshealth-checkmicroservices