Melhores Práticas para Gerenciar Health Checks no Kubernetes com Spring Boot

Por Gaspar Barancelli Junior em 10 de janeiro de 2025
Imagem ilustrativa sobre o post Melhores Práticas para Gerenciar Health Checks no Kubernetes com Spring Boot

Introdução

Quando falamos em arquiteturas baseadas em microserviços e contêineres, o Kubernetes se destaca como uma das plataformas mais populares para orquestração. Para garantir alta confiabilidade e disponibilidade das aplicações, o Kubernetes permite a configuração de dois tipos de health checks: liveness e readiness. Neste artigo, veremos em detalhes como eles funcionam em uma aplicação Java com Spring Boot 3 — a versão mais recente do framework — e Spring Boot Actuator, destacando as melhores práticas de configuração, incluindo a separação de porta para o Actuator, definição de path padrão e a propriedade correta para habilitar os endpoints de liveness e readiness.

O que são Liveness e Readiness?

Liveness Probe

  • Liveness é o indicador que o Kubernetes usa para verificar se a sua aplicação ainda está “viva”.

  • Se o contêiner não responder a esse check, o Kubernetes entenderá que a aplicação está em um estado irrecuperável e iniciará o processo de reiniciar o contêiner.

Readiness Probe

  • Readiness é o indicador que o Kubernetes utiliza para verificar se a sua aplicação está pronta para receber tráfego.

  • Se o contêiner não estiver pronto, o Kubernetes não enviará requisições para aquele pod, removendo-o do balanceamento de carga até que ele indique novamente estar pronto.

A separação entre liveness e readiness ajuda a eliminar falsas sinalizações e a controlar melhor o ciclo de vida da aplicação no Kubernetes. Por exemplo, durante o processo de inicialização, se a aplicação não consegue atender requisições ainda, o readiness deve sinalizar “não pronto” para que o Kubernetes não envie tráfego prematuramente.

Adicionando o Spring Boot Actuator ao Projeto

Antes de configurar os health checks, é necessário garantir que a dependência do Spring Boot Actuator esteja incluída no projeto. Abaixo estão os exemplos para adicionar a dependência no Maven e no Gradle:

Maven

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

Gradle

implementation 'org.springframework.boot:spring-boot-starter-actuator'

Configuração do Spring Boot Actuator

O Spring Boot Actuator oferece endpoints para monitoramento e gerenciamento da aplicação. Um dos endpoints principais é o /actuator/health, que fornece informações sobre o status da aplicação.

A partir do Spring Boot 2.3, foram introduzidos endpoints específicos para liveness e readiness, e no Spring Boot 3 esses recursos continuam disponíveis. Porém, para habilitá-los de fato, é essencial ajustar a propriedade de forma correta, conforme mostra a documentação oficial:

management.endpoint.health.probes.enabled=true (Por padrão, esta propriedade é false, ou seja, desabilitada)

Esse ajuste é fundamental para expor os seguintes endpoints: - /actuator/health/liveness – para verificar se a aplicação está viva - /actuator/health/readiness – para verificar se a aplicação está pronta para receber tráfego

Configuração de Endpoints no application.properties ou application.yml

No seu arquivo de configuração (application.properties ou application.yml), inclua as seguintes linhas:

# Habilita a exibição de detalhes do health endpoint
management.endpoint.health.show-details=always

# Habilita os endpoints de liveness e readiness
management.endpoint.health.probes.enabled=true

Ou em application.yml:

management:
  endpoint:
    health:
      show-details: always
      probes:
        enabled: true

Após esse ajuste, o Actuator disponibiliza: - /actuator/health – endpoint padrão que agrega todos os checks disponíveis - /actuator/health/liveness – status de liveness da aplicação - /actuator/health/readiness – status de readiness da aplicação

Boa Prática: Separar a Porta do Actuator

Uma prática comum para aumentar a segurança e separar as preocupações de monitoramento do tráfego de produção é executar o Actuator em uma porta diferente daquela que o restante da aplicação utiliza. Por exemplo, ao expor a aplicação em 8080 e o Actuator em 9090, o Kubernetes pode consultar o Actuator internamente, sem necessidade de expor essa porta externamente em um Service ou Ingress.

Para fazer isso, basta adicionar a seguinte configuração no seu application.properties ou application.yml:

management.server.port=9090

Dessa forma, a aplicação principal fica em 8080, enquanto os endpoints do Actuator (incluindo /actuator/health/liveness e /actuator/health/readiness) são servidos em 9090.

Path Padrão do Actuator

Ao definir uma porta diferente para o management.server.port, o Actuator passa a ser servido no path raiz, ou seja, "/" em vez de "/actuator". Sendo assim, seus endpoints ficariam acessíveis em http://localhost:9090/health, http://localhost:9090/health/liveness, e assim por diante.

Caso você deseje manter o prefixo "/actuator" mesmo com a porta separada, pode usar a propriedade abaixo:

management.server.port=9090
management.context-path=/actuator

Assim, seus endpoints de saúde e demais endpoints do Actuator continuarão com o prefixo /actuator, por exemplo: - http://localhost:9090/actuator/health - http://localhost:9090/actuator/health/liveness - http://localhost:9090/actuator/health/readiness

Como Usar Liveness e Readiness no Kubernetes

Uma vez que a aplicação expõe endpoints distintos para liveness e readiness, e possivelmente em uma porta diferente (como 9090), basta configurar seu Deployment (ou outro objeto de workload) do Kubernetes para apontar para os endpoints corretos. Exemplo em YAML (trecho de um Deployment):

apiVersion: apps/v1
kind: Deployment
metadata:
  name: minha-aplicacao
spec:
  replicas: 1
  selector:
    matchLabels:
      app: minha-aplicacao
  template:
    metadata:
      labels:
        app: minha-aplicacao
    spec:
      containers:
      - name: minha-aplicacao-container
        image: minha-aplicacao:latest
        ports:
          - containerPort: 8080  # Porta principal da aplicação
          - containerPort: 9090  # Porta do Actuator
        livenessProbe:
          httpGet:
            path: /health/liveness
            port: 9090
          initialDelaySeconds: 10
          periodSeconds: 10
          timeoutSeconds: 2
          successThreshold: 1
          failureThreshold: 3
        readinessProbe:
          httpGet:
            path: /health/readiness
            port: 9090
          initialDelaySeconds: 5
          periodSeconds: 5
          timeoutSeconds: 2
          successThreshold: 1
          failureThreshold: 3

Detalhes importantes

  1. initialDelaySeconds: tempo (em segundos) que o Kubernetes espera antes de realizar o primeiro check. Importante para dar tempo à aplicação de subir.

  2. periodSeconds: intervalo (em segundos) entre as execuções do health check. Mantenha esse valor consistente para checagens regulares.

  3. timeoutSeconds: o tempo máximo (em segundos) que o Kubernetes aguardará a resposta do endpoint antes de considerar a verificação como falha.

  4. successThreshold: número consecutivo de sucessos para o contêiner ser considerado saudável (liveness) ou pronto (readiness).

  5. failureThreshold: número consecutivo de falhas para o contêiner ser considerado não saudável (liveness) ou não pronto (readiness).

  6. Portas: observe que a porta 9090 (Actuator) não precisa ser exposta em um Service externo, pois fica restrita para uso interno do Kubernetes (kubelet). Já a porta 8080 é a que pode ser exposta como Service e configurada no Ingress/Gateway.

Boas Práticas

  1. Não Bloquear o Liveness O Liveness Probe serve para verificar se a aplicação está viva; não coloque nele checks complexos que dependam de recursos externos (ex.: banco de dados). Ele deve, preferencialmente, verificar se a aplicação (JVM + container) não está travada.

  2. Configurar o Readiness para Verificar Dependências Críticas O Readiness Probe pode (e deve) incluir checks que verificam se a aplicação depende de algo para processar requisições. Se a aplicação requer banco de dados ou outros serviços externos, isso deve ser incluído no readiness para marcar a aplicação como pronta apenas quando suas dependências estiverem disponíveis.

  3. Definir Intervalos e Thresholds Coerentes Ajuste o initialDelaySeconds, periodSeconds, timeoutSeconds, successThreshold e failureThreshold conforme o tempo de inicialização e comportamento da sua aplicação. Dessa forma, você evita reinícios prematuros ou atrasos muito grandes para identificar problemas.

  4. Separar Porta do Actuator Como mencionado, usar management.server.port em uma porta distinta (por exemplo, 9090) é uma prática interessante para manter endpoints de monitoramento restritos ao tráfego interno do cluster, garantindo mais segurança e melhor organização.

    • Lembre-se que ao definir essa porta (por exemplo, 9090), o caminho padrão dos endpoints do Actuator passa a ser "/". Se quiser manter "/actuator", não esqueça de definir management.context-path=/actuator.

  5. Testar Localmente Antes de Subir em Produção Verifique se o comportamento no Kubernetes corresponde ao esperado. Faça testes interrompendo serviços externos para confirmar se o readiness muda para “não pronto” e se o liveness eventualmente faz o Kubernetes reiniciar o pod quando ele está realmente inoperante.

  6. Combine com Graceful Shutdown Para evitar problemas ao encerrar containers, é fundamental realizar um Graceful Shutdown que permita a finalização adequada das requisições em trânsito. Confira este artigo sobre a importância do Graceful Shutdown em containers e como implementar no Kubernetes e Spring Boot.

Conclusão

Usar liveness e readiness de forma adequada é essencial para garantir que o Kubernetes gerencie corretamente os pods em seu cluster. Enquanto o liveness se preocupa em reiniciar pods que estão em estado irrecuperável, o readiness assegura que apenas pods prontos (e com dependências críticas disponíveis) recebam tráfego. Com o Spring Boot 3 e o Actuator, esses checks são fáceis de implementar e fornecem alta visibilidade do estado real da aplicação — desde que você se lembre de:

  • Habilitar os endpoints com a propriedade management.endpoint.health.probes.enabled=true.

  • Separar a porta do Actuator (opcional, mas recomendado) com management.server.port, se desejar restringir o acesso a esses endpoints.

  • Ajustar o prefixo via management.context-path=/actuator, caso queira manter "/actuator" mesmo na porta separada.

Não se esqueça de dar atenção ao Graceful Shutdown para garantir uma finalização suave das requisições em trânsito, evitando falhas ou perdas de dados.

Gostou do conteúdo? Compartilhe e fique de olho no blog para mais dicas sobre Kubernetes, Spring Boot e boas práticas de desenvolvimento em nuvem!

// Compartilhe esse Post

💫
🔥 NOVO APP

Domine o Inglês em 30 dias!

Inteligência Artificial + Repetição Espaçada • Método cientificamente comprovado

✅ Grátis para começar 🚀 Resultados rápidos
×