Cap 8: Backends de Observabilidad
El stack open source completo
La combinación más común para observabilidad self-hosted:
flowchart LR
APP[Aplicaciones\nSDK OTel] -->|OTLP| COL[OTel Collector]
COL -->|traces| TEMPO[Grafana Tempo\no Jaeger]
COL -->|metrics| PROM[Prometheus]
COL -->|logs| LOKI[Grafana Loki]
TEMPO --> GRAFANA[Grafana]
PROM --> GRAFANA
LOKI --> GRAFANA
Jaeger — Traces distribuidos
Jaeger es el backend de traces open source más usado. Desarrollado por Uber, ahora en la CNCF.
Acepta: OTLP/gRPC, OTLP/HTTP, Thrift (legacy)
Características principales:
- UI para explorar traces con flamegraphs
- Búsqueda por service, operación, tags, duración
- Comparación de traces
- Análisis de dependencias entre servicios
Docker Compose (modo all-in-one para desarrollo):
services:
jaeger:
image: jaegertracing/all-in-one:latest
ports:
- "4317:4317" # OTLP gRPC
- "4318:4318" # OTLP HTTP
- "16686:16686" # UI de Jaeger
environment:
- COLLECTOR_OTLP_ENABLED=true
Acceder en http://localhost:16686.
Para producción: usar Jaeger con Elasticsearch o Cassandra como storage, o migrar a Grafana Tempo (más eficiente en almacenamiento).
Grafana Tempo — Traces (alternativa a Jaeger)
Tempo es el backend de traces de Grafana Labs. Más eficiente en storage que Jaeger (usa object storage: S3, GCS).
Ventaja clave: integración nativa con Grafana, correlación directa entre métricas, logs y traces.
services:
tempo:
image: grafana/tempo:latest
command: ["-config.file=/etc/tempo.yaml"]
volumes:
- ./tempo.yaml:/etc/tempo.yaml
ports:
- "4317:4317" # OTLP gRPC
- "3200:3200" # API de Tempo
# tempo.yaml
server:
http_listen_port: 3200
distributor:
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
storage:
trace:
backend: local
local:
path: /tmp/tempo/blocks
Prometheus — Métricas
Prometheus es el estándar de facto para métricas en sistemas cloud-native.
Dos formas de recibir métricas de OTel:
- Remote Write (desde el Collector)
- Prometheus scrape (expones
/metricsen tu app o Collector)
Exporter Prometheus en la app:
from opentelemetry.exporter.prometheus import PrometheusMetricReader
from prometheus_client import start_http_server
# Exponer endpoint /metrics en puerto 8000
start_http_server(port=8000, addr="0.0.0.0")
reader = PrometheusMetricReader()
provider = MeterProvider(resource=resource, metric_readers=[reader])
Prometheus scrape config:
# prometheus.yml
scrape_configs:
- job_name: 'my-service'
static_configs:
- targets: ['my-service:8000']
scrape_interval: 15s
Docker Compose:
services:
prometheus:
image: prom/prometheus:latest
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.retention.time=15d'
Queries PromQL básicas:
# Rate de requests por segundo
rate(http_server_request_count_total[5m])
# Percentil 95 de latencia
histogram_quantile(0.95, rate(http_server_request_duration_bucket[5m]))
# Tasa de errores
rate(http_server_request_count_total{status_code=~"5.."}[5m])
/ rate(http_server_request_count_total[5m])
# Costo de Claude Code por modelo (si exportas OTel de Claude Code)
rate(claude_code_cost_usage_total[1h])
Grafana Loki — Logs
Loki es el backend de logs de Grafana Labs. A diferencia de Elasticsearch, solo indexa labels (no el contenido completo del log) — mucho más eficiente en storage.
Exportar a Loki desde el Collector:
# En el Collector
exporters:
loki:
endpoint: http://loki:3100/loki/api/v1/push
default_labels_enabled:
exporter: true
job: true
service:
pipelines:
logs:
receivers: [otlp]
processors: [batch]
exporters: [loki]
Docker Compose:
services:
loki:
image: grafana/loki:latest
ports:
- "3100:3100"
command: -config.file=/etc/loki/local-config.yaml
Queries LogQL básicas:
# Logs de un servicio
{service_name="order-service"}
# Filtrar por nivel de severidad
{service_name="order-service"} |= "ERROR"
# Logs con trace_id específico
{service_name="order-service"} | json | trace_id="abc123"
# Contar errores por minuto
sum(rate({service_name="order-service"} |= "ERROR" [1m]))
Grafana — UI unificada
Grafana conecta los tres backends en una sola interfaz:
Data sources a configurar:
- Prometheus → métricas
- Tempo o Jaeger → traces
- Loki → logs
La correlación funciona así:
1. Ves en Grafana un spike de latencia en una métrica Prometheus
2. Haces clic en el spike → link directo a Tempo/Jaeger
3. Ves el trace que tardó mucho → encuentras el span lento
4. Haces clic en el span → link directo a Loki
5. Ves los logs exactos de ese span con su trace_id
Esta navegación requiere configurar las correlaciones en Grafana (Explore → Derived Fields).
Docker Compose:
services:
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_AUTH_ANONYMOUS_ENABLED=true
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
volumes:
- ./grafana/datasources:/etc/grafana/provisioning/datasources
- ./grafana/dashboards:/etc/grafana/provisioning/dashboards
Stack completo con Docker Compose
# docker-compose.yaml
version: "3.8"
services:
otel-collector:
image: otel/opentelemetry-collector-contrib:latest
command: ["--config=/etc/otel-collector-config.yaml"]
volumes:
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
ports:
- "4317:4317"
- "4318:4318"
depends_on: [tempo, prometheus, loki]
tempo:
image: grafana/tempo:latest
command: ["-config.file=/etc/tempo.yaml"]
volumes:
- ./tempo.yaml:/etc/tempo.yaml
ports:
- "3200:3200"
prometheus:
image: prom/prometheus:latest
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
loki:
image: grafana/loki:latest
ports:
- "3100:3100"
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_AUTH_ANONYMOUS_ENABLED=true
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
volumes:
- ./grafana/:/etc/grafana/provisioning/
depends_on: [tempo, prometheus, loki]
Backends SaaS (alternativas cloud)
| Backend | Señales | Ventaja |
|---|---|---|
| Honeycomb | Traces + Logs | Columnar, queries ad-hoc sin pre-definir métricas |
| Datadog | Traces + Metrics + Logs | Suite completa, correlación automática |
| New Relic | Traces + Metrics + Logs | Free tier generoso con OTLP nativo |
| Grafana Cloud | Stack completo (Grafana managed) | Mismas herramientas que self-hosted |
| AWS X-Ray | Traces | Integración nativa con servicios AWS |
| Google Cloud Trace | Traces | Integración nativa con GCP |
Todos aceptan OTLP — cambiar de backend es solo cambiar el endpoint del exporter.
Comparativa de elección
flowchart TD
Q1{¿Self-hosted o SaaS?} -->|Self-hosted| Q2
Q1 -->|SaaS| Q3
Q2{¿Ya usas Grafana?} -->|Sí| GRAF[Tempo + Loki\n+ Prometheus + Grafana]
Q2{¿Ya usas Grafana?} -->|No| JAE[Jaeger + Prometheus\n+ Loki + Grafana]
Q3{¿Budget?} -->|Limitado| NR[New Relic\nfree tier 100GB/mes]
Q3 -->|Flexible + análisis ad-hoc| HNY[Honeycomb]
Q3 -->|Enterprise| DD[Datadog]