Conceitos Fundamentais
O Modelo Pull
Prometheus usa pull model: ele mesmo coleta métricas dos alvos em intervalos configurados (scraping). Isso simplifica a descoberta e evita que alvos sobrecarreguem o servidor com pushes.
┌─────────────┐ HTTP GET /metrics ┌─────────────────┐
│ Prometheus │ ──────────────────────► │ Target (app) │
│ Server │ ◄────────────────────── │ :8080/metrics │
└─────────────┘ exposition format └─────────────────┘
│
▼
┌─────────────┐
│ TSDB │ (Time Series Database local)
└─────────────┘Vantagens do pull model:
- Simples de depurar (basta acessar
/metricsmanualmente) - Prometheus controla o ritmo de coleta
- Facilita descoberta de alvos mortos
- Sem necessidade de abrir portas no firewall para o app
TSDB — Time Series Database
Prometheus armazena dados como séries temporais: pares (timestamp, valor float64) identificados por nome e labels.
# Série temporal: nome{label1="valor1", label2="valor2"} → [(t1, v1), (t2, v2), ...]
http_requests_total{method="GET", status="200", job="api"} → [(1700000000, 1042), (1700000015, 1053), ...]Características do TSDB:
- Blocos imutáveis de 2 horas no disco
- Compactação em background
- Retenção padrão: 15 dias
- Formato Prometheus TSDB (não compatível com outras ferramentas diretamente)
- Write-ahead log (WAL) para durabilidade
Formato de Exposição (Exposition Format)
# HELP http_requests_total Total de requisições HTTP recebidas
# TYPE http_requests_total counter
http_requests_total{method="GET",status="200"} 1027
http_requests_total{method="POST",status="201"} 43
http_requests_total{method="GET",status="404"} 12
# HELP process_memory_bytes Uso de memória em bytes
# TYPE process_memory_bytes gauge
process_memory_bytes 2.4e+08
# HELP http_request_duration_seconds Duração das requisições
# TYPE http_request_duration_seconds histogram
http_request_duration_seconds_bucket{le="0.005"} 847
http_request_duration_seconds_bucket{le="0.01"} 923
http_request_duration_seconds_bucket{le="0.025"} 1001
http_request_duration_seconds_bucket{le="0.05"} 1020
http_request_duration_seconds_bucket{le="+Inf"} 1027
http_request_duration_seconds_sum 5.23
http_request_duration_seconds_count 1027Tipos de Métricas
Counter
O que é: Valor que só aumenta (ou reseta ao reiniciar o processo).
Quando usar:
- Número total de requisições
- Número de erros
- Bytes enviados/recebidos
- Tarefas completadas
# Exemplo em Python
from prometheus_client import Counter
requests_total = Counter(
'http_requests_total',
'Total de requisições HTTP',
['method', 'status']
)
# Incrementar
requests_total.labels(method='GET', status='200').inc()
requests_total.labels(method='POST', status='500').inc(1)PromQL com counters — use rate() ou increase():
# Taxa de requisições por segundo (média nos últimos 5 minutos)
rate(http_requests_total[5m])
# Aumento absoluto nos últimos 10 minutos
increase(http_requests_total[10m])Gauge
O que é: Valor que pode subir e descer livremente.
Quando usar:
- Temperatura atual
- Número de goroutines/threads ativas
- Uso de memória
- Itens em uma fila
- Conexões abertas
from prometheus_client import Gauge
memory_usage = Gauge('process_memory_bytes', 'Uso de memória em bytes')
queue_size = Gauge('job_queue_size', 'Itens na fila de jobs')
# Setar valor
memory_usage.set(1024 * 1024 * 256) # 256 MB
# Incrementar/decrementar
queue_size.inc()
queue_size.dec()
# Medir duração de função (gauge de tempo)
@memory_usage.track_inprogress()
def process_job():
passPromQL com gauges — use o valor direto ou avg_over_time():
# Valor atual
process_memory_bytes
# Média nas últimas 5 minutos
avg_over_time(process_memory_bytes[5m])
# Máximo nas últimas 1 hora
max_over_time(process_memory_bytes[1h])Histogram
O que é: Amostra observações e as distribui em buckets configuráveis. Calcula soma e contagem automaticamente.
Quando usar:
- Latência de requisições HTTP
- Tamanho de payloads
- Duração de queries SQL
- Qualquer distribuição que precise de percentis
O que gera automaticamente:
<nome>_bucket{le="N"}— contagem de observações ≤ N<nome>_sum— soma de todas observações<nome>_count— número total de observações
from prometheus_client import Histogram
request_duration = Histogram(
'http_request_duration_seconds',
'Duração das requisições HTTP',
['method', 'endpoint'],
buckets=[0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0]
)
# Observar um valor
request_duration.labels(method='GET', endpoint='/api/users').observe(0.042)
# Usar como context manager
with request_duration.labels(method='POST', endpoint='/api/orders').time():
process_order()PromQL com histograms:
# Percentil 99 de latência (nos últimos 5 minutos)
histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))
# Percentil 95 por endpoint
histogram_quantile(0.95,
sum by (endpoint, le) (rate(http_request_duration_seconds_bucket[5m]))
)
# Latência média
rate(http_request_duration_seconds_sum[5m])
/ rate(http_request_duration_seconds_count[5m])Summary
O que é: Similar ao Histogram, mas calcula percentis no lado do cliente. Mais preciso, porém não agregável.
Quando usar:
- Quando você precisa de percentis precisos por instância
- Quando não precisa agregar entre instâncias
Diferença chave vs Histogram:
| Histogram | Summary | |
|---|---|---|
| Percentis calculados | No servidor (PromQL) | No cliente |
| Agregável entre instâncias | Sim | Não |
| Uso de memória | Baixo | Alto |
| Requer buckets pré-definidos | Sim | Não |
from prometheus_client import Summary
request_latency = Summary(
'http_request_latency_seconds',
'Latência das requisições',
['method']
)
with request_latency.labels(method='GET').time():
handle_request()PromQL — Linguagem de Query
Seletores e Matchers
# Seletor de série exata
http_requests_total
# Filtrar por label com igualdade
http_requests_total{job="api", status="200"}
# Filtrar com regex
http_requests_total{status=~"2.."} # Status 2xx
http_requests_total{status!~"5.."} # Excluir 5xx
http_requests_total{endpoint!="/health"} # Excluir health check
http_requests_total{job=~"api|gateway"} # Múltiplos jobs
# Matchers disponíveis:
# = igualdade exata
# != diferença
# =~ regex match
# !~ regex não-matchRange Queries e Vetores
# Instant vector — valor no momento atual
http_requests_total
# Range vector — valores em uma janela de tempo
http_requests_total[5m] # últimos 5 minutos
http_requests_total[1h] # última 1 hora
http_requests_total[30s] # últimos 30 segundos
# Offset — query no passado
http_requests_total offset 1h # valor de 1 hora atrás
rate(http_requests_total[5m]) offset 24h # taxa de ontemFunções de Taxa
# rate() — taxa por segundo, suavizada, lida resets de counter
# SEMPRE use com counters. Janela mínima recomendada: 4x o intervalo de scrape
rate(http_requests_total[5m])
# irate() — taxa instantânea (últimos 2 pontos)
# Mais responsivo a picos, menos suavizado. Use para dashboards de alertas
irate(http_requests_total[5m])
# increase() — aumento total na janela
# Equivale a rate() * duração_da_janela
increase(http_requests_total[1h])
# Diferença entre rate e irate:
# rate → média dos últimos 5min (suavizado)
# irate → derivada instantânea (sensível a spikes)Funções de Agregação no Tempo
# Média ao longo do tempo
avg_over_time(process_memory_bytes[1h])
# Máximo ao longo do tempo
max_over_time(http_active_connections[30m])
# Mínimo ao longo do tempo
min_over_time(node_filesystem_free_bytes[6h])
# Desvio padrão ao longo do tempo
stddev_over_time(http_request_duration_seconds_sum[1h])
# Percentil ao longo do tempo (0.0 a 1.0)
quantile_over_time(0.95, http_request_duration_seconds[1h])
# Contagem de amostras na janela
count_over_time(up[5m])Operadores de Agregação
# Somar todas as séries
sum(http_requests_total)
# Somar agrupando por label
sum by (status) (http_requests_total)
sum by (job, method) (http_requests_total)
# Somar excluindo labels específicas (equivalente)
sum without (instance) (http_requests_total)
# Contagem de séries
count(http_requests_total)
count by (job) (http_requests_total)
# Média
avg(process_memory_bytes)
avg by (job) (process_memory_bytes)
# Top N valores
topk(5, http_requests_total)
topk(3, sum by (endpoint) (rate(http_requests_total[5m])))
# Bottom N valores
bottomk(5, node_filesystem_free_bytes)
# Máximo / Mínimo entre séries
max by (job) (process_memory_bytes)
min by (instance) (node_cpu_seconds_total)
# Percentil entre séries (0.0 a 1.0)
quantile(0.5, http_request_duration_seconds)Operadores Binários e Correspondência de Labels
# Operadores aritméticos
node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes
# Percentual de memória usada
(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes)
/ node_memory_MemTotal_bytes * 100
# Comparação (retorna séries que satisfazem a condição)
process_memory_bytes > 1e9 # mais de 1 GB
http_requests_total > bool 1000 # retorna 0 ou 1
# Correspondência de labels em operações binárias
# on() — apenas os labels listados devem corresponder
# ignoring() — ignorar esses labels na correspondência
method_code:http_errors:rate5m / ignoring(code)
group_left method:http_requests:rate5m
# group_left / group_right para many-to-one
# group_left = lado esquerdo tem mais séries
sum(rate(http_errors_total[5m])) by (method, code)
/ ignoring(code) group_left()
sum(rate(http_requests_total[5m])) by (method)Queries Práticas
# Error rate (porcentagem de erros 5xx)
sum(rate(http_requests_total{status=~"5.."}[5m]))
/ sum(rate(http_requests_total[5m])) * 100
# Latência P99 por endpoint
histogram_quantile(0.99,
sum by (endpoint, le) (rate(http_request_duration_seconds_bucket[5m]))
)
# Disponibilidade do serviço (uptime %)
avg_over_time(up{job="api"}[24h]) * 100
# CPU usage por instância (%)
100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
# Memória livre em GB
node_memory_MemAvailable_bytes / 1024 / 1024 / 1024
# Disco usado em %
(node_filesystem_size_bytes - node_filesystem_avail_bytes)
/ node_filesystem_size_bytes * 100
# Saturation: conexões ativas vs máximo
pg_stat_activity_count / pg_settings_max_connections * 100
# Apdex score (latência < 0.1s = satisfeito, < 0.4s = tolerado)
(
sum(rate(http_request_duration_seconds_bucket{le="0.1"}[5m]))
+ sum(rate(http_request_duration_seconds_bucket{le="0.4"}[5m]))
) / 2 / sum(rate(http_request_duration_seconds_count[5m]))Labels e Cardinalidade
Boas Práticas de Labels
# BOM: labels de baixa cardinalidade
http_requests_total{
method="GET", # 4-5 valores possíveis
status="200", # ~10 valores possíveis
endpoint="/api/users" # dezenas de valores
}
# RUIM: labels de alta cardinalidade — EXPLODEM o TSDB
http_requests_total{
user_id="12345", # MILHÕES de valores possíveis!
request_id="abc-123", # ÚNICO por requisição!
ip="192.168.1.1" # alto número de valores
}Regras para Labels
- Cardinalidade máxima sugerida: cada label deve ter menos de ~100 valores distintos
- Nunca use como label: IDs de usuário, IDs de transação, timestamps, UUIDs
- Prefira enumerações finitas: status HTTP (200, 404, 500), método HTTP (GET, POST), ambiente (prod, staging)
- Labels são parte da identidade da série — mudar um label cria uma nova série
# Verificar cardinalidade atual
# Total de séries ativas
prometheus_tsdb_head_series
# Séries por job
count by (job) ({__name__=~".+"})
# Top métricas por número de séries
topk(10, count by (__name__) ({__name__=~".+"}))prometheus.yml — Configuração Principal
# prometheus.yml
global:
# Com que frequência coletar métricas (padrão: 1m)
scrape_interval: 15s
# Timeout para cada scrape (deve ser < scrape_interval)
scrape_timeout: 10s
# Com que frequência avaliar regras de alerta
evaluation_interval: 15s
# Labels adicionados a todas as séries coletadas
external_labels:
cluster: 'prod-us-east-1'
environment: 'production'
# Caminho para arquivos de regras de alerta e recording rules
rule_files:
- "rules/*.yml"
- "alerts/*.yml"
# Configuração do Alertmanager
alerting:
alertmanagers:
- static_configs:
- targets:
- 'alertmanager:9093'
# Timeout para envio de alertas
timeout: 10s
# Configuração de scrape (onde coletar métricas)
scrape_configs:
# Prometheus auto-monitora a si mesmo
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
# Aplicação com configuração estática
- job_name: 'api-backend'
scrape_interval: 10s # sobrescreve o global
metrics_path: '/actuator/prometheus' # Spring Boot Actuator
scheme: 'https'
tls_config:
insecure_skip_verify: false
static_configs:
- targets:
- 'api-1.prod.internal:8080'
- 'api-2.prod.internal:8080'
labels:
env: 'production'
region: 'us-east-1'
# Node Exporter (métricas do SO)
- job_name: 'node-exporter'
static_configs:
- targets:
- 'server-1:9100'
- 'server-2:9100'
- 'server-3:9100'
# Descoberta via Docker (containers em execução)
- job_name: 'docker-containers'
docker_sd_configs:
- host: 'unix:///var/run/docker.sock'
refresh_interval: 30s
relabel_configs:
# Apenas containers com label prometheus.io/scrape=true
- source_labels: [__meta_docker_container_label_prometheus_io_scrape]
action: keep
regex: 'true'
# Usar porta customizada do label
- source_labels: [__meta_docker_container_label_prometheus_io_port]
action: replace
target_label: __address__
regex: (.+)
replacement: '${1}'
# Descoberta via Kubernetes
- job_name: 'kubernetes-pods'
kubernetes_sd_configs:
- role: pod
relabel_configs:
# Manter apenas pods com anotação prometheus.io/scrape: "true"
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep
regex: 'true'
# Usar path customizado da anotação
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
action: replace
target_label: __metrics_path__
regex: (.+)
# Construir endereço a partir de IP e porta
- source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
action: replace
regex: ([^:]+)(?::\d+)?;(\d+)
replacement: $1:$2
target_label: __address__
# Copiar namespace como label
- source_labels: [__meta_kubernetes_namespace]
target_label: namespace
# Copiar nome do pod como label
- source_labels: [__meta_kubernetes_pod_name]
target_label: podRelabeling
Relabeling é executado antes do scrape (sobre metadata da descoberta):
relabel_configs:
# keep — manter apenas séries que fazem match
- source_labels: [__meta_kubernetes_pod_ready]
action: keep
regex: 'true'
# drop — remover séries que fazem match
- source_labels: [__meta_kubernetes_namespace]
action: drop
regex: 'kube-system'
# replace — transformar label
- source_labels: [__meta_kubernetes_pod_name]
target_label: instance
action: replace
# labelmap — copiar metadata labels para labels finais
- action: labelmap
regex: __meta_kubernetes_pod_label_(.+)
# labeldrop — remover labels do resultado final
- action: labeldrop
regex: __meta_kubernetes_.*Alertmanager
Regras de Alerta (alert rules)
# rules/alerts.yml
groups:
- name: api_alerts
interval: 30s # frequência de avaliação deste grupo
rules:
# Alerta se o serviço estiver down
- alert: ServiceDown
expr: up{job="api-backend"} == 0
for: 1m # precisa persistir por 1 minuto para disparar
labels:
severity: critical
team: platform
annotations:
summary: "Serviço {{ $labels.job }} está down"
description: "{{ $labels.instance }} está inacessível há mais de 1 minuto."
runbook: "https://wiki.internal/runbooks/service-down"
# Error rate > 5%
- 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: warning
annotations:
summary: "Alta taxa de erros em {{ $labels.job }}"
description: "Error rate de {{ humanizePercentage $value }} nos últimos 5 minutos."
# Latência P99 > 500ms
- alert: HighLatencyP99
expr: |
histogram_quantile(0.99,
sum by (job, le) (rate(http_request_duration_seconds_bucket[5m]))
) > 0.5
for: 5m
labels:
severity: warning
annotations:
summary: "Latência P99 alta em {{ $labels.job }}"
description: "P99 = {{ humanizeDuration $value }}"
# Memória acima de 90%
- alert: HighMemoryUsage
expr: |
(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes)
/ node_memory_MemTotal_bytes * 100 > 90
for: 10m
labels:
severity: critical
annotations:
summary: "Uso de memória crítico em {{ $labels.instance }}"
description: "{{ $value | printf \"%.1f\" }}% de memória usada."
# Disco acima de 85%
- alert: DiskSpaceLow
expr: |
(node_filesystem_size_bytes{fstype!~"tmpfs|overlay"}
- node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"})
/ node_filesystem_size_bytes{fstype!~"tmpfs|overlay"} * 100 > 85
for: 30m
labels:
severity: warning
annotations:
summary: "Pouco espaço em disco em {{ $labels.instance }}"
description: "Disco {{ $labels.mountpoint }} com {{ $value | printf \"%.1f\" }}% cheio."Configuração do Alertmanager
# alertmanager.yml
global:
# Tempo que um alerta fica em "resolved" antes de ser removido
resolve_timeout: 5m
# Configuração SMTP global
smtp_smarthost: 'smtp.gmail.com:587'
smtp_from: 'alertas@empresa.com'
smtp_auth_username: 'alertas@empresa.com'
smtp_auth_password_file: '/etc/alertmanager/smtp_password'
# Templates customizados
templates:
- '/etc/alertmanager/templates/*.tmpl'
# Rota raiz — todas as rotas herdam desta
route:
receiver: 'default-receiver'
group_by: ['alertname', 'job', 'severity']
group_wait: 30s # espera antes de enviar primeiro alerta do grupo
group_interval: 5m # espera para enviar alertas novos no mesmo grupo
repeat_interval: 4h # quanto tempo antes de re-notificar um alerta ativo
routes:
# Alertas críticos vão para PagerDuty + Slack
- matchers:
- severity = critical
receiver: 'critical-alerts'
repeat_interval: 1h
# Alertas de infraestrutura vão para canal específico
- matchers:
- team = infra
receiver: 'infra-slack'
# Silenciar alertas de staging fora do horário comercial
- matchers:
- environment = staging
mute_time_intervals:
- outside-business-hours
# Receivers — destinos das notificações
receivers:
- name: 'default-receiver'
slack_configs:
- api_url: 'https://hooks.slack.com/services/T00/B00/xxx'
channel: '#alertas-gerais'
title: '{{ .GroupLabels.alertname }}'
text: '{{ range .Alerts }}{{ .Annotations.description }}{{ end }}'
- name: 'critical-alerts'
pagerduty_configs:
- routing_key: 'sua-chave-pagerduty'
description: '{{ .GroupLabels.alertname }}: {{ .CommonAnnotations.summary }}'
slack_configs:
- api_url: 'https://hooks.slack.com/services/T00/B00/yyy'
channel: '#alertas-criticos'
color: '{{ if eq .Status "firing" }}danger{{ else }}good{{ end }}'
title: '{{ if eq .Status "firing" }}ALERTA: {{ else }}RESOLVIDO: {{ end }}{{ .GroupLabels.alertname }}'
text: |
{{ range .Alerts }}
*Summary:* {{ .Annotations.summary }}
*Description:* {{ .Annotations.description }}
*Runbook:* {{ .Annotations.runbook }}
{{ end }}
- name: 'infra-slack'
slack_configs:
- api_url: 'https://hooks.slack.com/services/T00/B00/zzz'
channel: '#infra-alertas'
- name: 'email-oncall'
email_configs:
- to: 'oncall@empresa.com'
subject: '[ALERTA] {{ .GroupLabels.alertname }}'
html: '{{ template "email.html" . }}'
# Inibições — suprimir alertas menos graves quando há alertas mais graves
inhibit_rules:
# Se o nó está down, inibir alertas de serviços nesse nó
- source_matchers:
- alertname = NodeDown
target_matchers:
- alertname =~ ".*"
equal: ['instance']
# Intervalos de silêncio
time_intervals:
- name: outside-business-hours
time_intervals:
- weekdays: ['saturday', 'sunday']
- times:
- start_time: '00:00'
end_time: '08:00'
- start_time: '18:00'
end_time: '24:00'Recording Rules
Recording rules pré-calculam expressions PromQL caras e salvam como novas séries. Úteis para dashboards e alertas complexos.
# rules/recording-rules.yml
groups:
- name: http_metrics
interval: 1m
rules:
# Taxa de requisições por job e método
- record: job_method:http_requests:rate5m
expr: sum by (job, method) (rate(http_requests_total[5m]))
# Error rate por job
- record: job:http_error_rate:ratio5m
expr: |
sum by (job) (rate(http_requests_total{status=~"5.."}[5m]))
/ sum by (job) (rate(http_requests_total[5m]))
# Latência P99 por job
- record: job:http_request_duration_p99:5m
expr: |
histogram_quantile(0.99,
sum by (job, le) (rate(http_request_duration_seconds_bucket[5m]))
)
# Latência P50 (mediana) por job
- record: job:http_request_duration_p50:5m
expr: |
histogram_quantile(0.50,
sum by (job, le) (rate(http_request_duration_seconds_bucket[5m]))
)
- name: node_metrics
interval: 1m
rules:
# CPU usage por instância
- record: instance:cpu_usage:rate5m
expr: |
100 - (avg by (instance)
(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
# Memória usada em %
- record: instance:memory_usage:ratio
expr: |
1 - (node_memory_MemAvailable_bytes
/ node_memory_MemTotal_bytes)Exporters
Node Exporter
Coleta métricas do sistema operacional Linux.
# Executar Node Exporter
docker run -d \
--name node-exporter \
--pid=host \
--network=host \
-v /proc:/host/proc:ro \
-v /sys:/host/sys:ro \
-v /:/rootfs:ro \
prom/node-exporter:latest \
--path.procfs=/host/proc \
--path.sysfs=/host/sys \
--collector.filesystem.ignored-mount-points="^/(sys|proc|dev|host|etc)($$|/)"Métricas importantes do Node Exporter:
# CPU idle por core
rate(node_cpu_seconds_total{mode="idle"}[5m])
# Memória disponível
node_memory_MemAvailable_bytes
# Disco livre
node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"}
# Network traffic
rate(node_network_receive_bytes_total[5m])
rate(node_network_transmit_bytes_total[5m])
# Load average
node_load1
node_load5
node_load15JMX Exporter (Java)
# jmx-exporter-config.yml
startDelaySeconds: 0
hostPort: localhost:1099
ssl: false
lowercaseOutputName: true
lowercaseOutputLabelNames: true
whitelistObjectNames:
- "java.lang:type=Memory"
- "java.lang:type=GarbageCollector,*"
- "java.lang:type=Threading"
- "Catalina:type=ThreadPool,*"
rules:
# Memória heap
- pattern: 'java.lang<type=Memory><HeapMemoryUsage>used'
name: jvm_memory_heap_used_bytes
type: GAUGE
# GC time
- pattern: 'java.lang<type=GarbageCollector,name=(.*)><CollectionTime>'
name: jvm_gc_collection_seconds_total
labels:
gc: "$1"
type: COUNTER# Na JVM, adicionar agente:
java -javaagent:/opt/jmx_prometheus_javaagent.jar=9404:/opt/jmx-config.yml \
-jar sua-aplicacao.jarPostgreSQL Exporter
docker run -d \
--name postgres-exporter \
-e DATA_SOURCE_NAME="postgresql://user:pass@host:5432/db?sslmode=disable" \
-p 9187:9187 \
wrouesnel/postgres_exporterMétricas PostgreSQL úteis:
# Conexões ativas
pg_stat_activity_count{state="active"}
# Tamanho dos bancos
pg_database_size_bytes
# Queries lentas
rate(pg_stat_database_blks_hit[5m]) /
(rate(pg_stat_database_blks_read[5m]) + rate(pg_stat_database_blks_hit[5m]))
# Replication lag
pg_replication_lagExporter Customizado
# custom_exporter.py
from prometheus_client import start_http_server, Gauge, Counter
import time
import requests
# Métricas customizadas
api_health = Gauge('external_api_health', 'Status da API externa (1=up, 0=down)',
['api_name'])
api_latency = Gauge('external_api_response_seconds', 'Latência da API externa',
['api_name'])
APIS_TO_CHECK = {
'payment-gateway': 'https://api.pagamento.com/health',
'cep-service': 'https://viacep.com.br/ws/01310100/json/'
}
def check_apis():
for name, url in APIS_TO_CHECK.items():
try:
start = time.time()
resp = requests.get(url, timeout=5)
latency = time.time() - start
api_health.labels(api_name=name).set(1 if resp.status_code < 400 else 0)
api_latency.labels(api_name=name).set(latency)
except Exception:
api_health.labels(api_name=name).set(0)
if __name__ == '__main__':
# Iniciar servidor HTTP na porta 8000
start_http_server(8000)
while True:
check_apis()
time.sleep(30)Instrumentação em Java (Micrometer + Spring Boot)
<!-- pom.xml -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency># application.yml
management:
endpoints:
web:
exposure:
include: health,info,prometheus,metrics
base-path: /actuator
endpoint:
prometheus:
enabled: true
metrics:
tags:
application: ${spring.application.name}
environment: ${spring.profiles.active:dev}
distribution:
percentiles-histogram:
http.server.requests: true # habilita histograma para requisições HTTP
percentiles:
http.server.requests: 0.5, 0.95, 0.99
slo:
http.server.requests: 50ms, 100ms, 200ms, 500ms// Instrumentação customizada com Micrometer
import io.micrometer.core.instrument.*;
@Service
public class OrderService {
private final Counter ordersCreated;
private final Counter ordersFailed;
private final Timer orderProcessingTime;
private final Gauge pendingOrders;
private final AtomicInteger pendingCount = new AtomicInteger(0);
public OrderService(MeterRegistry registry) {
// Counter
this.ordersCreated = Counter.builder("orders.created.total")
.description("Total de pedidos criados")
.tag("service", "orders")
.register(registry);
this.ordersFailed = Counter.builder("orders.failed.total")
.description("Total de pedidos com falha")
.register(registry);
// Timer (equivalente ao Histogram do Prometheus)
this.orderProcessingTime = Timer.builder("orders.processing.duration")
.description("Duração do processamento de pedidos")
.publishPercentiles(0.5, 0.95, 0.99)
.publishPercentileHistogram(true)
.register(registry);
// Gauge
this.pendingOrders = Gauge.builder("orders.pending", pendingCount, AtomicInteger::get)
.description("Pedidos aguardando processamento")
.register(registry);
}
public Order createOrder(OrderRequest request) {
pendingCount.incrementAndGet();
return orderProcessingTime.record(() -> {
try {
Order order = processOrder(request);
ordersCreated.increment();
return order;
} catch (Exception e) {
ordersFailed.increment();
throw e;
} finally {
pendingCount.decrementAndGet();
}
});
}
}Instrumentação em Python
# requirements: prometheus-client==0.19.0
from prometheus_client import (
Counter, Gauge, Histogram, Summary,
start_http_server, CollectorRegistry, push_to_gateway
)
import functools
import time
# Definir métricas no nível do módulo (escopo global)
REQUEST_COUNT = Counter(
'http_requests_total',
'Total de requisições HTTP',
['method', 'endpoint', 'http_status']
)
REQUEST_LATENCY = Histogram(
'http_request_duration_seconds',
'Duração das requisições HTTP',
['method', 'endpoint'],
buckets=[0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0]
)
IN_PROGRESS = Gauge(
'http_requests_in_progress',
'Requisições HTTP em andamento',
['method']
)
# Decorator para instrumentar funções
def track_requests(method, endpoint):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
IN_PROGRESS.labels(method=method).inc()
start = time.time()
status = '200'
try:
result = func(*args, **kwargs)
return result
except Exception as e:
status = '500'
raise
finally:
REQUEST_COUNT.labels(method=method, endpoint=endpoint,
http_status=status).inc()
REQUEST_LATENCY.labels(method=method, endpoint=endpoint)\
.observe(time.time() - start)
IN_PROGRESS.labels(method=method).dec()
return wrapper
return decorator
# Usar com Flask
from flask import Flask
app = Flask(__name__)
@app.route('/api/users', methods=['GET'])
@track_requests('GET', '/api/users')
def list_users():
return {'users': []}
if __name__ == '__main__':
start_http_server(8000) # Prometheus scrape endpoint
app.run(port=5000)Instrumentação em Node.js (prom-client)
// npm install prom-client
const promClient = require('prom-client');
// Criar registry customizado (ou usar o default)
const register = new promClient.Registry();
// Adicionar métricas padrão do processo (CPU, memória, event loop)
promClient.collectDefaultMetrics({ register, prefix: 'node_app_' });
// Counter
const httpRequestsTotal = new promClient.Counter({
name: 'http_requests_total',
help: 'Total de requisições HTTP',
labelNames: ['method', 'route', 'status'],
registers: [register]
});
// Histogram
const httpRequestDuration = new promClient.Histogram({
name: 'http_request_duration_seconds',
help: 'Duração das requisições HTTP',
labelNames: ['method', 'route'],
buckets: [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5],
registers: [register]
});
// Gauge
const activeConnections = new promClient.Gauge({
name: 'active_connections',
help: 'Conexões WebSocket ativas',
registers: [register]
});
// Middleware Express
function metricsMiddleware(req, res, next) {
const start = Date.now();
const route = req.route?.path || req.path;
res.on('finish', () => {
const duration = (Date.now() - start) / 1000;
httpRequestsTotal.labels(req.method, route, res.statusCode).inc();
httpRequestDuration.labels(req.method, route).observe(duration);
});
next();
}
// Endpoint de métricas
app.use(metricsMiddleware);
app.get('/metrics', async (req, res) => {
res.setHeader('Content-Type', register.contentType);
res.send(await register.metrics());
});Pushgateway
Use o Pushgateway para jobs de curta duração (batch jobs, cron jobs) que não vivem tempo suficiente para serem scraped.
Quando usar:
- Cron jobs que executam por segundos/minutos
- Scripts de migração
- Jobs de processamento em lote
Quando NÃO usar:
- Serviços de longa duração (use scrape normal)
- Como proxy genérico de métricas
# Iniciando o Pushgateway
docker run -d -p 9091:9091 prom/pushgateway
# Push via curl (API REST)
# POST /metrics/job/<job_name>/[<label_name>/<label_value>]*
echo 'batch_job_duration_seconds 23.5' | \
curl --data-binary @- http://pushgateway:9091/metrics/job/backup_job/instance/server-1
# Múltiplas métricas
cat <<EOF | curl --data-binary @- http://pushgateway:9091/metrics/job/etl_pipeline
# TYPE etl_records_processed counter
etl_records_processed 15432
# TYPE etl_errors_total counter
etl_errors_total 3
# TYPE etl_duration_seconds gauge
etl_duration_seconds 127.4
EOF
# Deletar métricas de um job
curl -X DELETE http://pushgateway:9091/metrics/job/etl_pipeline/instance/server-1# Push com Python
from prometheus_client import CollectorRegistry, Gauge, Counter, push_to_gateway
registry = CollectorRegistry()
duration = Gauge('batch_job_duration_seconds', 'Duração do job',
registry=registry)
records = Counter('batch_records_processed_total', 'Registros processados',
registry=registry)
# ... executar job ...
duration.set(127.4)
records.inc(15432)
push_to_gateway('pushgateway:9091', job='etl_pipeline',
registry=registry,
grouping_key={'instance': 'server-1'})Armazenamento e Retenção
# prometheus.yml — flags de armazenamento (passados na linha de comando)
# --storage.tsdb.path=/prometheus # diretório dos dados
# --storage.tsdb.retention.time=15d # retenção por tempo
# --storage.tsdb.retention.size=50GB # retenção por tamanho
# --storage.tsdb.wal-compression # comprimir WAL# Linha de comando completa
prometheus \
--config.file=/etc/prometheus/prometheus.yml \
--storage.tsdb.path=/var/prometheus/data \
--storage.tsdb.retention.time=30d \
--storage.tsdb.retention.size=100GB \
--storage.tsdb.wal-compression \
--web.enable-lifecycle \ # habilita reload via API
--web.enable-admin-api # habilita API de administração# Reload de configuração sem restart
curl -X POST http://localhost:9090/-/reload
# Verificar configuração
promtool check config prometheus.yml
# Verificar regras
promtool check rules rules/*.ymlFederation
Permite que um Prometheus “global” scrape métricas de Prometheus locais (multi-datacenter, multi-cluster).
# prometheus-global.yml
scrape_configs:
- job_name: 'federated-dc-us-east'
scrape_interval: 30s
honor_labels: true # preservar labels do Prometheus de origem
metrics_path: '/federate'
params:
# Quais métricas federar (regex)
match[]:
- '{job="api-backend"}'
- '{__name__=~"job:.*"}' # apenas recording rules
- 'up'
static_configs:
- targets:
- 'prometheus-us-east.internal:9090'
- 'prometheus-eu-west.internal:9090'
labels:
datacenter: 'us-east'Thanos e Cortex (HA e Retenção Longa)
Thanos — Conceitos
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Prometheus │ │ Prometheus │ │ Prometheus │
│ + Sidecar │ │ + Sidecar │ │ + Sidecar │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
└──────────────────┼──────────────────┘
│ Thanos Store Gateway
▼
┌─────────────┐
│ Object Store│ (S3, GCS, MinIO)
│ (cold data) │
└─────────────┘
│
┌─────────────┐
│ Querier │ (PromQL global)
└─────────────┘
│
┌─────────────┐
│ Compactor │ (downsampling, deduplication)
└─────────────┘# docker-compose.thanos.yml (Thanos Sidecar com Prometheus)
services:
prometheus:
image: prom/prometheus:latest
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus-data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--storage.tsdb.retention.time=2h' # Thanos assume os dados antigos
- '--storage.tsdb.min-block-duration=2h'
- '--storage.tsdb.max-block-duration=2h'
- '--web.enable-lifecycle'
thanos-sidecar:
image: thanosio/thanos:latest
command:
- 'sidecar'
- '--tsdb.path=/prometheus'
- '--prometheus.url=http://prometheus:9090'
- '--objstore.config-file=/etc/thanos/s3.yml'
- '--http-address=0.0.0.0:19191'
- '--grpc-address=0.0.0.0:10901'
volumes:
- prometheus-data:/prometheus
- ./s3.yml:/etc/thanos/s3.ymlDocker Compose Completo
# docker-compose.monitoring.yml
version: '3.9'
volumes:
prometheus-data: {}
alertmanager-data: {}
grafana-data: {}
networks:
monitoring:
driver: bridge
services:
prometheus:
image: prom/prometheus:v2.48.0
container_name: prometheus
restart: unless-stopped
networks:
- monitoring
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- ./rules:/etc/prometheus/rules:ro
- prometheus-data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--storage.tsdb.retention.time=30d'
- '--web.enable-lifecycle'
- '--web.enable-admin-api'
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:9090/-/ready"]
interval: 30s
timeout: 10s
retries: 3
alertmanager:
image: prom/alertmanager:v0.26.0
container_name: alertmanager
restart: unless-stopped
networks:
- monitoring
ports:
- "9093:9093"
volumes:
- ./alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro
- alertmanager-data:/alertmanager
command:
- '--config.file=/etc/alertmanager/alertmanager.yml'
- '--storage.path=/alertmanager'
node-exporter:
image: prom/node-exporter:v1.7.0
container_name: node-exporter
restart: unless-stopped
networks:
- monitoring
pid: host
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
grafana:
image: grafana/grafana:10.2.0
container_name: grafana
restart: unless-stopped
networks:
- monitoring
ports:
- "3000:3000"
environment:
GF_SECURITY_ADMIN_PASSWORD: admin
GF_USERS_ALLOW_SIGN_UP: false
GF_DASHBOARDS_DEFAULT_HOME_DASHBOARD_PATH: /etc/grafana/provisioning/dashboards/overview.json
volumes:
- grafana-data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning:ro
pushgateway:
image: prom/pushgateway:v1.7.0
container_name: pushgateway
restart: unless-stopped
networks:
- monitoring
ports:
- "9091:9091"Referência Rápida de PromQL
# Operadores de agregação
sum() avg() min() max() count() stddev() stdvar()
topk(N, expr) bottomk(N, expr) quantile(φ, expr)
count_values("label", expr)
# Modificadores de agregação
by (label1, label2) # agrupar pelos labels listados
without (label1) # agrupar por todos, exceto os listados
# Funções para counters
rate(v[d]) # taxa por segundo (recomendado)
irate(v[d]) # taxa instantânea
increase(v[d]) # aumento absoluto
# Funções para histograms
histogram_quantile(φ, v)
# Funções no tempo
avg_over_time(v[d])
max_over_time(v[d])
min_over_time(v[d])
sum_over_time(v[d])
count_over_time(v[d])
stddev_over_time(v[d])
quantile_over_time(φ, v[d])
last_over_time(v[d])
# Funções matemáticas
abs() ceil() floor() round() sqrt()
exp() ln() log2() log10()
clamp_min() clamp_max()
delta() deriv()
predict_linear(v[d], t) # projeção linear
# Funções de labels
label_replace(v, dst, replacement, src, regex)
label_join(v, dst, separator, src1, src2, ...)
# Funções de tempo
time() # timestamp atual em segundos
timestamp(v) # timestamp de cada amostra
minute() hour() day_of_week() month() year()
# Modificadores de query
offset 1h # deslocar no tempo
@ 1700000000 # query em timestamp absoluto
subquery: v[1h:5m] # subquery com resolução customizadaRemote Write e Remote Storage
Por que o armazenamento local não é suficiente
O TSDB local do Prometheus foi projetado para dados recentes e alta velocidade de escrita, não para retenção longa. Limitações práticas:
- Retenção padrão: 15 dias (configurável com
--storage.tsdb.retention.time, mas cresce o disco) - Sem redundância: se o servidor cai, os dados do WAL podem ser perdidos
- Sem HA nativo: dois Prometheus independentes coletam dados duplicados, mas não os sincronizam
- Escala vertical: um único servidor tem limite de cardinalidade (~10 milhões de séries ativas)
A solução é enviar os dados para um remote storage enquanto o Prometheus local continua servindo queries recentes.
Configuração remote_write
# prometheus.yml
remote_write:
- url: "http://thanos-receive:19291/api/v1/receive"
# ou Mimir: http://mimir:8080/api/v1/push
# ou Cortex: http://cortex:9009/api/prom/push
# Autenticação básica (Mimir/Cortex multi-tenant)
basic_auth:
username: "tenant-id"
password: "secret"
# Controle de fila (tuning de performance)
queue_config:
capacity: 10000 # buffer in-memory de samples
max_shards: 200 # paralelismo máximo de workers
max_samples_per_send: 500
batch_send_deadline: 5s
# Filtrar métricas antes de enviar (reduzir custo/tráfego)
write_relabel_configs:
- source_labels: [__name__]
regex: "go_.*|process_.*"
action: drop # não envia métricas internas do runtimeConfiguração remote_read
# prometheus.yml
remote_read:
- url: "http://thanos-query:9090/api/v1/read"
read_recent: false # true = lê do remote mesmo para dados recentes
required_matchers:
env: "production" # só faz remote_read se a query contiver esse labelCom remote_read, o Prometheus transparentemente complementa dados locais com dados históricos do remote storage ao executar queries.
Thanos: Sidecar vs Receive
Duas arquiteturas distintas para integrar o Prometheus com o Thanos:
# Arquitetura Sidecar (recomendado para Prometheus existentes)
┌─────────────────────────────────────────────┐
│ Pod / VM │
│ ┌──────────────┐ ┌──────────────────┐ │
│ │ Prometheus │◄──►│ Thanos Sidecar │ │
│ │ :9090 │ │ :10902 (HTTP) │ │
│ └──────────────┘ │ :10901 (gRPC) │ │
│ └────────┬─────────┘ │
└────────────────────────────────│─────────────┘
│ upload blocos a cada 2h
▼
Object Storage (S3/GCS/MinIO)
# Arquitetura Receive (para remote_write direto)
Prometheus ──remote_write──► Thanos Receive ──► Object Storage
│
▼ (replicação)
Thanos Receive replicaThanos Sidecar:
- Copia blocos TSDB do disco do Prometheus para o object storage a cada 2 horas
- Expõe a API StoreAPI para o Thanos Query consultar dados locais
- Zero impacto no Prometheus (só lê do disco)
Thanos Receive:
- Atua como endpoint de
remote_write - Grava dados diretamente, sem depender do disco do Prometheus
- Suporta replicação multi-réplica (fator configurável)
Compactação e Downsampling com Thanos Store/Compactor
# thanos-compactor (roda como job periódico, não contínuo)
thanos compact \
--data-dir /tmp/thanos-compact \
--objstore.config-file /etc/thanos/objstore.yml \
--retention.resolution-raw=30d \ # dados brutos (1 ponto por scrape)
--retention.resolution-5m=90d \ # downsampled: 1 ponto a cada 5 min
--retention.resolution-1h=1y \ # downsampled: 1 ponto por hora
--wait # roda em loop contínuoNíveis de resolução:
raw— dados originais, 1 ponto porscrape_interval(ex: 15s)5m— gerado automaticamente pelo compactor quando dados têm >40h1h— gerado quando dados têm >10 dias
# Consultar dados históricos via Thanos Query
thanos query \
--store thanos-sidecar:10901 \ # dados recentes (via sidecar)
--store thanos-store:10901 \ # dados históricos (do object storage)
--query.replica-label replica # deduplica métricas de HA replicasService Discovery Avançado
Como funciona o ciclo de descoberta
Prometheus executa o ciclo de service discovery continuamente. A cada intervalo (refresh_interval, padrão 5m), ele:
- Consulta os providers configurados (Kubernetes API, arquivo, DNS, EC2…)
- Gera uma lista de targets com labels prefixados por
__meta_* - Aplica
relabel_configspara filtrar e transformar labels - Os targets resultantes entram no scraping
Labels prefixados com __ são internos e não aparecem nas métricas salvas (exceto os que forem copiados via relabeling).
kubernetes_sd_configs
# prometheus.yml — scraping de pods no Kubernetes
scrape_configs:
- job_name: "kubernetes-pods"
kubernetes_sd_configs:
- role: pod # role: pod | service | endpoints | node | ingress
namespaces:
names: ["production", "staging"]
# kubeconfig_file: /etc/prometheus/kubeconfig # fora do cluster
# Se dentro do cluster, usa ServiceAccount automático
relabel_configs:
# Scrape apenas pods com annotation prometheus.io/scrape: "true"
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep
regex: "true"
# Porta customizada via annotation prometheus.io/port
- source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
action: replace
regex: "([^:]+)(?::\\d+)?;(\\d+)"
replacement: "$1:$2"
target_label: __address__
# Path customizado via annotation prometheus.io/path
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
action: replace
target_label: __metrics_path__
regex: "(.+)"
# Copiar namespace como label da métrica
- source_labels: [__meta_kubernetes_namespace]
target_label: namespace
# Copiar nome do pod
- source_labels: [__meta_kubernetes_pod_name]
target_label: podRoles disponíveis no Kubernetes SD:
| Role | Descobre | __address__ padrão |
|---|---|---|
pod | cada Pod | podIP:containerPort |
service | cada Service | clusterIP:port |
endpoints | endpoints de Services | podIP:port |
node | cada Node | nodeIP:10250 (kubelet) |
ingress | cada Ingress | host do Ingress |
file_sd_configs — inventário dinâmico
Ideal para ambientes fora do Kubernetes ou para integrar com ferramentas de CMDB.
# prometheus.yml
scrape_configs:
- job_name: "file-targets"
file_sd_configs:
- files:
- "/etc/prometheus/targets/*.json"
- "/etc/prometheus/targets/*.yaml"
refresh_interval: 1m # recarrega arquivos periodicamente// /etc/prometheus/targets/servers.json
[
{
"targets": ["10.0.1.10:9100", "10.0.1.11:9100"],
"labels": {
"env": "production",
"datacenter": "us-east-1",
"role": "database"
}
},
{
"targets": ["10.0.2.5:9100"],
"labels": {
"env": "staging",
"role": "api"
}
}
]Prometheus detecta mudanças nos arquivos sem precisar recarregar config (SIGHUP).
ec2_sd_configs
scrape_configs:
- job_name: "ec2-instances"
ec2_sd_configs:
- region: us-east-1
access_key: AKID... # ou usa IAM role da instância
secret_key: secret...
port: 9100 # porta padrão para node_exporter
filters:
- name: "tag:monitored" # filtra por tag AWS
values: ["true"]
relabel_configs:
# Usar private DNS como label
- source_labels: [__meta_ec2_private_dns_name]
target_label: instance
# Tag "Name" da EC2 como label
- source_labels: [__meta_ec2_tag_Name]
target_label: name
# AZ como label
- source_labels: [__meta_ec2_availability_zone]
target_label: azdns_sd_configs
scrape_configs:
- job_name: "dns-services"
dns_sd_configs:
- names:
- "_prometheus._tcp.example.com" # SRV record
type: SRV
refresh_interval: 30s
- names:
- "api.example.com" # A/AAAA record
type: A
port: 9090__address__ vs labels __meta_*
__address__ → endereço de scrape (host:port) — usado pelo Prometheus para conectar
__scheme__ → http ou https
__metrics_path__ → caminho da rota /metrics (padrão: /metrics)
__param_<name> → parâmetros de query string
# Labels __meta_* são gerados pelo SD provider — exemplos:
__meta_kubernetes_pod_name → nome do pod
__meta_kubernetes_pod_namespace → namespace
__meta_kubernetes_pod_label_<label> → qualquer label do pod
__meta_kubernetes_pod_annotation_<ann> → qualquer annotation do pod
__meta_ec2_instance_id → ID da instância EC2
__meta_ec2_tag_<tag> → tag da EC2
# Todos os __meta_* e __ são descartados após relabeling
# → copie para labels normais se quiser que apareçam nas métricasRelabeling — relabel_configs
O que é e quando acontece
relabel_configs transforma os labels de um target antes do scrape acontecer. O fluxo completo é:
SD Provider gera targets com __meta_* labels
↓
relabel_configs (filtra e transforma)
↓
Targets finais com labels normais
↓
Prometheus scrape /metrics
↓
metric_relabel_configs (transforma labels das métricas coletadas)
↓
Métricas salvas no TSDBmetric_relabel_configs funciona igual, mas é aplicado às métricas após o scrape (útil para drop de métricas caras).
Campos de uma regra de relabeling
relabel_configs:
- source_labels: [label1, label2] # labels de entrada (concatenados com separator)
separator: ";" # separador padrão entre source_labels
target_label: novo_label # label de destino (para replace)
regex: "(.*)" # regex aplicada ao valor concatenado
replacement: "$1" # valor de substituição (suporta grupos de captura)
action: replace # ação a executar (padrão: replace)
modulus: 10 # usado com action: hashmodAções disponíveis
| Ação | O que faz |
|---|---|
replace | Se regex faz match, define target_label = replacement. Padrão. |
keep | Mantém apenas targets onde regex faz match nos source_labels |
drop | Remove targets onde regex faz match nos source_labels |
labelmap | Copia labels cujos nomes fazem match com regex para novos nomes definidos por replacement |
labeldrop | Remove labels cujos nomes fazem match com regex |
labelkeep | Mantém apenas labels cujos nomes fazem match com regex |
hashmod | Define target_label = hash(source_labels) % modulus (para sharding) |
Exemplos práticos
relabel_configs:
# 1. Filtrar pods por annotation — manter apenas os que pedem scraping
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep
regex: "true"
# 2. Renomear __meta_kubernetes_pod_name para label "pod"
- source_labels: [__meta_kubernetes_pod_name]
target_label: pod
# 3. Renomear __meta_kubernetes_namespace para "namespace"
- source_labels: [__meta_kubernetes_namespace]
target_label: namespace
# 4. Copiar todos os labels do pod (k8s) para labels da métrica via labelmap
# __meta_kubernetes_pod_label_app → app
# __meta_kubernetes_pod_label_version → version
- action: labelmap
regex: __meta_kubernetes_pod_label_(.+)
# 5. Drop de targets de namespaces de sistema
- source_labels: [__meta_kubernetes_namespace]
action: drop
regex: "kube-system|monitoring"
# 6. Construir __address__ customizado combinando IP + porta de annotation
- source_labels: [__meta_kubernetes_pod_ip, __meta_kubernetes_pod_annotation_prometheus_io_port]
separator: ":"
target_label: __address__
# 7. Remover labels internos antes de salvar
- action: labeldrop
regex: "__meta_.*"
# 8. Sharding: enviar apenas 1/3 dos targets para este Prometheus
- source_labels: [__address__]
modulus: 3
target_label: __tmp_hash
action: hashmod
- source_labels: [__tmp_hash]
action: keep
regex: "0" # este shard processa o módulo 0metric_relabel_configs — remover métricas caras após scrape
scrape_configs:
- job_name: "kubernetes-pods"
metric_relabel_configs:
# Drop de métricas de alta cardinalidade que não são usadas
- source_labels: [__name__]
regex: "go_gc_duration_seconds.*|go_memstats_.*"
action: drop
# Normalizar label "code" para apenas 2xx/3xx/4xx/5xx
- source_labels: [code]
regex: "(\\d)\\d+"
replacement: "${1}xx"
target_label: code_class