11. Jaeger v2 sobre OpenTelemetry Collector

Por: Artiko
jaegerjaeger-v2opentelemetry-collectormigracion

11. Jaeger v2 sobre OpenTelemetry Collector

En 2024 Jaeger publicó la versión 2 con una reescritura arquitectónica: en lugar de ser un proyecto monolítico independiente, Jaeger v2 se construye sobre OpenTelemetry Collector como base.

Idea central: el código del collector y agente que mantenía Jaeger por años hacía lo mismo que ya hace OTel Collector. Reescribir Jaeger sobre el collector elimina duplicación, alinea el ecosistema y aprovecha el trabajo de toda la comunidad OTel.


Qué se mantiene


Qué cambia

El binario

ComponenteJaeger v1Jaeger v2
CollectorBinario propioOTel Collector con extensiones Jaeger
QueryBinario propioOTel Collector con extensiones Jaeger
AgentBinario propio (deprecado)Eliminado
IngesterBinario propioOTel Collector con extensión Kafka
All-in-oneSingle binarioSingle binario OTel Collector

Hay un único binario jaeger en v2. Su comportamiento (collector, query, all-in-one) se decide vía configuración YAML.

La configuración

Jaeger v1 usaba flags de CLI y variables de entorno. Jaeger v2 usa YAML estilo OTel Collector:

service:
  extensions: [jaeger_storage, jaeger_query]
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [jaeger_storage_exporter]

extensions:
  jaeger_storage:
    backends:
      some_storage:
        memory:
          max_traces: 100000
  jaeger_query:
    storage:
      traces: some_storage

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

processors:
  batch:

exporters:
  jaeger_storage_exporter:
    trace_storage: some_storage

Si ya conocés la configuración de OTel Collector, esto te resulta familiar. Si venís de Jaeger v1, hay un período de adaptación.


Empezar con Jaeger v2

Imagen Docker

docker run -d --name jaeger-v2 \
  -p 16686:16686 \
  -p 4317:4317 \
  -p 4318:4318 \
  -v $PWD/jaeger-config.yaml:/etc/jaeger/config.yaml \
  jaegertracing/jaeger:2.0.0 \
  --config=/etc/jaeger/config.yaml

jaeger-config.yaml con storage en memoria:

service:
  extensions: [jaeger_storage, jaeger_query]
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [jaeger_storage_exporter]

extensions:
  jaeger_storage:
    backends:
      memory_store:
        memory:
          max_traces: 100000
  jaeger_query:
    storage:
      traces: memory_store

receivers:
  otlp:
    protocols:
      grpc:
      http:

processors:
  batch:
    timeout: 1s

exporters:
  jaeger_storage_exporter:
    trace_storage: memory_store

UI sigue en http://localhost:16686/.


Storage en v2

Cada backend se declara en la extensión jaeger_storage:

extensions:
  jaeger_storage:
    backends:
      es_main:
        elasticsearch:
          servers: ["http://elasticsearch:9200"]
          index_prefix: "jaeger"
      memory_archive:
        memory:
          max_traces: 50000
  jaeger_query:
    storage:
      traces: es_main
      traces_archive: memory_archive  # opcional

Notá:


Pipelines: el verdadero superpoder

Como ahora Jaeger es un OTel Collector, podés meter cualquier processor en el pipeline:

Tail sampling integrado

processors:
  tail_sampling:
    decision_wait: 30s
    policies:
      - { name: errors, type: status_code, status_code: { status_codes: [ERROR] } }
      - { name: slow, type: latency, latency: { threshold_ms: 1000 } }
      - { name: prob, type: probabilistic, probabilistic: { sampling_percentage: 1 } }

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [tail_sampling, batch]
      exporters: [jaeger_storage_exporter]

En v1 necesitabas un OTel Collector externo para esto. En v2 viene incluido.

Atributos: redacción de info sensible

processors:
  attributes:
    actions:
      - key: http.url
        action: update
        from_attribute: http.url
        pattern: 'token=[^&]+'
        replacement: 'token=REDACTED'
      - key: user.email
        action: hash

Filter: descartar lo que no querés guardar

processors:
  filter:
    error_mode: ignore
    traces:
      span:
        - 'attributes["http.route"] == "/health"'
        - 'attributes["http.route"] == "/metrics"'

Adiós a las trazas de health checks llenando tu storage.

Dual-write a múltiples backends

exporters:
  jaeger_storage_exporter/main:
    trace_storage: cassandra_main
  otlp/tempo:
    endpoint: tempo:4317

service:
  pipelines:
    traces:
      receivers: [otlp]
      exporters: [jaeger_storage_exporter/main, otlp/tempo]

Migración a otro backend sin downtime: escribís a los dos por un período, validás, cortás el viejo.


Breaking changes vs v1

Compatibilidad con SDKs

OK: SDKs de OpenTelemetry exportando vía OTLP siguen funcionando sin cambios.

No OK: clientes Jaeger deprecados (Thrift sobre UDP/HTTP) requieren receptor especial:

receivers:
  jaeger:
    protocols:
      grpc:
        endpoint: 0.0.0.0:14250
      thrift_http:
        endpoint: 0.0.0.0:14268

Configuración

YAML reemplaza CLI flags y env vars. Migración no es 1:1, hay que reescribir el setup.

Agent eliminado

Si tenías sidecar agents, hay que migrar las apps a OTLP directo o usar el OTel Collector como sidecar.


Cuándo migrar

Migrá ya si

Esperá si

Jaeger v1 sigue mantenido. No hay urgencia operacional de migrar inmediatamente, pero el desarrollo activo se está moviendo a v2.


Migración paso a paso

flowchart TD
    P1[1. Levantar Jaeger v2 en paralelo\ncon mismo storage que v1] --> P2[2. Apuntar 5-10% del tráfico a v2\ncon dual-pipeline o LB]
    P2 --> P3[3. Validar UI y queries equivalentes]
    P3 --> P4[4. Migrar gradualmente al 100%]
    P4 --> P5[5. Apagar v1]

Detalles operativos:

  1. Schema compartido: para storage como ES o Cassandra, el schema es compatible. Mismo cluster, mismos índices.
  2. Ports: hacé que v2 escuche en el mismo puerto que v1 cuando cambies, para que apps no necesiten reconfig.
  3. Smoke tests: antes de cortar v1, verificá búsquedas, lookups por trace_id, dependency graph y monitor.

Beneficios concretos que ya disfrutamos


¿Qué viene?

Cubrimos la teoría y la migración. En el próximo capítulo bajamos al deployment de producción real: Kubernetes, Operator, Helm, alta disponibilidad y observabilidad del propio Jaeger.