Modernizando uma stack de observabilidade com Grafana e Prometheus
A stack tinha ficado para trás: o Grafana além do fim de vida (end-of-life), o Prometheus com oito versões minor de atraso e os dashboards pesados levando dezenas de segundos para renderizar. Veja como levei ambos ao LTS e deixei esses dashboards cerca de 30 vezes mais rápidos, sem perda de dados e sem tocar nos alertas.
O trabalho combinou upgrades de versão, recording rules e validação operacional.
Contribuição em resumo
- Minha responsabilidade
- Upgrades faseados do monitoramento, limpeza de dashboards e trabalho com recording rules.
- Escopo
- Modernização do Grafana e do Prometheus com backups e validação operacional.
- Colaboração & limites
- Mantive a stack de monitoramento e verifiquei o comportamento de queries e dashboards ao longo das mudanças de versão.
- Resultado
- Atualizei a stack de monitoramento e preservei os dashboards migrados; este caso não estabelece uma melhoria de latency em toda a frota.
Stacks de observabilidade apodrecem em silêncio. Os upgrades parecem arriscados, então são adiados, e um dia você está num Grafana em fim de vida, com dashboards em que ninguém confia e queries que levam meio minuto. Este é um relato de como trouxe uma dessas stacks de volta ao estado atual: duas versões major do Grafana, uma atualização de LTS no Prometheus e as recording rules que foram a verdadeira vitória.
Grafana: do fim de vida ao 11.6 LTS
O Grafana estava numa release em fim de vida, a uma versão major de distância de o runtime de plugins Angular ser removido por completo. Levei-o ao 11.6 LTS de um jeito deliberado e reversível:
- Atualização de imagem em duas etapas (passando por uma minor intermediária) em vez de um único grande salto, para que cada etapa fosse pequena e fácil de fazer rollback.
- Remoção do runtime Angular. O Grafana 11 desativa o runtime de plugins Angular, então primeiro desinstalei os plugins de painel Angular legados e depois conduzi os dashboards de schema antigo pela automigração, para que os novos tipos de painel em React fossem preservados.
- Limpeza de dashboards. Exportei todos os dashboards da instância em produção para o git (ela tinha divergido do banco de dados) e depois apaguei os abandonados.
- Backups defensivos. Snapshots de disco antes de cada mudança. Ambos ficaram sem uso, o que é exatamente como backups deveriam terminar.
- Zero interrupção. Todas as alert rules e os datasources passaram pelo upgrade sem alterações. Uma passagem posterior o levou à próxima versão major, com uma auditoria completa de dashboards.
A verdadeira vitória: recording rules do Prometheus
Os dashboards pesados de ingress eram sofríveis: de 8 a 33 segundos por render. A causa era o histogram_quantile() sendo recalculado a partir de dezenas de milhares de séries de bucket (cerca de 57k) a cada atualização. A correção é clássica e fácil de esquecer de usar: recording rules.
Adicionei três recording rules por ambiente que pré-agregam o P50, o P90 e o P99 do histograma de duração de requisições, e apontei os dashboards para as séries pré-computadas em vez dos buckets brutos.
| Ambiente | Antes | Depois | Aceleração |
|---|---|---|---|
| Produção (cardinalidade maior) | 8 a 33 s | ~0.66 s | ~30× |
| Sandbox (cardinalidade menor) | 3.5 a 9 s | ~0.66 s | ~5 a 13× |
Prometheus: 2.45 to 2.53 LTS
Atualizei o Prometheus em oito versões minor até o 2.53 LTS, de forma faseada e segura: primeiro o cluster de sandbox, depois a produção após um período de observação de 24 horas. Uma restrição do mundo real que vale mencionar: a stack roda o fork gerenciado (GMP) do Prometheus porque os alertas e dashboards do Cloud Monitoring do ambiente dependem do exporter embutido dele. O Prometheus upstream já está na 3.x, mas o fork gerenciado vai no máximo até o 2.53 LTS, então esse era o teto. Saber em que fork você está, e por quê, é metade do upgrade.
Um refactor de config que vale a pena
As configs do Prometheus por ambiente tinham divergido em ordens de seção diferentes, o que tornava trabalhoso fazer o diff de um ambiente contra outro. Reordenei-as para uma única estrutura canônica (persistent volumes, config, RBAC, StatefulSet, services, ingress). Nenhuma mudança funcional, mas agora um diff lado a lado mostra só as diferenças que importam.
Um bug latente, encontrado de passagem
Enquanto validava as recording rules, encontrei um painel exibindo "No data" sobre uma query que claramente tinha dados. A causa era uma config de thresholds do Grafana (thresholdsStyle: line+area) com um passo de threshold final sem o seu valor. O tipo de bug que se esconde por meses até alguém olhar de perto o painel certo.
O que eu diria a quem está começando isto
- Recorra às recording rules antes de culpar o dashboard. Se um painel recalcula um quantil sobre dezenas de milhares de séries a cada render, pré-agregue-o.
- Faça upgrade em etapas pequenas e reversíveis, e tire um snapshot antes de cada uma. O salto do fim de vida ao LTS assusta menos como dois saltos pequenos do que como um grande.
- Conheça o seu fork. O fork gerenciado do Prometheus e o upstream não seguem a mesma linha de releases; escolha o LTS mais alto que o seu fork realmente entrega.
- Mantenha a config em uma ordem canônica entre os ambientes. Divergência na estrutura esconde divergência no significado.
Decisões e próximos passos
Uma visão retrospectiva da abordagem e seus limites. A próxima melhoria é uma proposta, não um resultado entregue.
Alternativa e justificativa
Um único upgrade grande reduz etapas, mas combina modos de falha. Mudanças faseadas e backups tornam as incompatibilidades mais fáceis de isolar.
Trade-off
Recording rules reduzem o trabalho repetido de query, mas introduzem definições adicionais cujo significado e atualidade precisam ser mantidos.
O que eu melhoraria em seguida
Próxima melhoria: manter queries de dashboard representativas e testes de regras em uma suíte repetível de aceitação de upgrade.