Os health checks do Kubernetes são uma daquelas funcionalidades que parecem óbvias na documentação e depois surpreendem na primeira vez que um deployment quebra silenciosamente em produção. Já perdi mais tempo do que gostaria a depurar pods que estavam tecnicamente a correr mas sem servir nada de útil, tudo porque as probes estavam mal configuradas ou simplesmente ausentes.
Este artigo não é uma repetição dos conceitos básicos do Kubernetes. É especificamente sobre as probes de liveness, readiness e startup — o que fazem de facto, como interagem entre si, e os erros que me custaram tempo de inatividade real.
Três Probes, Três Funções
O Kubernetes tem três tipos de probes. É fácil confundi-las porque todas respondem à questão "este container está bem?" — mas têm significados diferentes para o scheduler.
Liveness responde: este container deve ser reiniciado? Se falhar, o Kubernetes mata e reinicia o container. Use-a para detetar deadlocks ou estados corrompidos que a aplicação não consegue recuperar por si própria.
Readiness responde: este container deve receber tráfego? Se falhar, o pod é removido dos endpoints do Service. O container continua a correr — apenas deixa de receber pedidos. Use-a para startup lento, verificação de dependências, ou degradação controlada.
Startup responde: o container terminou a inicialização? Bloqueia as outras duas probes. Enquanto a startup probe está ativa, liveness e readiness não correm. Isto é muito importante para aplicações JVM de arranque lento ou serviços .NET que carregam caches grandes.
O Erro Clássico: Usar Liveness para Tudo
O erro mais comum que vejo é usar a liveness probe para verificar dependências externas — uma base de dados, uma cache, uma API downstream. A lógica parece razoável: se a base de dados estiver em baixo, a aplicação não está saudável, por isso reiniciá-la.
O problema é que reiniciar o pod não vai corrigir a base de dados. O resultado é um crash loop, o Kubernetes aumenta o intervalo com backoff exponencial, e uma indisponibilidade parcial transforma-se numa falha total.
A liveness só deve verificar coisas que o próprio pod consegue corrigir ao reiniciar. Se um restart não resolve o problema, a liveness é a probe errada.
A readiness é a ferramenta certa para dependências externas. Quando a base de dados está inacessível, o pod deixa de receber tráfego sem reiniciar. Quando recupera, o pod fica disponível novamente. Sem crash loop, sem falha em cascata.
Startup Probes para Serviços Lentos
Antes das startup probes existirem, a solução era definir initialDelaySeconds com um valor suficientemente grande para cobrir o pior caso de arranque. O problema: se o container falhasse imediatamente, o Kubernetes esperava o delay completo antes de reiniciar. Um atraso de 60 segundos a cada restart acumula-se rapidamente.
As startup probes resolvem isto de forma limpa. Dá-se ao container uma janela generosa para terminar o arranque — por exemplo, 30 verificações com intervalos de 10 segundos — e quando a startup probe passa, as probes de liveness e readiness mais apertadas assumem.
startupProbe:
httpGet:
path: /health/startup
port: 8080
failureThreshold: 30
periodSeconds: 10
Isto dá ao container até 5 minutos para arrancar. Depois, a liveness corre com as suas definições normais. Para uma aplicação .NET a carregar um ficheiro de modelo de 2GB, a diferença entre um deployment funcional e um restart loop perpétuo está aqui.
HTTP vs TCP vs Exec
As probes HTTP são as mais comuns e as mais úteis. Fazem um pedido a um endpoint e verificam o código de resposta. Qualquer valor entre 200 e 399 conta como sucesso. Controla-se o path, a porta e os headers.
As probes TCP apenas verificam se a porta está aberta. São úteis quando o serviço não fala HTTP — um servidor TCP puro, um serviço gRPC sem endpoint HTTP de health, ou um sidecar de base de dados.
As probes Exec executam um comando dentro do container. São flexíveis mas dispendiosas — cada probe faz fork de um novo processo. Adequado para verificações pouco frequentes, não para algo a correr a cada poucos segundos.
Para a maioria dos serviços HTTP, o padrão que uso é:
- Um endpoint
/health/liveque devolve 200 se o processo está a correr e não está em deadlock. Sem chamadas externas. - Um endpoint
/health/readyque verifica conectividade à base de dados, disponibilidade da cache, e tudo o que seja necessário para servir tráfego. - Um endpoint
/health/startupque devolve 200 apenas após o carregamento inicial de dados estar completo.
Manter estes endpoints separados no código torna a intenção explícita e evita o acoplamento acidental entre liveness e estado externo.
Definições de Threshold e Timing
Cada probe tem quatro parâmetros: initialDelaySeconds, periodSeconds, failureThreshold, e successThreshold.
initialDelaySeconds é maioritariamente obsoleto se estiver a usar startup probes. Sem elas, defina-o alto o suficiente para evitar falsos negativos durante o arranque.
periodSeconds controla a frequência da probe. O valor por defeito é 10 segundos. Para liveness, algo entre 10 e 30 costuma ser suficiente. Verificar a cada segundo é quase sempre excessivo e adiciona carga desnecessária.
failureThreshold define quantas falhas consecutivas são necessárias antes de se tomar ação. O valor por defeito é 3. Para liveness, isto significa 3 falhas seguidas antes de o Kubernetes reiniciar o container. Aumentar este valor dá mais margem para falhas transitórias — útil durante pausas de garbage collection ou picos de rede.
successThreshold para readiness controla quantos sucessos consecutivos são necessários antes de o tráfego ser restaurado. O valor por defeito é 1. Aumentar para 2 ou 3 previne flapping — o pod não começa a receber tráfego até estar consistentemente disponível.
Um Exemplo Real para uma API .NET
Aqui está uma configuração que usei numa API .NET 8 que se conecta a PostgreSQL e Redis:
startupProbe:
httpGet:
path: /health/startup
port: 8080
failureThreshold: 20
periodSeconds: 5
livenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 0
periodSeconds: 15
failureThreshold: 3
timeoutSeconds: 3
readinessProbe:
httpGet:
path: /health/ready
port: 8080
periodSeconds: 10
failureThreshold: 3
successThreshold: 2
timeoutSeconds: 5
A startup probe dá até 100 segundos para a aplicação inicializar. A liveness verifica a cada 15 segundos — suficiente para detetar um deadlock sem sobrecarregar o processo. A readiness exige dois sucessos consecutivos para restaurar o tráfego, o que previne flapping durante uma breve reconexão à base de dados.
Probes e Rolling Deployments
As readiness probes são especialmente importantes durante atualizações progressivas. O Kubernetes não encaminha tráfego para um novo pod até a sua readiness probe passar, e não termina os pods antigos enquanto os novos não estiverem prontos. Se a readiness probe estiver errada — demasiado permissiva ou a não verificar as coisas certas — os utilizadores podem aceder a pods novos que não estão realmente prontos para servir.
Já vi deployments a correr mal porque a readiness probe era apenas uma verificação TCP na porta, que passava assim que o processo arrancava, antes da aplicação ter terminado de se conectar às suas dependências. A correção foi mudar para uma verificação HTTP num endpoint que faz validação real das dependências.
Depurar Falhas de Probes
Quando as probes estão a falhar, kubectl describe pod é o primeiro passo. A secção Events mostra as falhas com detalhes. Para readiness, verifique se o pod está listado nos endpoints do Service com kubectl get endpoints.
Se não tiver a certeza do que o endpoint de health está a devolver, entre no pod e faça curl diretamente:
kubectl exec -it <pod-name> -- curl -v http://localhost:8080/health/ready
Isto remove a rede da equação e mostra exatamente o que a probe veria.
Vale a Pena Fazer Bem
As probes são uma superfície de configuração pequena com um impacto grande. Uma liveness probe mal configurada pode transformar uma falha momentânea da base de dados numa cascata de restarts. Uma readiness probe em falta significa que os utilizadores acedem a pods que não estão realmente prontos. Um threshold de startup demasiado apertado significa que o serviço nunca faz deploy com sucesso num nó lento.
Configurá-las corretamente não demora muito, uma vez que se percebe para que serve cada probe. A chave está em manter as preocupações de liveness e readiness separadas, usar startup probes para qualquer coisa que não arranque instantaneamente, e testar os endpoints de health que se configuram — em vez de assumir que funcionam.



