OpenTelemetry (III): uso de OpenTelemetry en Compute

Ya está disponible nuestra primera biblioteca de OpenTelemetry para Compute, por lo que tu aplicación de Compute puede generar seguimientos conforme a las especificaciones, que te darán información sobre su rendimiento y sus recursos. En este artículo verás lo fácil que es añadir esta funcionalidad a una aplicación en el edge.

La plataforma Compute ejecuta en toda nuestra red de servidores del edge módulos de WebAssembly compilados a partir de tu lenguaje favorito. Aunque oficialmente es compatible con Rust, AssemblyScript y JavaScript, algunos de nuestros clientes más ingeniosos han lanzado aplicaciones creadas en C++, Swift y Zig.

OpenTelemetry es compatible con las bibliotecas de muchos de estos lenguajes, de modo que los desarrolladores pueden trabajar con llamadas y objetos del SDK en lugar de tener que comunicarse en el lenguaje del protocolo sin procesar, como sucede al añadir OpenTelemetry a un servicio VCL, tal y como vimos en la segunda parte de esta serie de artículos. Los exportadores predeterminados incluidos en las bibliotecas de OpenTelemetry no se pueden usar directamente. Esto sucede porque, por motivos de seguridad, la única comunicación con el exterior que permite Compute son las llamadas al host que exponemos a través de los SDK. Aun así, gracias al diseño modular de OpenTelemetry, podemos ampliar las bibliotecas oficiales de OpenTelemetry con componentes compatibles con Compute.

Gracias a ello, hemos podido desarrollar la compatibilidad de OpenTelemetry en Compute, empezando por JavaScript.

Configuración de un recopilador

Antes de empezar a emitir datos, necesitamos un recopilador que acepte los seguimientos que enviará tu aplicación. En una arquitectura de producción debes tener un recopilador de OpenTelemetry con un receptor OTLP mediante HTTP habilitado, ejecutándose en algún servidor al alcance de tu aplicación. Este recopilador, a su vez, exporta los datos a un backend. Gracias a OpenTelemetry, se pueden exportar a muchos tipos de backend como, por ejemplo, instancias de Jaegar o ZipKin, o servicios como Honeycomb.

Si quieres hacer pruebas en tu entorno local, lo más fácil para ejecutar un recopilador es usar OpenTelemetry Collector Demo, que pone en marcha un recopilador y varios backends, como Jaeger. Las capturas de pantalla de este artículo reflejan esta configuración y muestran la interfaz de usuario de Jaeger.

Para poder usar Collector Demo con los ejemplos de este artículo, vas a necesitar Docke. Además, tendrás que seguir las instrucciones para ejecutar la demo haciendo algunos cambios en su configuración para habilitar OTLP mediante HTTP. Modifica el archivo otel-collector-config.yaml para habilitar el protocolo http:

receivers:
otlp:
protocols:
grpc:
http: # Add this
exporters:
...

Como Collector Demo se ejecuta en Docker, tenemos que habilitar el puerto para que esté disponible y, de paso, deshabilitar algunos de sus componentes que no necesitamos. Para ello, añade docker-compose.override.yaml a examples/demo/:

version: "2"
services:
otel-collector:
ports:
- "4318:4318" # OTLP HTTP receiver
demo-server:
profiles:
- disabled
demo-client:
profiles:
- disabled

Después de aplicar los cambios, ya puedes iniciar Collector Demo:

$ cd examples/demo
$ docker-compose up

En cuanto se ejecute, puedes empezar a enviar seguimientos a tu recopilador, que los exportará tanto a Jaeger como a ZipKin. Tienes la IU de Jaeger en http://localhost:16686/ y la de ZipKin en http://localhost:9411/

Ahora sí que lo tenemos todo listo para empezar a recopilar datos, así que ya podemos usar OpenTelemetry en Compute.

Cómo añadir OpenTelemetry a una aplicación

Por lo general, una aplicación de JavaScript instrumentada con OpenTelemetry añadirá un archivo de inicialización de seguimiento (denominado «tracing.js» o algo similar), que crea instancias de lo siguiente:

  • Resource: describe la aplicación al recopilador.

  • Instrumentations: módulos que se enlazan a eventos de la plataforma o del marco para generar datos de instrumentación.

  • Exporter: un módulo que puede extraer datos de OpenTelemetry y enviarlos a un recopilador externo.

  • SDK: un agente que aglutina todo lo anterior.

He creado un paquete de código abierto en JavaScript para poder implementar todo esto en Compute. Se llama @fastly/compute-js-opentelemetry on npm e incluye, entre otras cosas, los siguientes componentes personalizados:

  • instrumentaciones para el ciclo de vida de peticiones de Fastly, además de capturas de datos del backend desde tu código en el edge;

  • un exportador personalizado, capaz de enviar los datos de OpenTelemetry a un recopilador mediante los mecanismos de registro en tiempo real o captura de datos del backend de Fastly;

  • un SDK personalizado que simplifica la conexión entre el exportador, las instrumentaciones y los recursos.

Para que veas lo fácil que es añadir OpenTelemetry, te voy a mostrar una sencilla aplicación de Compute creada en JavaScript:

/// <reference types='@fastly/js-compute' />
async function handleRequest(event) {
const backendResponse = await fetch('https://httpbin.org/json', {
backend: 'httpbin',
});
const data = await backendResponse.text();
return new Response(data.length, {
status: 200,
Headers: {
'Content-Type': 'text/plain',
},
});
}
addEventListener('fetch', (event) => event.respondWith(handleRequest(event)));

En este ejemplo, para responder a una petición, se capturan algunos datos del backend httpbin, se mide el tamaño de los datos y se crea una respuesta para el cliente con el resultado.

Para añadir OpenTelemetry, debemos agregar a nuestro proyecto @fastly/compute-js-opentelemetry y algunas bibliotecas de OpenTelemetry:

$ npm install @fastly/compute-js-opentelemetry
$ npm install @opentelemetry/resources @opentelemetry/semantic-conventions

Para poder usar estas bibliotecas en Compute, se necesitan unos cuantos polyfills y shims, que pueden importarse fácilmente mediante el paquete de ayuda @fastly/compute-js-opentelemetry/webpack-helpers durante el proceso de compilación. Para ello, debes modificar el archivo webpack.config.js de tu proyecto:

// Reference the helper module
const webpackHelpers = require("@fastly/compute-js-opentelemetry/webpack-helpers");
module.exports = {
entry: "./src/index.js",
/* ... other configuration */
};
// Add this line
module.exports = webpackHelpers.apply(module.exports);

Luego debes crear un archivo de inicialización del seguimiento, tracing.js, como este:

import { Resource } from '@opentelemetry/resources';
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
import { FastlySDK } from '@fastly/compute-js-opentelemetry/sdk-fastly';
import { OTLPTraceExporter } from '@fastly/compute-js-opentelemetry/exporter-trace-otlp-fastly-backend';
import { getComputeJsAutoInstrumentations } from '@fastly/compute-js-opentelemetry/auto-instrumentations-compute-js';
const sdk = new FastlySDK({
traceExporter: new OTLPTraceExporter({backend: 'otlp-collector'}),
instrumentations: [getComputeJsAutoInstrumentations(),],
resource: new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: 'my-fastly-service',
}),
});
await sdk.start();

Es muy parecido al archivo de inicialización del seguimiento del tutorial sobre Node.js de OpenTelemetry, aunque algunos componentes se han sustituido por implementaciones de Compute. En él, el exportador presupone que hay un recopilador registrado en el servicio de Fastly asociado al otlp-collector del backend.

Para activar el seguimiento, importa tracing.js en tu aplicación:

/// <reference types='@fastly/js-compute' />
import './tracing.js';
async function handleRequest(event) {
...

A continuación, ejecuta la aplicación para empezar a recibir intervalos de tus peticiones como por arte de magia.

Otel blog pt. 3 - image 1

Si examinamos este seguimiento, podemos ver algunas cosas interesantes.

En primer lugar, tenemos un intervalo para la petición en su totalidad, FetchEvent, y varios intervalos secundarios para la función listener, el evento Backend Fetch (a example.com) y la llamada a event.respondWith, que en conjunto forman una invocación. Como puedes ver, la función listener y event.respondWith se ejecutan muy rápidamente. Esto sucede porque la función handler devuelve de inmediato una promesa, lo que permite que los servicios de Compute puedan seguir procesándose después de que se reciba la respuesta de la función listener. Mientras tanto, nuestro programa continúa con el evento Backend Fetch. El SDK, por su parte, también aprovecha este mecanismo para ampliar la duración del evento hasta que se hayan enviado todos los datos al recopilador y lo oculta para que no tengas que preocuparte de él.

Es fantástico que tracing.js esté completamente separado del código de tu aplicación. El mecanismo de seguimiento y las instrumentaciones predeterminadas configuradas en este archivo son suficientes para generar telemetría al margen de tu aplicación.

Intervalos personalizados

Sin embargo, algunas veces lo que quieres es instrumentar el código con eventos e intervalos personalizados porque, por ejemplo, quieres marcar los tiempos de inicio y fin de una función o porque te interesa saber con qué frecuencia se le llama.

Pues bien, puedes hacer todo eso porque estamos implementando OpenTelemetry estándar:

$ npm install @opentelemetry/api
/// <reference types='@fastly/js-compute' />
import './tracing.js';
import { context, trace } from "@opentelemetry/api";
async function handleRequest(event) {
const tracer = trace.getTracerProvider()
.getTracer('my-tracer');
const mySpan = tracer.startSpan('my-task');
context.with(trace.setSpan(context.active(), mySpan), () => {
doTask(); // spend some time in a task
});
mySpan.end();
return new Response('OK', {
status: 200,
headers: new Headers({"Content-Type": "text/plain"}),
});
}
addEventListener("fetch", (event) => event.respondWith(handleRequest(event)));

Este es el seguimiento que se genera:

Otel blog #3 image 2

Esta vez no tenemos ningún Backend Fetch, pero sí vemos un intervalo personalizado denominado my-task que ha tardado 19 ms.

El único código específico de Fastly se encuentra en el módulo de inicialización del seguimiento y todo lo que ejecutamos en la fuente principal del programa con OpenTelemetry se realiza con las API de OpenTelemetry estándar. Es decir, no se requiere nada específico para Fastly. De hecho, el código que incorpores en tu aplicación de Compute y que ya se haya instrumentado con OpenTelemetry funcionará perfectamente, y el mecanismo de seguimiento que hemos configurado también captará esos datos.

Propagación del contexto de seguimiento

Como acabo de decir, el mecanismo de seguimiento capta los datos de OpenTelemetry del resto de código incorporado en la aplicación de Compute y generado por tu propio código. Pero ahí no queda la cosa: OpenTelemetry nos permite ir más allá y captar datos incluso de otros procesos y API web que se invocan desde la aplicación de Fastly.

Uno de los mayores atractivos de OpenTelemetry, y también uno de sus puntos fuertes, es que te permite visualizar la arquitectura de todo el sistema, con seguimientos que abarcan diversos componentes, incluso procesos y servidores. Esto es posible gracias a la propagación del contexto de seguimiento, es decir, la capacidad de que un seguimiento iniciado en un componente propague su contexto a otros componentes a los que llame. Tal y como explicamos en la segunda parte de esta serie de artículos, las API web utilizan el encabezado traceparent para ello. 

Las instrumentaciones incluidas en @fastly/compute-js-opentelemetry facilitan este mecanismo tanto en las peticiones entrantes como en las salientes. Como consecuencia, si se llama a tu aplicación en el contexto de un seguimiento que ya existe, esta biblioteca extrae la información de ese seguimiento de los encabezados de la petición entrante y lo usa como elemento principal de cualquier intervalo generado. Del mismo modo, si tu aplicación ejecuta capturas de datos del backend, esta biblioteca inserta el contexto del seguimiento actual en los encabezados de la petición durante las capturas. Lo mejor de todo es que se activan automáticamente al inicializar el mecanismo de seguimiento con getComputeJsAutoInstrumentations.

Eso significa que una mera llamada a las API que emiten OpenTelemetry genera automáticamente seguimientos anidados como estos:

Otel #3 - image 3

Esa es la gran ventaja de OpenTelemetry: mientras todos los componentes emitan seguimientos que pueda captar un recopilador, este podrá juntarlos y generar estos tipos de representaciones gráficas. Gracias a ello, podrás realizar un seguimiento del ciclo de vida completo de una ejecución de extremo a extremo en la totalidad de tu aplicación, componente a componente.

Todos estos datos quedan a disposición de cualquier herramienta de análisis e información que quieras usar, y creemos que nuestras herramientas harán que Fastly ocupe un lugar destacado en la arquitectura de tu sistema.

Cuestiones pendientes

OpenTelemetry todavía está en pañales: queda mucho por pulir conforme vayan evolucionando las bibliotecas oficiales y aumente el número de herramientas, plataformas y marcos compatibles.

Una de las cuestiones pendientes es que la API de métricas y el SDK de las bibliotecas de OpenTelemetry para JavaScript siguen en fase de desarrollo. Por eso, nuestra biblioteca todavía no es del todo compatible con las métricas, aunque tenemos previsto actualizarla a medida que vayan evolucionando las bibliotecas oficiales y esperamos poder usar las métricas muy pronto.

Un vistazo a la biblioteca

¿A ti también te entusiasma OpenTelemetry? A mí me pasó la primera vez que vi los diagramas de seguimiento que se generaban desde mis propias demos. Puedes echar un vistazo al código fuente y a la documentación de la biblioteca de JavaScript de OpenTelementry para Compute de la que hablamos en este artículo. La tienes en fastly/compute-js-opentelemetry en GitHub.

La llegada de OpenTelemetry a Compute es una gran noticia. Si usas esta biblioteca para añadir instrumentación a tu aplicación de Compute, cuéntanoslo para que sepamos cómo te ha ido.

En la cuarta parte de esta serie veremos un caso práctico del uso de OpenTelemetry para instrumentar nuestra propia herramienta Fiddle. ¡No te lo pierdas!


Ya están publicadas las cuatro partes de nuestra serie de artículos sobre OpenTelemetry:

Katsuyuki Omuro
Senior Software Engineer, Developer Relations
Fecha de publicación:

8 min de lectura

Comparte esta entrada
Katsuyuki Omuro
Senior Software Engineer, Developer Relations

Katsuyuki, o «Kats», es un desarrollador afincado en Japón y un entusiasta de la web en tiempo real que trabaja en el equipo de Developer Relations. Siempre intenta averiguar cómo funcionan las cosas y disfruta ayudando a los demás a seguir aprendiendo y evolucionando.

¿List@ para empezar?

Ponte en contacto o crea una cuenta.