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
-
initialDelaySeconds: tempo (em segundos) que o Kubernetes espera antes de realizar o primeiro check. Importante para dar tempo à aplicação de subir.
-
periodSeconds: intervalo (em segundos) entre as execuções do health check. Mantenha esse valor consistente para checagens regulares.
-
timeoutSeconds: o tempo máximo (em segundos) que o Kubernetes aguardará a resposta do endpoint antes de considerar a verificação como falha.
-
successThreshold: número consecutivo de sucessos para o contêiner ser considerado saudável (liveness) ou pronto (readiness).
-
failureThreshold: número consecutivo de falhas para o contêiner ser considerado não saudável (liveness) ou não pronto (readiness).
-
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 porta8080
é a que pode ser exposta como Service e configurada no Ingress/Gateway.
Boas Práticas
-
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.
-
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.
-
Definir Intervalos e Thresholds Coerentes Ajuste o
initialDelaySeconds
,periodSeconds
,timeoutSeconds
,successThreshold
efailureThreshold
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. -
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 definirmanagement.context-path=/actuator
.
-
-
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.
-
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!