← todos os artigos

3 erros que quebram o Prometheus em produção e como corrigir cada um

0

Prometheus é uma das ferramentas mais sólidas que uso no dia a dia. Mas ele tem uma característica que aprendi da forma difícil: aceita tudo que você joga nele até o momento em que não aceita mais.

Sem aviso. Sem graceful shutdown. Só um OOM e silêncio total no dashboard.

Já vi isso acontecer por três motivos diferentes. Todos evitáveis. Todos silenciosos até o momento errado. Vou contar cada um com o que causou, como detectar antes que vire incidente, e como corrigir.

Erro 1: cardinalidade explosiva em labels

O Prometheus armazena cada combinação única de métrica + labels como uma série temporal separada na memória. Isso é o que torna ele poderoso. E é exatamente o que mata ele quando você não presta atenção.

Um request_id como label parece inofensivo. Mas em produção, com milhares de requisições por segundo, cada ID único vira uma série nova. Em horas, você tem dezenas de milhões de séries ativas. O processo não avisa — ele simplesmente consome toda a RAM disponível e morre.

Labels que explodem cardinalidade quase sempre são os mesmos: user_id, request_id, trace_id, session_id, caminhos de URL brutos. Se o valor pode ser diferente a cada requisição, não é label — é dado de trace.

Como perceber que está acontecendo

# Séries ativas no TSDB — saudável: 100k–2M. Acima de 5M: problema. Acima de 10M: urgente.
prometheus_tsdb_head_series

# Quais métricas estão explodindo
topk(10, count by (__name__) ({__name__=~".+"}))

# Taxa de criação de séries novas — churn acelerado é sinal de label problemático
rate(prometheus_tsdb_head_series_created_total[5m])

Como corrigir

Se você não controla o exporter, dropa o label antes que entre no TSDB:

metric_relabel_configs:
  - regex: '(request_id|trace_id|user_id|session_id)'
    action: labeldrop

E coloca um alerta pra não ser pego de surpresa de novo:

- alert: CardinalidadeAlta
  expr: prometheus_tsdb_head_series > 5000000
  for: 10m
  labels:
    severity: warning
  annotations:
    summary: "{{ $value }} séries ativas  investiga antes de virar OOM"

Erro 2: retention sem planejamento de storage

Esse é mais silencioso. Não mata o Prometheus na hora — mata ele no pior momento possível.

O default é 15 dias de retenção. Na maioria dos setups, ninguém muda. O problema é que o número de séries cresce com o tempo — novos serviços, novos exporters, novos labels — mas o disco fica o mesmo. Em algum ponto, o volume diário de dados supera o que o disco aguenta para 15 dias.

Sem o parâmetro --storage.tsdb.retention.size configurado, o Prometheus vai consumir todo o disco disponível. Com ele configurado errado, você perde exatamente o histórico que precisaria pra fechar um postmortem.

Já vi time passar horas tentando entender a causa raiz de um incidente sem conseguir — porque o Prometheus tinha deletado os dados de 3 semanas atrás que mostrariam o padrão.

Como acompanhar

# Uso atual do TSDB em bytes
prometheus_tsdb_storage_blocks_bytes

# Crescimento diário estimado
rate(prometheus_tsdb_storage_blocks_bytes[24h])

Como configurar direito

O ponto não é aumentar o tempo de retenção. É combinar os dois parâmetros de forma que o disco nunca estoure e você nunca perca dados antes do prazo esperado.

--storage.tsdb.retention.time=7d
--storage.tsdb.retention.size=50GB

O primeiro limite atingido prevalece. Se o volume cresceu mais do que o esperado e o disco está chegando em 50GB antes dos 7 dias, o Prometheus deleta os blocos mais antigos automaticamente. Você não perde a instância — perde só o histórico mais velho, de forma controlada.

Sem o retention.size, o Prometheus vai crescer até ocupar todo o disco disponível. Aí não tem deleção controlada: tem crash.

Uma coisa que pouca gente lembra: o WAL (write-ahead log) ocupa espaço além dos blocos compactados. Em instâncias com alta ingestão, ele pode representar 2–3x o tamanho do head block. Monitora separado.

E se você precisa de histórico longo — mais de 30 ou 60 dias — o Prometheus não é a ferramenta certa pra isso. Thanos, Mimir ou VictoriaMetrics existem pra isso. Deixa o Prometheus local com janela curta e delega o histórico longo pra quem foi projetado pra aguentar.

Erro 3: scrape interval curto demais em targets pesados

Esse erro nasce de uma boa intenção: “mais granularidade = melhor observabilidade”. Em alguns casos é verdade. Em outros, você está criando o problema que quer evitar.

O default do Prometheus é 1 minuto. Muitos times reduzem pra 15s ou 10s. O problema aparece quando você aplica esse interval em exporters pesados. O kube-state-metrics e o node-exporter em clusters grandes são os casos mais comuns. Um node-exporter num cluster de 50 nodes pode gerar facilmente 100k samples por scrape. Com interval de 15s, você está ingerindo centenas de milhares de samples por segundo só de infra, antes de qualquer métrica de aplicação.

O sintoma: scrape_duration_seconds maior que o próprio interval. Quando isso acontece, o Prometheus começa a enfileirar scrapes, atrasa alertas, e passa a consumir mais CPU do que os workloads que ele deveria monitorar.

Como detectar

# Targets onde o scrape está demorando mais que o interval
scrape_duration_seconds > 55  # para interval de 1m

# Volume de samples por job
scrape_samples_scraped

# Scrapes que excederam o limite de samples
increase(prometheus_target_scrapes_exceeded_sample_limit_total[1h])

Como corrigir

Diferencia o interval por job. Não tem por que scrapear métricas de disco a cada 15 segundos.

scrape_configs:
  - job_name: 'app-latency'
    scrape_interval: 15s
    static_configs:
      - targets: ['app:9090']

  - job_name: 'node-exporter'
    scrape_interval: 5m
    static_configs:
      - targets: ['node-exporter:9100']

E coloca sample_limit como proteção: se um exporter mal configurado começar a gerar samples demais, o scrape falha. O Prometheus continua de pé.

scrape_configs:
  - job_name: 'app'
    sample_limit: 50000

Melhor perder um scrape do que perder a instância inteira.

O que os três erros têm em comum

Nenhum deles aparece de uma vez. Todos crescem devagar, por semanas, até o dia em que você precisa do Prometheus funcionando. E ele não está.

A boa notícia é que os três são detectáveis antes de virar incidente, com as queries certas e alertas no lugar certo.

Observabilidade que não monitora a si mesma não é observabilidade.