Cap 8: Backends de Observabilidad

Por: Artiko
opentelemetryjaegerprometheusgrafanalokihoneycomb

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:

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:

  1. Remote Write (desde el Collector)
  2. Prometheus scrape (expones /metrics en 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:

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)

BackendSeñalesVentaja
HoneycombTraces + LogsColumnar, queries ad-hoc sin pre-definir métricas
DatadogTraces + Metrics + LogsSuite completa, correlación automática
New RelicTraces + Metrics + LogsFree tier generoso con OTLP nativo
Grafana CloudStack completo (Grafana managed)Mismas herramientas que self-hosted
AWS X-RayTracesIntegración nativa con servicios AWS
Google Cloud TraceTracesIntegració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]