
Postmortem — O failover do Postgres que me ensinou o que é PDB
Este é o primeiro postmortem do curso. Ele não é um conto de herói: é o registro de um teste de carga que saiu do controle e do que aprendi consertando.
Resumo
Durante um ensaio de capacidade no cluster de homolog, subi o HPA do backend para 10 réplicas e disparei uma rampa de 50 usuários autenticados. O teste não devolveu erros HTTP, mas a latência p95 foi para 8,4 segundos. Minutos depois, o primário do PostgreSQL perdeu os probes de liveness e o CloudNativePG iniciou um failover. O antigo primário ficou preso em shutdown e precisei removê-lo para o cluster promover a réplica.
Nenhum dado foi perdido. O serviço continuou respondendo via pods antigos durante a maior parte do incidente. Mas o susto foi grande — e as lições, valiosas.
Linha do tempo (resumida)
| Hora | Evento |
|---|---|
| T+0 | HPA ajustado para maxReplicas: 10; teste de 50 VUs autenticados inicia |
| T+1 min | Workers chegam a 92% e 82% de memória |
| T+2 min | Backend escala para 10 pods; p95 sobe para 8,4 s |
| T+3 min | Probes do Postgres começam a falhar por timeout |
| T+5 min | CNPG inicia failover; primário antigo entra em shutdown |
| T+8 min | Primário antigo não termina o shutdown; cluster fica Failing over |
| T+10 min | Removo o pod do primário antigo; CNPG promove a réplica |
| T+12 min | Cluster healthy; HPA reduzido para 6; latência normaliza |
Causa raiz
Não foi “o Kubernetes”. Foi uma combinação de decisões:
- HPA sem orçamento de memória. O limite de 10 pods foi definido pensando em CPU, mas cada pod de backend usava ~1,2–1,3 GiB. Em workers de 8 GiB, 10 pods competiam com banco, Elasticsearch, MinIO e RabbitMQ.
- Pressão de memória nos nós. Com 92% de uso, o kubelet e os probes passam a sofrer. O Postgres não foi morto por OOM, mas os probes de liveness/readiness começaram a estourar timeout.
- Probe agressiva em um banco sob carga. O liveness do CNPG usa o instance manager; sob contenção de CPU/rede, o timeout de 25 s foi suficiente para marcar o primário como doente.
- Failover sem plano de fencing. O primário antigo não terminou o shutdown; o cluster ficou em
Failing overaté a remoção manual do pod.
O que funcionou
- O CNPG promoveu a réplica corretamente assim que o primário antigo saiu.
- O backend continuou servindo com os pods antigos; não houve queda total.
- O PDB do Postgres impediu evictions acidentais antes da hora (e depois mostrou por que existe).
- O runbook de failover foi escrito a partir do incidente e ficou no repositório.
O que não funcionou
- Ajustar HPA só por CPU, ignorando memória por pod.
- Rodar carga autenticada sem olhar
kubectl top nodesa cada minuto. - Testar pelo NodePort direto: o rate limit viu o IP de NAT (
10.244.1.0) e gerou 429, mascarando o resultado real. Pelo ingress, o mesmo teste teve 0% de erro. - Deixar o HPA no máximo durante o failover, pressionando ainda mais os nós.
Ações tomadas
| Ação | Resultado |
|---|---|
HPA reduzido para maxReplicas: 6 |
Estabilidade imediata |
| Workers ampliados para 16 GiB | Margem para 8–10 pods com dados |
| Teste refeito pelo ingress | 0% de erro, p95 83,9 ms em 50 VUs |
| Runbook de failover CNPG | Procedimento documentado |
| Plano de capacidade | HPA máximo por orçamento de memória, não só CPU |
Lições
- HPA é uma decisão de capacidade, não um número mágico. Calcule memória por pod × réplicas × nós.
- PDB protege, mas não salva de falta de recurso. Ele evita evictions; não impede o nó de ficar sem memória.
- Probe de banco é sensível a contenção. Ajuste timeout e
failureThresholdcom dados reais. - Failover precisa de fencing. Se o primário antigo não morre, o cluster não promove a réplica sozinho.
- Teste pelo caminho real. NodePort direto não representa o tráfego do ingress.
- Postmortem sem culpa. O objetivo é corrigir o sistema, não a pessoa que apertou o botão.
Checklist para o seu cluster
- [ ] Sei quanta memória cada réplica do backend usa.
- [ ] O HPA tem
maxReplicascompatível com a memória dos workers. - [ ] O Postgres tem PDB e anti-affinity.
- [ ] Sei o que fazer se o primário antigo não terminar o shutdown.
- [ ] Testo carga pelo ingress, não pelo NodePort.
- [ ] Tenho runbook de failover e restore.
Próximo
No capítulo 05 vamos montar o control plane em HA — e entender por que o etcd com quórum é a diferença entre “alta disponibilidade” e “três máquinas que caem juntas”.
Continuar a ler
Leia também
KubernetesKubernetes de Verdade — do zero ao cluster em produção
Uma trilha prática para entender, montar e operar um cluster Kubernetes — do primeiro pod ao failover do Postgres.
4 min de leitura
KubernetesCurso Kubernetes 28 — DR e capstone: provar que o cluster volta
RPO, RTO e drills reais: cenários de desastre, runbook, restore medido e o capstone que fecha o curso com um cluster que se recupera.
5 min de leitura
KubernetesCurso Kubernetes 27 — SLO/SLI, on-call e postmortem blameless
O que prometer, como medir e o que fazer quando o erro estoura: SLI, SLO, error budget, alertas que acordam alguém e postmortem sem caça às bruxas.
4 min de leitura
Comentários
Ainda sem comentários
Seja o primeiro a compartilhar sua opinião sobre este artigo.
Deixe um comentário