Observabilidade consolidada: 620 painéis em 5 dashboards para 50 em 1
Cluster gov federal: -88% painéis por máquina, alertas Postgres com roteamento semântico, dois incidentes capturados antes do usuário nas primeiras 48h.
DevOps/Infra Engineer (Estagiário) · Consultoria AWS Select Partner
- Grafana
- Prometheus
- Loki
- Promtail
- Alertmanager
- PostgreSQL
- Node Exporter
- Blackbox Exporter
TL;DR
- 5 dashboards e ~620 painéis consolidados em 1 dashboard / ~50 painéis (-88% por máquina, -92% no total).
- 13/13 alertas Postgres entregues (10 infra + 3 app), com roteamento semântico via Alertmanager.
- 8 rotas de API gov monitoradas (Blackbox OAuth2 + fallback Python), 4 abordagens avaliadas, 2 selecionadas por trade-off documentado.
Contexto
O ambiente era um cluster de produção de um cliente gov federal mantido por uma Consultoria AWS Select Partner. A operação herdou cinco dashboards Grafana criados ao longo do tempo por times diferentes, totalizando aproximadamente 620 painéis. Não havia SLA pactuado por escrito, e os alertas viviam em canal de chat sem severidade nem rota — qualquer ruído acabava silenciado por adaptação humana.
Quando algo quebrava em produção, o plantão precisava abrir as cinco visualizações em sequência para correlacionar disco, RAM, banco e rota de API. O custo cognitivo era alto e o tempo até decisão ficava no nível do “perceptível, mas sem baseline formal”.
Capacity rodava com risco silencioso: durante a auditoria inicial, encontramos uma máquina de aplicação com disco em 92,1% sem alarme configurado.
Ação
A entrega foi quebrada em um pipeline de oito estágios (S0–S8) com 12 ADRs e 24 decisões técnicas registradas. Três frentes principais:
Consolidação dos dashboards
Inventário completo, classificação dos painéis por intenção (capacity vs. saúde vs. performance) e padronização de thresholds:
- Visual: 70 / 85 (warning / critical) para CPU, RAM e disco.
- Alerta: 80 / 90, com banda de tolerância para evitar flapping em sazonalidades de ETL.
Resultado: de 124 painéis por máquina para 12 (-88%), e ~620 totais para ~50 (-92%). Cada painel sobrevivente respondia uma pergunta operacional explícita, escrita na descrição.
Alertas Postgres
Treze alertas modelados (10 infra + 3 app), agrupados por severidade e rota de notificação. Baseline foi descoberto empiricamente medindo o pico real de queries simultâneas durante janelas de ETL/GIS — cerca de 35 — para então fixar um threshold realista em > 45 queries ativas por mais de 5 minutos.
# recording_rules.yaml — trecho ilustrativo
groups:
- name: postgres-saturation
interval: 30s
rules:
- record: pg:active_queries:5m
expr: avg_over_time(pg_stat_activity_count{state="active"}[5m])
- alert: PgQueryPressureHigh
expr: pg:active_queries:5m > 45
for: 5m
labels:
severity: warning
team: db
annotations:
summary: "Pressão sustentada de queries ativas (baseline ~35, threshold 45)"
Quatro bloqueios reais consumiram tempo aqui — VPN, cross-network, security group tcp/9100 e SMTP 535 — e estão documentados como aprendizado de infra real, não como vitrine.
Monitoramento de APIs
Quatro abordagens foram avaliadas para autenticar contra um broker OAuth2 gov: probe HTTP simples, exporter custom em Go, exporter Python com cache de token, e Blackbox com módulo OAuth2 nativo. Duas selecionadas:
- Blackbox Exporter com módulo OAuth2 para 4 rotas internas com renovação automática.
- Fallback Python exporter para as 4 rotas que retornavam payload condicional ao escopo do token (Blackbox cobria status, exporter mediu integridade da resposta).
Scrape configurado em janelas de 10 minutos para respeitar o rate limit do broker.
# blackbox.yml — módulo oauth2 (anonimizado)
modules:
api_gov_oauth2:
prober: http
timeout: 8s
http:
method: GET
headers:
Authorization: "Bearer ${OAUTH_TOKEN}"
valid_status_codes: [200, 204]
fail_if_body_not_matches_regexp:
- '"status"\s*:\s*"ok"'
Trade-offs e o que NÃO fiz
- Dual Grafana vs. instância única (DT-10): considerei manter uma instância única consolidada para reduzir overhead operacional, mas escolhi dual Grafana (uma para infra, outra para aplicação) por isolamento de blast radius — uma falha de plugin ou upgrade quebrado em um lado não derrubava a observabilidade do outro. O custo é manter dois conjuntos de dashboards sincronizados; o ganho é poder fazer manutenção sem ficar sem visão.
- Não migrei alertas para Grafana Alerting: Alertmanager ficou como roteador porque o time já tinha rotina operacional sobre ele; trocar agora seria churn sem ganho.
- Não escrevi exporter custom em Go apesar da tentação técnica: Python entregou em 1/3 do tempo e a equipe consegue manter.
- Não cobri tracing distribuído (OpenTelemetry): ficou no backlog explícito como próximo ciclo — ROI maior agora estava em alertas confiáveis, não em traces extra.
Resultado
Nas primeiras 48h após a entrada em produção dos 13 alertas Postgres, dois incidentes legítimos foram capturados antes de qualquer reclamação chegar ao suporte:
- Disco em 93,5% numa máquina de aplicação — o threshold 90 disparou, time agiu em janela planejada, sem indisponibilidade.
- Queries lentas sustentadas acima do threshold de 45 ativas durante uma janela ETL não anunciada — investigação reduziu o pico futuro com índice ajustado.
MTTR formal não tinha baseline antes da consolidação, então o ganho está descrito qualitativamente (“plantão deixou de abrir cinco dashboards para abrir um”); SLA segue como meta pactuada com o PO do cliente, sem número público.
A entrega final somou 22 tasks em 4 fases, 9 tópicos de research documentados e 5h efetivas em 4 sessões focadas para a fase de alertas Postgres — o que importou aqui não foi a velocidade, e sim que a configuração sobreviveu ao primeiro fim de semana sem precisar de mim.