O problema real
Seu cluster rodando 10 microsserviços. Um deles começa lento. Você não sabe qual — avisa pelo Slack que "aplicação tá lenta", mas ninguém responde rápido. 30 minutos depois, descobre que é Rate Limit da API terceira. Se tivesse observabilidade, um gráfico apontaria: "requisições pra API X estão falhando 5%", e você resolveria em 2 minutos.
Realidade: aplicação rápida demanda não é "está tudo bem", é "temos dados de que está bem". Sem Prometheus + Grafana, você só vê crash no NewRelic quando é tarde demais.
Os 4 tipos de métrica
Prometheus coleta números. Cada número é de um tipo:
Counter: apenas aumenta
# Contador: requisições totais
requests_total = Counter(
'http_requests_total',
'Total de requisições HTTP',
['method', 'endpoint', 'status']
)
# Incrementa quando request completa
requests_total.labels(method='GET', endpoint='/users', status=200).inc()
requests_total.labels(method='POST', endpoint='/users', status=201).inc()
requests_total.labels(method='GET', endpoint='/users', status=500).inc()
Uso: requisições, erros, logs, mensagens processadas. Não use para: temperatura da sala, memória (ela desce).
Gauge: sobe e desce
# Gauge: memória em uso (varia)
memory_usage = Gauge(
'process_memory_bytes',
'Memória do processo em bytes'
)
memory_usage.set(1024 * 1024 * 256) # 256 MB
memory_usage.set(1024 * 1024 * 512) # depois 512 MB
memory_usage.set(1024 * 1024 * 128) # depois 128 MB
Uso: memória, CPU, tamanho de fila, número de conexões.
Histogram: distribuição de valores
# Histograma: latência de requisição
request_duration = Histogram(
'http_request_duration_seconds',
'Latência das requisições em segundos',
buckets=(0.001, 0.01, 0.1, 0.5, 1.0)
)
import time
@app.route('/api/users')
def get_users():
start = time.time()
# ... lógica
duration = time.time() - start
request_duration.observe(duration)
Prometheus calcula automaticamente:
- Soma de todas latências
- Contagem de requisições
- Percentis (p50, p95, p99)
Uso: latência, tamanho de arquivo, tempo de processamento.
Summary: percentil direto
Similar a Histogram, mas calcula percentil no cliente (mais barato):
# Summary: latência com percentil automático
response_time = Summary(
'response_time_seconds',
'Tempo de resposta em segundos'
)
@app.route('/api')
def api():
with response_time.time():
# ... lógica
pass
Diferença Histogram vs Summary:
- Histogram: preciso de percentil? Use isso. Calcula no Prometheus (query lenta).
- Summary: aplicação calcula percentil (query rápida, mas menos flexível).
Instrumentar uma aplicação de verdade
Setup Prometheus + Grafana em Docker:
# docker-compose.yml
version: '3.8'
services:
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
GF_SECURITY_ADMIN_PASSWORD: admin
volumes:
- grafana_data:/var/lib/grafana
app:
build: .
ports:
- "5000:5000"
depends_on:
- prometheus
volumes:
prometheus_data:
grafana_data:
prometheus.yml:
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'app'
static_configs:
- targets: ['localhost:5000']
Aplicação Flask instrumentada:
from flask import Flask
from prometheus_client import Counter, Gauge, Histogram, generate_latest
import time
app = Flask(__name__)
# Métricas
requests_total = Counter(
'http_requests_total',
'Total requisições',
['method', 'endpoint', 'status']
)
request_duration = Histogram(
'http_request_duration_seconds',
'Latência em segundos',
buckets=(0.01, 0.025, 0.05, 0.1, 0.5, 1.0)
)
active_connections = Gauge(
'active_connections',
'Conexões ativas'
)
# Middleware: registra cada requisição
@app.before_request
def before_request():
request.start_time = time.time()
active_connections.inc()
@app.after_request
def after_request(response):
duration = time.time() - request.start_time
request_duration.observe(duration)
requests_total.labels(
method=request.method,
endpoint=request.path,
status=response.status_code
).inc()
active_connections.dec()
return response
@app.route('/metrics')
def metrics():
return generate_latest()
@app.route('/users')
def get_users():
time.sleep(0.05) # simula delay
return {'users': ['alice', 'bob']}
if __name__ == '__main__':
app.run(port=5000)
Rode: curl localhost:5000/metrics e vê as métricas em formato Prometheus.
PromQL prático: a linguagem de queries
Acesse http://localhost:9090, clique em "Graph", escreva queries:
Taxa de requisições por segundo
# rate() calcula mudança ao longo do tempo
rate(http_requests_total[5m])
# Resultado: requisições por segundo nos últimos 5 minutos
# Exemplo: 10 requisições
Taxa de erro
# Requisições com erro (status >= 400)
rate(http_requests_total{status=~"4..|5.."}[5m])
# Percentual de erro
(
rate(http_requests_total{status=~"4..|5.."}[5m])
/
rate(http_requests_total[5m])
) * 100
# Resultado: 2.5% (2.5 em cada 100 requisições falham)
Percentil de latência
# P95 (95% das requisições mais rápidas)
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))
# Resultado: 0.234 segundos (p95 é 234 ms)
# P99 (99% mais rápidas)
histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))
# Resultado: 0.512 segundos
Por que média engana:
Latências: 10ms, 10ms, 10ms, 10ms, 10.000ms
Média = (10+10+10+10+10000) / 5 = 2.008 segundos (engana!)
P95 = 10ms (verdade — 95% dos usuários vê 10ms)
Memória em MB
# Gauge em bytes, divide por 1 milhão pra MB
process_memory_bytes / 1024 / 1024
# Resultado: 256 (256 MB)
Os 4 sinais dourados
Google SRE recomenda monitorar esses 4 sinais:
1. Latência (tempo de resposta)
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))
Alarme: p95 > 500ms.
2. Tráfego (carga)
rate(http_requests_total[5m])
Alarme: > 10.000 requisições/segundo (ajuste por sua aplicação).
3. Erros (taxa de falha)
rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m])
Alarme: > 1% de erro (5xx).
4. Saturação (recursos no limite)
# CPU
node_cpu_seconds_total
# Memória
process_resident_memory_bytes / 1024 / 1024
# Disk I/O
node_disk_io_time_seconds_total
Alarme: > 80% de utilização.
Alertas que não viram ruído
❌ Alertas ruins (viram ruído):
- alert: HighMemory
expr: process_memory_bytes > 512*1024*1024 # > 512 MB
# Problema: memória natural sobe, você ignora 100 alerts/dia
✓ Alertas bons (sintoma, não causa):
- alert: ErrorRateHigh
expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.01
for: 5m # apenas se persistir por 5 minutos
- alert: LatencyHighp95
expr: histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) > 1.0
for: 10m
- alert: ContainerCrashLoop
expr: rate(container_last_seen[5m]) == 0
# Sintoma: container não responde. Causa é desconhecida.
Regra: alerta em sintoma (usuário sofre), não em causa (memória sobe).
SLI, SLO, SLA com número concreto
SLI (Service Level Indicator): métrica mensurável.
# SLI: requisições bem-sucedidas (< 500ms)
(
sum(rate(http_requests_total{status=~"2.."}[30d])[30d:])
/
sum(rate(http_requests_total[30d])[30d:])
) * 100
SLO (Service Level Objective): meta de SLI.
Objetivo: 99.5% de requisições respondem em < 500ms
(número que você publica internamente)
SLA (Service Level Agreement): contrato, penalidade se falhar.
Se SLI < SLO por mais de 1 dia, cliente ganha crédito 5%
(contrato legal — envolve dinheio)
Exemplo real:
App: API de usuários
SLI: (requisições 2xx) / (todas) = 99.2%
SLO: >= 99.5%
SLA: se < 99.5%, cliente ganha $500 crédito
Resultado: 30 dias com 99.2% (abaixo SLO) = cliente recebe $500
Cardinalidade: o erro que derruba Prometheus
Nunca use valores dinâmicas como rótulo:
# ❌ ERRADO: user_id é dinâmico (milhões de valores)
# Cada usuário = nova série = Prometheus explode
requests_by_user = Counter(
'requests_by_user_total',
'Requisições por usuário',
['user_id']
)
requests_by_user.labels(user_id=request.user_id).inc()
# 1 milhão de usuários = 1 milhão de séries!
Prometheus fica lento (memória cresce), queries demoram 10 segundos:
ERRO: cardinality explosion
Series: 5,000,000
Memory: 8 GB (esperava 200 MB)
✓ CORRETO: rótulos com baixa cardinalidade
# Bom: endpoint é ~50 valores
requests_total = Counter(
'http_requests_total',
'Total requisições',
['method', 'endpoint', 'status'] # ~6 * 4 * 10 = 240 séries
)
requests_total.labels(
method='GET',
endpoint='/users',
status=200
).inc()
Regra: cardinalidade = (labels[0] valores) * (labels[1] valores) * ...
Se > 100.000 séries, Prometheus sofre. Limite!
Armadilhas comuns
1. Prometheus sem retenção (disco cheio):
--storage.tsdb.retention.time=30d # manter 30 dias
2. Scrape interval muito curto:
scrape_interval: 1s # ❌ Prometheus coleta a cada 1 segundo, fica lento
scrape_interval: 15s # ✓ Padrão, suficiente pra maioria
3. Alertas sem runbook:
- alert: HighErrorRate
expr: error_rate > 0.05
# "O que fazer agora?" — ninguém sabe
Bom:
- alert: HighErrorRate
expr: error_rate > 0.05
annotations:
runbook_url: https://wiki.company.com/alerts/high_error_rate
summary: "Taxa de erro acima de 5%"
Quando NÃO usar Prometheus
Métrica com mudança a cada nanosegundo: Prometheus coleta a cada 15 segundos. Se precisa tempo real, use InfluxDB.
Logs estruturados: Prometheus é pra métricas. Use ELK/Loki para logs.
Forecasting/ML: Prometheus não prevê. Use M3/Graphite se precisa predição.
Próximos passos
Correlacione métricas com erros em aplicações IA: Observabilidade em aplicações com LLM — inclui setup completo de logging + métricas.
Deploy Prometheus em produção: Terraform na prática: sua primeira infra versionada — provisiona com IaC.
Teste: rode o docker-compose acima, acessa http://localhost:3000 (Grafana), cria dashboard com 4 gráficos (latência, taxa de erro, tráfego, saturação). Gera carga: ab -n 1000 -c 10 http://localhost:5000/users. Vê em tempo real as métricas subindo em Grafana. Isso é ouro puro.