¿Qué es la telemetría abierta?

OpenTelemetry (OTEL para abreviar) es un marco de observabilidad, diseñado para crear y gestionar datos de telemetría (trazas, métricas y registros). OpenTelemetry es proveedor agnóstico, lo que significa que no está vinculado a una plataforma específica y se puede utilizar para enviar datos de telemetría a cualquier backend de observabilidad que lo soporte, por ejemplo: Azure Monitor (AppInsights), AWS, Grafana, Dynatrace y New Relic.

¿Qué telemetría podemos obtener?

Huellas

Las trazas nos permiten “rastrear” el camino de una petición (o quizás entrada para ser más generales) una vez que entra en tu aplicación. No importa si hablamos de un único monolito o de una complicada malla de servicios, OTEL facilita el seguimiento del procesamiento de la solicitud y la detección de cualquier problema por el camino.

Registros

Puedes enviar los registros generados por tus aplicaciones a un backend utilizando el marco OTEL y vincularlos automáticamente a las trazas para que puedas ver lo que está pasando y encontrar cualquier problema con facilidad.

Métricas

Medidas que podemos tomar para obtener información sobre disponibilidad y rendimiento

¿Cómo podemos conseguirlo?

Para obtener telemetría necesitamos instrumentar nuestros sistemas. Instrumentar una base de código significa añadir código para generar y enviar telemetría, como marcar el inicio y el final de una operación, emitir métricas al recibir una nueva solicitud, etc.

Esto puede hacerse manualmente utilizando las herramientas sdk de OTEL del lenguaje apropiado y añadiendo unas pocas líneas de código, o utilizando librerías de instrumentación. Son librerías especiales que inyectan instrumentación en librerías (y frameworks) de uso común, por ejemplo, en Python tenemos librerías de instrumentación para requests, Django, flask, MySQL, etc. (lo veremos en el ejemplo).

También tenemos que configurar los exportadores de telemetría para enviar la telemetría al backend. Open Telemetry sdk viene con algunos genéricos, pero los backends pueden tener sus propios exportadores como paquetes separados como plugins (¡también puede venir con sus propios paquetes de plugins!).

Existen tres tipos de exportadores: de trazas, de tramos y de registros. La mayoría de los exportadores necesitan ser configurados utilizando algún argumento que puede ser proporcionado por variables de entorno.

La aplicación de ejemplo

Veamos cómo utilizar open telemetry en un proyecto Python con un sencillo ejemplo de flask.

He creado tres sencillas aplicaciones flask: server, api1 y api2.

Necesitamos usar una versión de flask inferior a la 3.0 ya que la 3 no está soportada todavía por OTEL, y debido a un problema con la librería, necesitaremos forzar werkzeug a una versión inferior a la 3.0.0.

pip3 install 'werkzeug<3.0.0' 'flask<3'

servidor.py

import requests

from random import randint

from flask import Flask

from time import sleep

app = Flask(__name__)

@app.route("/callserviceok")

def callOK():

   # lets waste some time

   sleep(2)

   # then call our service

   response = requests.get("http://localhost:8081/get-data")

   # imagine we are processing something here

   sleep(1)

   response = requests.get("http://localhost:8082/get-data")

   return str(response.content)

@app.route("/callserviceerr")

def callErr():

   # lets waste some time

   sleep(2)

   # imagine we are processing something here

   sleep(1)

   response = requests.get("http://localhost:8082/err")

   return str(response.content)

api1.py

from flask import Flask

from opentelemetry.trace import get_tracer, SpanKind

from time import sleep

tracer = get_tracer(__name__)

app = Flask(__name__)

@app.route("/get-data")

def get_data():

   sleep(3)

   data = get_data_from_db()

   return data

def get_data_from_db():

   with tracer.start_as_current_span("my_database", kind=SpanKind.CLIENT):

       sleep(2)

       return "my data"

api2.py

from flask import Flask

from opentelemetry.trace import get_tracer, SpanKind

tracer = get_tracer(__name__)

app = Flask(__name__)

@app.route("/get-data")

def get_data():

   return "my data"

@app.route("/err")

def err():

   raise Exception("An error occured")

Elegir un backend

Como se mencionó anteriormente, puede utilizar OTEL para enviar telemetría a cualquier backend que lo soporte,

En general, instrumentar el proyecto es la parte laboriosa, y cambiar a o añadir un backend es sólo cuestión de instalar algunos paquetes y utilizar las variables de entorno adecuadas.

Existen varios proyectos backend de observabilidad gratuitos que podemos utilizar para recoger y visualizar telemetría como Jaeger y Zipkin. En este ejemplo, usaré una instancia de Zipkin en docker.

Configuración de OpenTelemetry

En primer lugar, debemos instalar los paquetes necesarios. La implementación OpenTelemetry Python (requiere Python 3.6 o superior) proporciona herramientas de línea de comandos para arrancar el trabajo de instrumentación.

pip install opentelemetry-distro

El comando opentelemetry-bootstrap detectará los paquetes instalados en su entorno local y listará o instalará las librerías de instrumentación necesarias (puede que aún necesite instalar manualmente algunos paquetes adicionales de librerías de instrumentación).

opentelemetry-bootstrap -a install

También podemos utilizar el comando para obtener una lista requirements.txt de paquetes a instalar.

`opentelemetry-bootstrap -a requirements`

También necesitará instalar paquetes adicionales (los exportadores) dependiendo de su elección de backend, en este caso para zipkin.

pip install opentelemetry-exporter-zipkin

Instrumentación de nuestras aplicaciones

Para este ejemplo, utilizaremos la autoinstrumentación. Este enfoque podría ser suficiente si su proyecto se basa en un marco popular como Django o flask y hace uso de bases de datos comunes y bibliotecas de Python, ya que la telemetría que necesita es probable que ya sea proporcionada por las bibliotecas de instrumentación disponibles.

El comando opentelemetry-instrument wrapper inyecta toda la instrumentación a la aplicación Python de destino, podemos configurarlo utilizando variables de entorno o argumentos de línea de comandos.

Configurar variables de entorno

Como quiero que esto sea corto, el único parámetro que configuraré usando variables de entorno será el endpoint de zipkin, el resto de parámetros irán a través de argumentos.

export OTEL_EXPORTER_ZIPKIN_ENDPOINT=http://localhost:9411/api/v2/spans

Ejecutar las aplicaciones con instrumentación

Vamos a ejecutar esta aplicación al mismo tiempo, en diferentes consolas.

opentelemetry-instrument --traces_exporter zipkin --metrics_exporter none --service_name server flask --app server.py  run -p 8080

opentelemetry-instrument --traces_exporter zipkin --metrics_exporter none --service_name service-api-1 flask --app api1.py  run -p 8081

opentelemetry-instrument --traces_exporter zipkin --metrics_exporter none --service_name service-api-2 flask --app api2.py  run -p 8082

Esto ejecutará la instrumentación y habilitará el exportador de trazas zipkin (también deshabilitará el de métricas, ya que no lo mostraremos aquí). Para cada aplicación, definimos un nombre de servicio, y así es como identificaremos cada servicio.

Veamos lo que puede hacer

Ahora, vamos a llamar a la aplicación servidor desde el navegador al endpoint /callserviceok. Todos nuestros rastros se mostrarán en zipkin:

Esto es lo que vemos si hacemos clic en “mostrar” en la traza principal:

Zipkin es bastante básico, pero lo que vemos aquí estará presente en todos los demás programas de monitorización. Aquí podemos ver la línea de tiempo de la traza y qué se llamó, cuándo, a dónde, el estado de la respuesta, etc. Además, un montón de datos recogidos como mi agente de usuario.

El endpoint llama a los dos servicios API, service-api-1 que simula algo de trabajo y una llamada a un servidor de base de datos, y service-api-2 que devuelve inmediatamente.

Vamos a simular un error ocurrido en una de las APIs llamando al endpoint /callserviceerr en su lugar.

Lanzará una excepción en api2, y aquí vemos lo fácil que es detectarla en la lista de peticiones.

He aquí los detalles de la solicitud:

Como puede ver, la información de error se recoge automáticamente, y podemos ver claramente cuál de las solicitudes de API falló (api2).

Zipkin también muestra un mapa de dependencias generado a partir de los datos de rastreo

Un software de monitorización más complejo como Azure Appinsight, Dynatrace o Grafana permite recopilar registros para obtener una imagen más clara de lo que está ocurriendo. La telemetría abierta es una herramienta realmente potente y está implementada para muchos lenguajes de programación. Como se puede ver en este ejemplo, la telemetría abierta permite a zipkin correlacionar toda la información de diferentes servicios en un flujo, y esto funciona fuera de la caja la mayor parte del tiempo. Digamos que tenemos una aplicación javascript compleja en el frontend, con unos pocos pasos podemos instrumentarla también, y ser capaces de inspeccionar de un vistazo todo el comportamiento de la aplicación desde el frontend hasta el backend.