Controla tus servicios con OTEL, Jaeger y Prometheus

Hablemos de una pregunta importante: ¿cómo monitoreamos nuestros servicios si algo sale mal?

Por un lado, contamos con Prometheus con alertas y Kibana para paneles y otras funciones útiles. También sabemos cómo recopilar registros: la pila ELK es nuestra solución preferida. Sin embargo, simplemente llevar un registro no siempre es suficiente: no proporciona una vista holística del recorrido de una solicitud a través de todo el ecosistema de componentes.

Puedes encontrar más información sobre ELK aquí.

Pero, ¿qué ocurre si queremos visualizar las solicitudes? ¿Y si necesitamos correlacionar las solicitudes que viajan entre sistemas? Esto se aplica tanto a microservicios como a monolitos; no importa cuántos servicios tengamos, sino cómo gestionamos su latencia.

De hecho, cada solicitud de usuario puede pasar por toda una cadena de servicios independientes, bases de datos, colas de mensajes y APIs externas.

En un entorno tan complejo, resulta extremadamente difícil precisar dónde se producen los retrasos, identificar qué parte de la cadena actúa como cuello de botella de rendimiento y encontrar rápidamente la causa raíz de las fallas cuando ocurren.

Para abordar estos desafíos de manera efectiva, necesitamos un sistema centralizado y consistente para recopilar datos de telemetría: trazas, métricas y registros. Aquí es donde OpenTelemetry y Jaeger entran en acción.

Veamos los conceptos básicos

Hay dos términos principales que debemos entender:

Identificador de traza

Un Identificador de traza es un identificador de 16 bytes, a menudo representado como una cadena hexadecimal de 32 caracteres. Se genera automáticamente al inicio de una traza y permanece igual en todos los segmentos creados por una solicitud particular. Esto facilita ver cómo una solicitud viaja a través de diferentes servicios o componentes en un sistema.

Identificador de segmento

Cada operación individual dentro de una traza recibe su propio Identificador de segmento, que generalmente es un valor de 64 bits generado aleatoriamente. Los segmentos comparten el mismo Identificador de traza, pero cada uno tiene un Identificador de segmento único, por lo que puedes identificar exactamente qué parte del flujo de trabajo representa cada segmento (como una consulta a una base de datos o una llamada a otro microservicio).

¿Cómo están relacionados?

Identificador de traza y Identificador de segmento se complementan mutuamente. 

Cuando se inicia una solicitud, se genera un Identificador de traza y se pasa a todos los servicios involucrados. Cada servicio, a su vez, crea un segmento con un Identificador de segmento único vinculado al Identificador de traza, lo que te permite visualizar todo el ciclo de vida de la solicitud de principio a fin.

De acuerdo, entonces ¿por qué no usar solo Jaeger? ¿Por qué necesitamos OpenTelemetry (OTEL) y todas sus especificaciones? ¡Esa es una excelente pregunta! Vamos a analizarlo paso a paso.

Encuentra más sobre Jaeger aquí.

TL;DR

  • Jaeger es un sistema para almacenar y visualizar trazas distribuidas. Recoge, almacena, busca y muestra datos que muestran cómo las solicitudes “viajan” a través de tus servicios.
  • OpenTelemetry (OTEL) es un estándar (y un conjunto de bibliotecas) para recopilar datos de telemetría (trazas, métricas, registros) de tus aplicaciones e infraestructura. No está vinculado a ninguna herramienta de visualización o backend en particular.

En pocas palabras:

  • OTEL es como un “lenguaje universal” y un conjunto de bibliotecas para la recopilación de telemetría.
  • Jaeger es un backend y una interfaz de usuario para ver y analizar trazas distribuidas.

¿Por qué necesitamos OTEL si ya tenemos Jaeger?

1. Un único estándar para la recopilación

En el pasado, hubo proyectos como OpenTracing y OpenCensus. OpenTelemetry unifica estos enfoques para recopilar métricas y trazas en un único estándar universal.

2. Integración fácil

Escribes tu código en Go (o en otro lenguaje), añades bibliotecas de OTEL para la auto-inyección de interceptores y spans, y eso es todo. Después, no importa a dónde quieras enviar esos datos—Jaeger, Tempo, Zipkin, Datadog, un backend personalizado—OpenTelemetry se encarga de la conexión. Solo cambias el exportador.

3. No solo trazas

OpenTelemetry cubre trazas, pero también maneja métricas y registros. Terminas con un único conjunto de herramientas para todas tus necesidades de telemetría, no solo trazas.

4. Jaeger como backend

Jaeger es una excelente opción si estás principalmente interesado en la visualización de trazas distribuidas. Pero no proporciona la instrumentación entre lenguajes de forma predeterminada. OpenTelemetry, por otro lado, te ofrece una forma estandarizada de recopilar datos, y luego decides a dónde enviarlos (incluyendo Jaeger).

En la práctica, a menudo trabajan juntos:

Tu aplicación utiliza OpenTelemetry → se comunica a través del protocolo OTLP → va al OpenTelemetry Collector (HTTP o grpc) → se exporta a Jaeger para visualización.


Parte técnica

Diseño del sistema (un poco)

Esbozaremos rápidamente un par de servicios que harán lo siguiente:

  1. Servicio de compras – procesa un pago y lo registra en MongoDB
  2. CDC con Debezium – escucha los cambios en la tabla de MongoDB y los envía a Kafka
  3. Procesador de compras – consume el mensaje de Kafka y llama al Servicio de Autenticación para buscar el user_id para validación
  4. Servicio de Autenticación – un servicio de usuario simple

En resumen:

  • 3 servicios en Go
  • Kafka
  • CDC (Debezium)
  • MongoDB

Parte del código

Comencemos con la infraestructura. Para unir todo en un solo sistema, crearemos un gran archivo de Docker Compose. Empezaremos configurando la telemetría.

Nota: Todo el código está disponible a través de un enlace al final del artículo, incluyendo la infraestructura.

YAML

 

También configuraremos el colector, el componente que recopila la telemetría.

Aquí, elegimos gRPC para la transferencia de datos, lo que significa que la comunicación ocurrirá a través de HTTP/2:

YAML

 

Asegúrate de ajustar las direcciones según sea necesario, y habrás terminado con la configuración base.

Ya sabemos que OpenTelemetry (OTEL) utiliza dos conceptos clave — ID de Rastreo e ID de Segmento— que ayudan a rastrear y monitorear las solicitudes en sistemas distribuidos.

Implementando el Código

Ahora, veamos cómo hacer que esto funcione en tu código de Go. Necesitamos las siguientes importaciones:

Go

 

Luego, agregamos una función para inicializar nuestro rastreador en main() cuando la aplicación se inicia:

Go

 

Con el rastreo configurado, solo necesitamos colocar segmentos en el código para rastrear las llamadas. Por ejemplo, si queremos medir las llamadas a la base de datos (ya que generalmente es el primer lugar donde buscamos problemas de rendimiento), podemos escribir algo como esto:

Go

 

Tenemos el rastreo en la capa de servicios — ¡genial! Pero podemos ir aún más profundo, instrumentando la capa de la base de datos:

Go

 

Ahora tenemos una vista completa del viaje de la solicitud. Dirígete a la interfaz de Jaeger, consulta las últimas 20 trazas bajo servicio-de-autenticación, y verás todos los segmentos y cómo se conectan en un solo lugar.

Ahora, todo es visible. Si lo necesitas, puedes incluir la consulta completa en las etiquetas. Sin embargo, ten en cuenta que no debes sobrecargar tu telemetría, agrega datos deliberadamente. Simplemente estoy demostrando lo que es posible, pero incluir la consulta completa de esta manera no es algo que generalmente recomendaría.

Cliente-servidor gRPC

Si deseas ver un rastreo que abarca dos servicios gRPC, es bastante sencillo. Todo lo que necesitas es agregar los interceptores listos para usar de la biblioteca. Por ejemplo, en el lado del servidor:

Go

 

En el lado del cliente, el código es igual de corto:

Go

 

¡Eso es todo! Asegúrate de que tus exportadores estén configurados correctamente y verás un solo ID de Rastreo registrado en estos servicios cuando el cliente llama al servidor.

Manejo de Eventos CDC y Rastreo

¿Quieres manejar también eventos del CDC? Un enfoque simple es incrustar el ID de Rastreo en el objeto que MongoDB almacena. De esta manera, cuando Debezium captura el cambio y lo envía a Kafka, el ID de Rastreo ya es parte del registro.

Por ejemplo, si estás utilizando MongoDB, puedes hacer algo así:

Go

 

Luego, Debezium recoge este objeto (incluyendo trace_id) y lo envía a Kafka. En el lado del consumidor, simplemente parseas el mensaje entrante, extraes el trace_id y lo fusionas en tu contexto de rastreo:

Go

 

Go

 

Alternativa: Uso de Cabeceras de Kafka

A veces, es más fácil almacenar el ID de traza en los encabezados de Kafka en lugar de en la carga útil en sí misma. Para flujos de trabajo de CDC, esto podría no estar disponible de forma predeterminada — Debezium puede limitar lo que se agrega a los encabezados. Pero si controlas el lado del productor (o si estás utilizando un productor estándar de Kafka), puedes hacer algo así con Sarama:

Inyectar un ID de traza en los encabezados

Go

 

Extraer un ID de traza en el lado del consumidor

Go

 

Dependiendo de tu caso de uso y de cómo esté configurado tu canalización de CDC, puedes elegir el enfoque que mejor funcione:

  1. Incrustar el ID de traza en el registro de la base de datos para que fluya naturalmente a través de CDC.
  2. Usar los encabezados de Kafka si tienes más control sobre el lado del productor o si deseas evitar inflar la carga del mensaje.

De cualquier manera, puedes mantener tus trazas consistentes en múltiples servicios, incluso cuando los eventos se procesan de forma asincrónica a través de Kafka y Debezium.

Conclusión

Utilizar OpenTelemetry y Jaeger proporciona trazas detalladas de solicitudes, lo que te ayuda a identificar dónde y por qué se producen retrasos en sistemas distribuidos.

Agregar Prometheus completa el cuadro con métricas — indicadores clave de rendimiento y estabilidad. Juntos, estas herramientas forman un completo conjunto de observabilidad, permitiendo una detección y resolución más rápidas de problemas, optimización de rendimiento y confiabilidad general del sistema.

Puedo decir que este enfoque acelera significativamente la resolución de problemas en un entorno de microservicios y es una de las primeras cosas que implementamos en nuestros proyectos.

Enlaces

Source:
https://dzone.com/articles/control-services-otel-jaeger-prometheus