Tracing Distribuido
Spans, traces, OpenTelemetry, Jaeger y cómo seguir requests a través de múltiples servicios.
¿Qué es el tracing distribuido?
Cuando un request del usuario atraviesa 5, 10 o 20 servicios antes de devolver una respuesta, ¿cómo sabés dónde se gastó el tiempo? ¿Qué servicio falló? ¿Qué llamada fue la más lenta?
El tracing distribuido resuelve exactamente este problema: permite seguir el recorrido completo de un request a través de todos los servicios que participan, midiendo tiempos y capturando contexto en cada paso.
Conceptos fundamentales
Trace
Un trace representa el recorrido completo de una operación end-to-end. Por ejemplo, un trace de “crear orden” incluye todo desde que el frontend envía el request hasta que el usuario recibe la confirmación.
Cada trace tiene un identificador único (traceId) que se propaga entre todos los servicios involucrados.
Span
Un span representa una unidad de trabajo dentro de un trace. Cada servicio o componente que participa en la operación crea uno o más spans.
Un span contiene:
- Nombre: Descripción de la operación (ej: “POST /api/orders”).
- Timestamps: Inicio y fin de la operación.
- SpanId: Identificador único del span.
- ParentSpanId: Referencia al span padre, formando un árbol.
- Tags/Attributes: Metadatos adicionales (método HTTP, status code, etc.).
- Events/Logs: Eventos puntuales dentro del span.
- Status: OK, ERROR o UNSET.
Contexto de propagación
Para que el tracing funcione entre servicios, el contexto (traceId, spanId) debe propagarse en cada llamada. Los mecanismos más comunes son:
- Headers HTTP:
traceparent,tracestate(estándar W3C Trace Context). - Headers de mensajería: Atributos en mensajes de Kafka, RabbitMQ, SQS.
- gRPC metadata: Propagación automática en llamadas gRPC.
OpenTelemetry
OpenTelemetry (OTel) es el estándar abierto para instrumentación de observabilidad. Unifica la recolección de trazas, métricas y logs bajo una sola API.
¿Por qué OpenTelemetry?
Antes de OTel, cada vendor tenía su propia API de instrumentación. Si usabas Jaeger, instrumentabas con la API de Jaeger. Si cambiabas a Zipkin, tenías que reinstrumentar todo. OpenTelemetry resuelve esto con una API vendor-neutral.
Componentes de OTel
- API: Interfaz para instrumentar código. Es estable y no cambia.
- SDK: Implementación de la API con configuración de exportadores, samplers, etc.
- Exporters: Envían datos a backends específicos (Jaeger, Zipkin, OTLP).
- Collector: Componente intermedio que recibe, procesa y exporta telemetría.
- Auto-instrumentación: Librerías que instrumentan frameworks populares automáticamente.
El Collector
El OTel Collector es un componente clave en arquitecturas de producción:
Servicios → OTel Collector → Backend (Jaeger, Tempo, etc.)
Ventajas de usar un Collector:
- Desacoplamiento: Los servicios no necesitan saber a qué backend enviar datos.
- Procesamiento: Puede filtrar, samplear y enriquecer trazas antes de exportarlas.
- Buffering: Absorbe picos de tráfico sin perder datos.
- Multi-backend: Puede enviar datos a múltiples destinos simultáneamente.
Jaeger
Jaeger es una plataforma de tracing distribuido open-source, originalmente desarrollada por Uber.
Funcionalidades principales
- Visualización de traces: Interfaz web para explorar traces como diagramas de cascada (waterfall).
- Búsqueda: Encontrar traces por servicio, operación, tags, duración o traceId.
- Comparación: Comparar dos traces para identificar diferencias de rendimiento.
- Grafos de dependencia: Visualizar las relaciones entre servicios basándose en las trazas recolectadas.
Arquitectura de Jaeger
Jaeger puede desplegarse de varias formas:
- All-in-one: Un solo binario para desarrollo y testing.
- Producción: Collector, almacenamiento (Elasticsearch, Cassandra) y Query service separados.
- Con OTel Collector: Usando el Collector como receptor en lugar del agente de Jaeger.
Alternativas a Jaeger
- Zipkin: Otra plataforma open-source de tracing, más simple.
- Grafana Tempo: Backend de trazas optimizado para costo, integrado con Grafana.
- AWS X-Ray: Servicio managed de tracing en AWS.
- Datadog APM: Plataforma SaaS con tracing integrado.
Correlación entre señales
El verdadero poder del tracing aparece cuando se correlaciona con logs y métricas:
Trace → Logs
Incluí el traceId en cada línea de log. Cuando encontrás un trace lento o con errores, podés buscar todos los logs asociados filtrando por ese traceId.
Trace → Métricas
Las trazas pueden generar métricas automáticamente:
- Latencia por servicio y operación.
- Tasa de errores por endpoint.
- Distribución de duración de spans.
Esto se conoce como “métricas derivadas de trazas” y herramientas como Grafana Tempo lo soportan nativamente.
Exemplars
Los exemplars son enlaces desde una métrica hacia un trace específico. Cuando ves un pico de latencia en un dashboard, podés hacer clic y saltar directamente al trace que lo causó.
Sampling
En sistemas de alto tráfico, capturar el 100% de las trazas puede ser prohibitivamente costoso. El sampling permite capturar solo un subconjunto representativo.
Tipos de sampling
- Head-based sampling: Se decide al inicio del trace si se captura o no. Simple pero puede perder traces interesantes.
- Tail-based sampling: Se decide al final del trace, cuando se conoce el resultado. Permite capturar el 100% de errores y traces lentos, pero requiere buffering.
- Rate limiting: Capturar un máximo de N traces por segundo.
Estrategia recomendada
- Capturá el 100% de errores — siempre.
- Capturá el 100% de traces lentos (por encima de un umbral).
- Aplicá sampling probabilístico al resto (ej: 10% de traces exitosos).
Buenas prácticas
Instrumentación significativa
No instrumentes cada función — instrumentá las operaciones que cruzan límites:
- Llamadas HTTP/gRPC entre servicios.
- Queries a bases de datos.
- Publicación/consumo de mensajes.
- Llamadas a servicios externos.
Nombres de spans descriptivos
Usá nombres que identifiquen la operación, no la implementación:
- Bien:
POST /api/orders,OrderRepository.save - Mal:
handler,function1,doStuff
Atributos útiles
Agregá atributos que ayuden a diagnosticar problemas:
http.method,http.status_code,http.urldb.system,db.statement(sin datos sensibles)user.id,order.id(identificadores de negocio)
Propagación consistente
Asegurate de que el contexto se propague en todos los canales de comunicación — HTTP, mensajería, cron jobs, workers asincrónicos. Un trace incompleto es peor que no tener trace.
Resumen
El tracing distribuido es el tercer pilar de la observabilidad y posiblemente el más valioso en arquitecturas de microservicios. Permite entender el flujo completo de una operación, identificar cuellos de botella y diagnosticar errores que involucran múltiples servicios. Con OpenTelemetry como estándar y herramientas como Jaeger, implementar tracing es más accesible que nunca.