← Todos os casos
Recuperar

War room: o disco do hipervisor encheu e derrubou 14 nós de produção

4,8 TBde espaço morto por trás da queda de 14 nós

A madrugada

De madrugada, o pool de armazenamento ZFS de um dos hipervisores Proxmox chegou a 99,8% de uso e levou junto mais de 14 nós do cluster Kubernetes de produção. O incidente durou cerca de 14 horas, até os últimos nós novos, criados em outro hipervisor, ficarem disponíveis.

A explicação óbvia era que os dados tinham crescido. Ela estava certa, mas incompleta, e um pós-mortem que para na explicação óbvia não evita a próxima queda.

O pool estava prometido quatro vezes

Comecei pela API do Proxmox, em modo somente leitura, levantando os nós, o armazenamento e os discos de cada VM. Havia 62 TB provisionados num pool físico de 14 TB, um overcommit de 4,3 para 1. Só as quatro VMs de banco tinham 6,5 TB cada, em thin provisioning. E o hipervisor não tinha backup, replicação nem monitoramento de capacidade.

Reconstruí a linha do tempo da queda e da recuperação a partir dos eventos de transição dos próprios nós do Kubernetes, e cruzei com os outros quatro incidentes do dia. Um alerta de disponibilidade da API era legítimo, porque a API estava mesmo degradada. Outro podia estar ligado a um serviço que rodava num nó que caiu. Um terceiro não tinha relação nenhuma: era um problema de permissão.

Blocos zumbis

Os dados de verdade não explicavam a velocidade com que o pool encheu, então fui mais fundo. O TRIM estava desligado em todos os 47 volumes do armazenamento distribuído do Kubernetes, o Longhorn. O MongoDB libera blocos a cada checkpoint e avisa o disco, mas o Longhorn ignorava o aviso, e o ZFS nunca recebia o espaço de volta.

Isso vinha acumulando havia mais de 500 dias. Em cada réplica do MongoDB, o sistema de arquivos mostrava cerca de 437 GB, enquanto o volume ocupava 1,1 TB. Só no MongoDB eram uns 4,8 TB de espaço morto, multiplicados pelas três réplicas do Longhorn. Vários volumes já estavam acima do tamanho provisionado, chegando a 135%. E o MongoDB não tinha TTL: os campos de data eram gravados como números inteiros, então o índice TTL nativo nunca funcionaria sem mudar a aplicação.

Um erro meu, corrigido em voz alta

No meio do incidente, olhando um dos alertas do dia, minha primeira leitura foi de que a regra estava mal configurada. Quando abri a regra inteira, vi que ela estava certa e que a API estava mesmo fora do ar. Corrigi o registro na hora. O problema real era outro: a página de status pública dizia "todos os sistemas operacionais" enquanto a API respondia 503.

O pós-mortem

A causa raiz ficou registrada como a combinação de três decisões: overcommit de 4,3 para 1, TRIM desligado com três réplicas por volume e dados sem expiração. Levantei nove falhas de processo, cinco delas com prioridade P0 ou P1, como a falta de alerta de capacidade no hipervisor e a ausência de backup e replicação.

A correção de maior impacto não dependia do time de desenvolvimento: ligar o TRIM nos volumes e rodar o fstrim, com uma estimativa de 3 a 5 TB recuperados, além de corrigir os volumes acima do limite e criar o alerta de capacidade do ZFS.

O que mudou

A causa raiz foi além do "os dados cresceram" e chegou ao mecanismo que fazia o espaço sumir. O plano de ação ficou priorizado, com a correção mais importante ao alcance da própria infraestrutura, e as nove falhas de processo foram registradas para não se repetirem.

O que eu levo disso

A explicação óbvia raramente é a causa raiz inteira. "O disco encheu porque os dados cresceram" era verdade, mas 4,8 TB de espaço morto não eram dados. Vieram de três decisões razoáveis quando vistas separadas, e que juntas formavam uma bomba-relógio.

E, no meio de um incidente, corrigir uma leitura errada em voz alta vale mais do que defendê-la.