DevOps Admin

O que Todo Dev Deve Saber sobre Prometheus: Coleta de Métricas, PromQL e Alertmanager Já leu

Introdução ao Prometheus: Por que Monitorar? Quando você trabalha com sistemas em produção, é fundamental saber o que está acontecendo em tempo real. O Prometheus é uma ferramenta de código aberto que coleta, armazena e permite consultar métricas de seus aplicativos e infraestrutura. Diferentemente de ferramentas antigas que dependiam de agentes push (enviando dados), o Prometheus utiliza um modelo pull (buscando dados), o que oferece maior controle, resiliência e simplicidade operacional. A arquitetura do Prometheus é composta por alguns componentes principais: o servidor Prometheus (que scrapa métricas), o Alertmanager (que gerencia alertas), o Node Exporter (que fornece métricas do sistema) e ferramentas de visualização como Grafana. Neste artigo, você aprenderá como configurar o Prometheus, escrever consultas em PromQL e implementar um sistema robusto de alertas. Coleta de Métricas com Prometheus Como o Prometheus Funciona O Prometheus opera em um modelo simples: em intervalos regulares (definidos em sua configuração), ele acessa um endpoint HTTP (chamado ) em cada aplicação monitorada e

Introdução ao Prometheus: Por que Monitorar?

Quando você trabalha com sistemas em produção, é fundamental saber o que está acontecendo em tempo real. O Prometheus é uma ferramenta de código aberto que coleta, armazena e permite consultar métricas de seus aplicativos e infraestrutura. Diferentemente de ferramentas antigas que dependiam de agentes push (enviando dados), o Prometheus utiliza um modelo pull (buscando dados), o que oferece maior controle, resiliência e simplicidade operacional.

A arquitetura do Prometheus é composta por alguns componentes principais: o servidor Prometheus (que scrapa métricas), o Alertmanager (que gerencia alertas), o Node Exporter (que fornece métricas do sistema) e ferramentas de visualização como Grafana. Neste artigo, você aprenderá como configurar o Prometheus, escrever consultas em PromQL e implementar um sistema robusto de alertas.

Coleta de Métricas com Prometheus

Como o Prometheus Funciona

O Prometheus opera em um modelo simples: em intervalos regulares (definidos em sua configuração), ele acessa um endpoint HTTP (chamado /metrics) em cada aplicação monitorada e coleta as métricas expostas em formato texto. Essas métricas são armazenadas localmente no disco em um formato otimizado de série temporal. Esse modelo pull oferece vantagens significativas: você tem controle sobre quando os dados são coletados, os aplicativos não precisam conhecer sobre o Prometheus, e a ferramenta é mais resiliente a falhas temporárias de rede.

As métricas no Prometheus seguem uma nomenclatura específica. Cada métrica tem um nome (como http_requests_total) e pode ter múltiplas labels (dimensões) como method="GET" ou status="200". Existem quatro tipos de métricas: Counter (aumenta monotonicamente), Gauge (pode subir ou descer), Histogram (distribui em buckets) e Summary (fornece quantis).

Instalando e Configurando o Prometheus

Para começar, você precisa baixar e configurar o Prometheus. Crie um arquivo prometheus.yml:

global:
  scrape_interval: 15s
  evaluation_interval: 15s

alerting:
  alertmanagers:
    - static_configs:
        - targets: ['localhost:9093']

rule_files:
  - 'alerts.yml'

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  - job_name: 'node'
    static_configs:
      - targets: ['localhost:9100']

  - job_name: 'app'
    static_configs:
      - targets: ['localhost:8080']

Este arquivo define que o Prometheus coletará métricas a cada 15 segundos de três fontes: ele mesmo, um Node Exporter na porta 9100 e sua aplicação na porta 8080. Você pode iniciar o Prometheus com:

./prometheus --config.file=prometheus.yml

Instrumentando sua Aplicação

Para que sua aplicação exponha métricas, você precisa usar uma biblioteca cliente. Aqui está um exemplo com Python usando a biblioteca prometheus_client:

from prometheus_client import Counter, Gauge, Histogram, start_http_server
import time
import random

# Definir métricas
request_count = Counter(
    'http_requests_total',
    'Total de requisições HTTP',
    ['method', 'endpoint', 'status']
)

request_duration = Histogram(
    'http_request_duration_seconds',
    'Duração das requisições em segundos',
    ['method', 'endpoint']
)

active_connections = Gauge(
    'active_connections',
    'Número de conexões ativas'
)

# Iniciar servidor HTTP na porta 8080
start_http_server(8080)

# Simular requisições
while True:
    method = random.choice(['GET', 'POST', 'DELETE'])
    endpoint = random.choice(['/users', '/products', '/orders'])
    status = random.choice([200, 201, 400, 500])
    duration = random.uniform(0.1, 2.0)

    request_count.labels(method=method, endpoint=endpoint, status=status).inc()
    request_duration.labels(method=method, endpoint=endpoint).observe(duration)
    active_connections.set(random.randint(10, 100))

    time.sleep(1)

Ao rodar este script, sua aplicação expõe métricas em http://localhost:8080/metrics. O Prometheus coletará automaticamente esses dados de acordo com a configuração.

PromQL: A Linguagem de Consulta do Prometheus

Fundamentos da PromQL

PromQL (Prometheus Query Language) é uma linguagem poderosa e expressiva para consultar dados de série temporal. Diferentemente do SQL, ela foi projetada especificamente para trabalhar com métricas temporais. Uma consulta PromQL simples retorna o valor atual de uma métrica, mas sua verdadeira força reside em funções de agregação, transformação e análise temporal.

A sintaxe básica é intuitiva: você começa com o nome da métrica seguido de filtros entre chaves. Por exemplo, http_requests_total{method="GET"} retorna todas as métricas com nome http_requests_total que possuem o label method com valor GET.

Consultando Métricas Básicas

Aqui estão exemplos de consultas fundamentais que você usará constantemente:

# Retorna o valor atual de uma métrica
http_requests_total

# Filtra por um label específico
http_requests_total{method="GET"}

# Filtra por múltiplos labels
http_requests_total{method="GET", status="200"}

# Usa operadores de regex para filtrar
http_requests_total{endpoint=~"/users.*"}

# Exclui valores com um operador negativo
http_requests_total{status!="200"}

Essas consultas retornam séries temporais instantâneas. Para análises mais úteis, você precisa de funções que operam sobre períodos de tempo.

Funções e Operações Temporais

As funções mais importantes no PromQL trabalham com períodos de tempo (ranges). A sintaxe [5m] significa "últimos 5 minutos". Confira os exemplos práticos:

# Taxa de aumento por segundo nos últimos 5 minutos
rate(http_requests_total[5m])

# Aumento absoluto nos últimos 10 minutos
increase(http_requests_total[10m])

# Valor máximo nos últimos 1 hora
max_over_time(http_request_duration_seconds[1h])

# Valor mínimo nos últimos 30 minutos
min_over_time(http_request_duration_seconds[30m])

# Média de uma métrica nos últimos 5 minutos
avg(rate(http_requests_total[5m]))

# Soma de uma métrica agrupada por método
sum(rate(http_requests_total[5m])) by (method)

# Percentil 95 de duração de requisições
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))

A função rate() é particularmente importante: ela calcula a taxa de mudança por segundo, ideal para counters que crescem continuamente. Para uma requisição que levou 2 segundos, você usaria increase() para saber quanto a métrica cresceu em um período, enquanto rate() já converte para unidade por segundo.

Agregações e Combinações Complexas

Quando você tem múltiplas instâncias ou endpoints, precisa agregar dados de forma inteligente:

# Soma de requisições por método (agrupação)
sum(http_requests_total) by (method)

# Quantidade de instâncias com taxa de erro acima de 1%
count(rate(http_requests_total{status=~"5.."}[5m]) > 0.01)

# Requisições por segundo por endpoint e método
sum(rate(http_requests_total[5m])) by (endpoint, method)

# Taxa de erro percentual
(sum(rate(http_requests_total{status=~"5.."}[5m])) by (endpoint) / 
 sum(rate(http_requests_total[5m])) by (endpoint)) * 100

# Instâncias que não tiveram scrape bem-sucedido nos últimos 5 minutos
up{job="app"} == 0

A consulta de taxa de erro mostra como combinar múltiplas consultas com operadores aritméticos. O resultado é uma porcentagem de requisições que falharam, extremamente útil para alertas.

Alertmanager: Gerenciando Alertas

Arquitetura e Conceitos de Alertas

O Alertmanager é o componente responsável por gerenciar alertas gerados pelo Prometheus. Ele recebe alertas da regra de avaliação do Prometheus, agrupa-os, deduplica-os e roteia-os para seus destinos finais (email, Slack, PagerDuty, etc.). Um ponto crítico que muitos iniciantes não entendem: o Prometheus e Alertmanager são separados. O Prometheus avalia as regras, o Alertmanager entrega as notificações.

Existem conceitos importantes a entender: um alert tem estados (firing, pending, inactive), pode ser silenciado temporariamente e pode ser roteado para diferentes receptores com base em seus labels. Isso permite que um alerta crítico vá para PagerDuty enquanto um aviso vai apenas para o Slack.

Definindo Regras de Alerta

No arquivo alerts.yml, você define quando um alerta deve disparar. Aqui está um exemplo funcional:

groups:
  - name: application_alerts
    interval: 30s
    rules:
      - alert: HighErrorRate
        expr: |
          (sum(rate(http_requests_total{status=~"5.."}[5m])) by (job) /
           sum(rate(http_requests_total[5m])) by (job)) > 0.05
        for: 5m
        labels:
          severity: critical
          team: backend
        annotations:
          summary: "Taxa de erro alta em {{ $labels.job }}"
          description: |
            A aplicação {{ $labels.job }} tem taxa de erro de {{ $value | humanizePercentage }}
            nos últimos 5 minutos.

      - alert: ServiceDown
        expr: up{job="app"} == 0
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "Serviço {{ $labels.job }} está down"
          description: "A instância {{ $labels.instance }} está inativa há mais de 2 minutos"

      - alert: HighMemoryUsage
        expr: (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) < 0.15
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "Uso de memória alto no servidor {{ $labels.instance }}"
          description: "Menos de 15% de memória disponível"

      - alert: SlowRequests
        expr: |
          histogram_quantile(0.95, 
            sum(rate(http_request_duration_seconds_bucket[5m])) by (endpoint, le)
          ) > 1
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Requisições lentas em {{ $labels.endpoint }}"
          description: "P95 de latência está acima de 1 segundo"

Cada regra tem uma expressão PromQL (expr), uma duração antes de disparar (for), labels para categorizar o alerta e anotações para fornecer contexto. O parâmetro for é crucial: garante que o alerta só dispare se a condição persistir por esse período, evitando alertas por espikes temporários.

Configurando o Alertmanager

O Alertmanager precisa estar rodando e configurado para receber alertas do Prometheus. Crie um arquivo alertmanager.yml:

global:
  resolve_timeout: 5m
  slack_api_url: 'YOUR_SLACK_WEBHOOK_URL'

route:
  receiver: 'default'
  group_by: ['alertname', 'cluster', 'service']
  group_wait: 10s
  group_interval: 10s
  repeat_interval: 12h
  routes:
    - match:
        severity: critical
      receiver: 'pagerduty'
      continue: true

    - match:
        severity: warning
      receiver: 'slack'

receivers:
  - name: 'default'
    slack_configs:
      - channel: '#alerts'
        title: 'Alerta Prometheus'
        text: '{{ range .Alerts }}{{ .Annotations.description }}{{ end }}'

  - name: 'pagerduty'
    pagerduty_configs:
      - service_key: 'YOUR_PAGERDUTY_SERVICE_KEY'
        description: '{{ .GroupLabels.alertname }}'

  - name: 'slack'
    slack_configs:
      - channel: '#warnings'
        text: '{{ .GroupLabels.alertname }}: {{ .Alerts.Firing | len }} alertas'

inhibit_rules:
  - source_match:
      severity: 'critical'
    target_match:
      severity: 'warning'
    equal: ['alertname', 'dev', 'instance']

Este arquivo define como os alertas são roteados. O route principal menciona que alertas críticos vão para PagerDuty enquanto avisos vão para Slack. A seção inhibit_rules evita notificações duplicadas: se um alerta crítico está ativo, alertas de warning relacionados são suprimidos. Inicie o Alertmanager com:

./alertmanager --config.file=alertmanager.yml

Exemplos Práticos de Alertas Bem Projetados

Um alerta bem projetado não gera ruído desnecessário. Aqui está um exemplo de um alerta robusto para timeout em operações de banco de dados:

- alert: DatabaseQueryTimeout
  expr: |
    rate(db_query_timeout_total[5m]) > 0.1
  for: 10m
  labels:
    severity: warning
    team: database
  annotations:
    summary: "Timeouts de banco de dados detectados em {{ $labels.database }}"
    description: |
      O banco de dados {{ $labels.database }} está tendo {{ $value | humanize }} 
      timeouts por segundo nos últimos 5 minutos. 

      Possíveis causas:
      - Carga de I/O alta no servidor
      - Queries lentas ou locks de tabela
      - Recursos de CPU/memória insuficientes

      Ação recomendada: Verificar logs do banco de dados e executar EXPLAIN nas queries lentas.

Este alerta combina várias boas práticas: usa for de 10 minutos para evitar alertas temporários, fornece contexto específico no summary, explica possíveis causas e até sugere ações. As anotações aparecem nos alertas notificados, ajudando o time a resolver rapidamente.

Integração Completa: Exemplo Prático End-to-End

Para consolidar o aprendizado, aqui está um exemplo completo de monitoramento. Suponha que você tem uma API de processamento de pedidos. Primeiro, crie a aplicação em Python com métricas:

from prometheus_client import Counter, Histogram, Gauge, start_http_server
from flask import Flask, request
import time
import random

app = Flask(__name__)
start_http_server(8080)

# Métricas
request_duration = Histogram(
    'order_api_duration_seconds',
    'Duração do processamento',
    ['endpoint', 'method'],
    buckets=(0.1, 0.5, 1.0, 2.0, 5.0)
)

requests_total = Counter(
    'order_api_requests_total',
    'Total de requisições',
    ['endpoint', 'method', 'status']
)

orders_processed = Counter(
    'orders_processed_total',
    'Total de pedidos processados',
    ['status']
)

processing_queue_size = Gauge(
    'processing_queue_size',
    'Tamanho da fila de processamento'
)

@app.route('/orders', methods=['POST'])
def create_order():
    start = time.time()
    try:
        # Simula processamento
        duration = random.uniform(0.1, 2.0)
        time.sleep(duration)

        # Simulação: 5% de falha
        if random.random() < 0.05:
            requests_total.labels(
                endpoint='/orders', method='POST', status=500
            ).inc()
            orders_processed.labels(status='failed').inc()
            return {'error': 'Processing failed'}, 500

        requests_total.labels(
            endpoint='/orders', method='POST', status=201
        ).inc()
        orders_processed.labels(status='success').inc()
        return {'order_id': 123}, 201

    finally:
        request_duration.labels(
            endpoint='/orders', method='POST'
        ).observe(time.time() - start)
        processing_queue_size.set(random.randint(5, 50))

if __name__ == '__main__':
    app.run(port=5000, debug=False)

Configure o arquivo prometheus.yml para coletar da aplicação:

global:
  scrape_interval: 15s

alerting:
  alertmanagers:
    - static_configs:
        - targets: ['localhost:9093']

rule_files:
  - 'alerts.yml'

scrape_configs:
  - job_name: 'order_api'
    static_configs:
      - targets: ['localhost:8080']

E defina alertas relevantes em alerts.yml:

groups:
  - name: order_system
    rules:
      - alert: OrderProcessingErrorRate
        expr: |
          (sum(rate(order_api_requests_total{status="500"}[5m])) /
           sum(rate(order_api_requests_total[5m]))) > 0.05
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "Taxa de erro em processamento de pedidos acima de 5%"

      - alert: SlowOrderProcessing
        expr: |
          histogram_quantile(0.95, 
            sum(rate(order_api_duration_seconds_bucket[5m])) by (le)
          ) > 2
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "Processamento de pedidos está lento"

      - alert: QueueBuildup
        expr: processing_queue_size > 100
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Fila de processamento acumulando"

Com essa configuração, você tem monitoramento completo: coleta de métricas da aplicação, consultas em PromQL para análise e alertas automáticos para situações anormais.

Conclusão

Você aprendeu três conceitos fundamentais para monitoramento profissional. Primeiro, como o Prometheus coleta métricas via modelo pull de aplicações instrumentadas, oferecendo simplicidade e resiliência superior a ferramentas antigas. Segundo, como usar PromQL para consultar dados de série temporal com funções como rate() e histogram_quantile(), transformando métricas brutas em insights acionáveis. Terceiro, como configurar o Alertmanager para rotear alertas de forma inteligente, evitando ruído ao mesmo tempo em que garante que problemas críticos sejam notificados imediatamente.

O verdadeiro domínio vem da prática: configure o Prometheus em seu próprio projeto, experimente diferentes PromQL queries no dashboard, e ajuste suas regras de alerta com base no feedback operacional. Lembre-se que um bom alerta é aquele que dispara apenas quando há ação a tomar — não aquele que notifica constantemente sobre problemas que você não pode resolver.

Referências


Artigos relacionados