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.