OpenTelemetry te ayuda a ver qué ocurre dentro de una aplicación y en el servidor. El programa envía a un mismo lugar las trazas de las peticiones, las métricas y las entradas del registro, y desde ahí puedes pasarlas a cualquier almacén y examinarlas en líneas de tiempo y gráficos. El intermediario principal de este esquema se llama OpenTelemetry Collector, y es el que levantaremos en el VPS. En este artículo iniciaremos Collector y Jaeger con Docker Compose, enviaremos datos de prueba, conectaremos una aplicación en Python sin tocar su código, abriremos una traza en el navegador y añadiremos las métricas del propio servidor. Hemos repetido todos los pasos en un servidor de pruebas con Debian 12, la versión de Collector en el momento de la prueba era la 0.163.1 y la de Jaeger, la 2.22.0.
Cómo funciona OpenTelemetry
Mientras una aplicación consta de un solo proceso, para entender su comportamiento suele bastar el registro. Pero cuando una petición pasa por varios servicios, por ejemplo por una aplicación web, una cola y una base de datos, el registro se queda corto. Quieres ver el recorrido de una petición concreta y saber cuánto tiempo pasó en cada paso, y de paso comprobar si el propio servidor se está quedando sin memoria o sin disco. Los datos que los programas y los servidores dan sobre su propio trabajo se llaman telemetría. OpenTelemetry, u OTel para abreviar, es un conjunto abierto de especificaciones, interfaces de programación y herramientas que describe cómo crear, transmitir y recoger la telemetría. No está atado a un proveedor concreto, así que los mismos datos se pueden enviar a Jaeger, a Prometheus o a un servicio comercial. Aun así, conviene ver enseguida el límite. Según la definición de la documentación oficial, OpenTelemetry no es un sistema de observabilidad, es decir, no guarda los datos ni dibuja gráficos, sino que deja el almacenamiento y la visualización a otros programas.
La telemetría se divide en tres tipos principales de señales. La traza (trace en inglés) describe el recorrido de una sola petición por la aplicación. Se compone de spans, y cada span representa una unidad de trabajo dentro de la petición, por ejemplo el procesamiento de una petición HTTP o una consulta a la base de datos. Un span tiene nombre, hora de inicio y de fin, atributos e identificadores. Todos los spans de una misma traza llevan un identificador de traza común, y un span anidado indica el identificador de su padre. Precisamente por estos vínculos Jaeger dibuja después el árbol de llamadas que veremos en el paso 5. La métrica es una medición tomada durante el funcionamiento, por ejemplo la memoria ocupada o el número de peticiones. A diferencia de la traza, describe el comportamiento del sistema en conjunto y no de una sola petición, por eso encaja bien en gráficos y alertas. El registro (log) es una anotación sobre un evento. Para no obligarte a abandonar las bibliotecas de registro habituales, OpenTelemetry se conecta a ellas, y cuando en la aplicación está activado el SDK o la instrumentación automática, a las entradas del registro se añaden por sí solos los identificadores de la traza y del span activos. Gracias a eso puedes vincular una línea del registro con la petición durante la cual apareció.
Veamos ahora cómo salen los datos de la aplicación. En el propio código se usa la API, que define cómo crear la telemetría, y de su implementación en cada lenguaje se encarga el SDK. El SDK recoge los spans y las métricas creados, los procesa y los exporta. No hace falta escribir la trazabilidad a mano para cada framework, porque existen bibliotecas de instrumentación que crean por sí solas los spans de las peticiones de las bibliotecas populares, y en los lenguajes que lo admiten se conectan sin tocar el código en absoluto. Así haremos con la aplicación en Flask. El SDK envía los datos mediante el protocolo OTLP, que tiene dos variantes de transmisión. OTLP/gRPC usa por defecto el puerto 4317 y OTLP/HTTP, el puerto 4318, y por HTTP los datos van a las direcciones /v1/traces, /v1/metrics y /v1/logs. Son dos protocolos distintos en dos puertos distintos, y confundirlos es muy fácil. En la sección de preguntas frecuentes hemos reproducido los errores que aparecen en ese caso.
La aplicación se puede configurar para que envíe los datos directamente al almacén, y para familiarizarse con OpenTelemetry o para un banco de pruebas pequeño es admisible. Sin embargo, en la mayoría de los casos junto a los servicios se ejecuta un Collector, un programa intermediario aparte que recibe la telemetría, la procesa y la reenvía. Así es más fácil para la aplicación, porque entrega los datos rápido y se olvida de ellos, mientras que los reintentos de envío, la agrupación en lotes, el cifrado y el filtrado de datos sensibles los asume Collector. Por dentro está organizado como un conjunto de canalizaciones, una para cada señal. La canalización empieza con los receptores (receivers), que reciben los datos, por ejemplo por OTLP o de las métricas del servidor. Después vienen los procesadores (processors), que modifican los datos por el camino, por ejemplo los agrupan en lotes. La canalización termina con los exportadores (exporters), que envían el resultado a uno o varios almacenes. Hay un detalle con el que tropiezan los principiantes. No basta con describir un componente en su sección de la configuración, además hay que enumerarlo en la sección service, o no se activará. Lo veremos en la configuración del paso 2. Collector se publica en varias compilaciones, y nosotros usaremos contrib, que tiene muchos más receptores que la básica. En las imágenes de la versión 0.163.1 hay 108 receptores frente a 7.
El papel de Jaeger en el esquema
Jaeger es un sistema abierto para guardar y ver trazas. En nuestro esquema hace de almacén e interfaz web, y no forma parte del propio OpenTelemetry. Jaeger 2.22.0 recibe datos por OTLP, en su registro se ven las líneas Starting GRPC server y Starting HTTP server, así que Collector le pasará las trazas con el mismo protocolo.
Qué necesitas
- Un servidor con Debian 12 y al menos 2 GB de memoria, por ejemplo el plan DE-RZ9-1. En nuestro servidor, Collector ocupó de 45 a 70 MB de memoria, Jaeger de 20 a 60 MB y la aplicación de demostración en Flask, unos 120 MB. Las imágenes de Docker ocuparon en el disco unos 750 MB, y el entorno virtual de Python, otros 67 MB. Si todavía no tienes servidor, contrátalo siguiendo la guía.
- Conexión por SSH, por ejemplo con Termius. Los comandos se ejecutan como
root. - Docker y Docker Compose. Cómo instalarlos se cuenta en un artículo aparte. En nuestro caso estaban instalados Docker 29.9.2 y Compose 5.6.1.
- Python 3 para la aplicación de demostración. En Debian 12 ya viene incluido, y nuestra versión era la 3.11.2.
- Un terminal y un navegador en tu ordenador, para abrir Jaeger a través de un túnel SSH.
Paso 1. Prepara la carpeta y el archivo Compose
Crea la carpeta del proyecto y entra en ella.
mkdir -p /opt/otel
cd /opt/otelAhora crea el archivo compose.yaml. Copia el bloque de abajo completo, con la línea cat al principio y EOF al final, y pégalo en la ventana del terminal.
cat > compose.yaml <<'EOF'
# Jaeger guarda y muestra las trazas, Collector recibe la telemetría
services:
jaeger:
image: cr.jaegertracing.io/jaegertracing/jaeger:2.22.0
container_name: otel-jaeger
restart: unless-stopped
ports:
- "127.0.0.1:16686:16686" # interfaz web de Jaeger, solo desde el propio servidor
collector:
image: otel/opentelemetry-collector-contrib:0.163.1
container_name: otel-collector
restart: unless-stopped
volumes:
- ./otel-config.yaml:/etc/otelcol-contrib/config.yaml:ro
ports:
- "127.0.0.1:4317:4317" # OTLP gRPC
- "127.0.0.1:4318:4318" # OTLP HTTP
depends_on:
- jaeger
EOFEn el archivo se describen dos contenedores.
- jaeger guarda las trazas y las muestra en la interfaz web. Su puerto 16686 está publicado solo en la dirección interna del servidor
127.0.0.1. - collector recibe la telemetría. Sus puertos 4317 (OTLP/gRPC) y 4318 (OTLP/HTTP) también están publicados solo en
127.0.0.1, así que las aplicaciones del propio servidor llegarán hasta él, pero desde internet no es accesible.
El archivo de ajustes de Collector se monta dentro del contenedor solo en modo lectura, y lo crearemos en el paso siguiente. Las versiones de las imágenes están indicadas de forma explícita para que, después de reiniciar, obtengas la misma versión y no una nueva.
Por qué los puertos solo en 127.0.0.1
El Collector de este artículo no tiene contraseñas, y recibe datos de cualquiera que consiga conectarse al puerto. La documentación oficial de OpenTelemetry aconseja publicar el puerto de Collector en Docker solo en 127.0.0.1 si lo usan programas del mismo servidor. Además, el cortafuegos UFW no cierra por sí solo los puertos de Docker, de eso se habla en detalle en el artículo sobre Docker. Hemos comprobado desde un ordenador doméstico que las conexiones a los puertos 4317, 4318 y 16686 en la dirección externa del servidor no se establecen y terminan por tiempo de espera agotado.
Paso 2. Escribe la configuración de Collector
Crea el archivo otel-config.yaml del mismo modo, copiando el bloque completo.
cat > otel-config.yaml <<'EOF'
receivers:
# Recepción de telemetría de las aplicaciones mediante el protocolo OTLP
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
# Agrupa los datos en lotes, así el envío es más eficiente
batch:
exporters:
# Las trazas van a Jaeger
otlp_grpc/jaeger:
endpoint: jaeger:4317
tls:
insecure: true
# Todo lo que recibe Collector se imprime en su registro
debug:
verbosity: basic
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlp_grpc/jaeger, debug]
metrics:
receivers: [otlp]
processors: [batch]
exporters: [debug]
logs:
receivers: [otlp]
processors: [batch]
exporters: [debug]
EOFRepasemos el archivo por secciones.
- receivers describe los receptores. El receptor
otlpespera datos en los puertos 4317 (gRPC) y 4318 (HTTP). La dirección0.0.0.0significa que la recepción se hace en todas las direcciones dentro del contenedor, y el acceso desde fuera queda limitado por cómo hemos publicado los puertos encompose.yaml. - processors describe los procesadores. El procesador
batchagrupa los datos en lotes antes de enviarlos. - exporters describe los exportadores. El nombre
otlp_grpc/jaegerse compone del tipo y de un nombre propio después de la barra, así se pueden tener varios exportadores del mismo tipo. La direcciónjaeger:4317es el nombre del servicio decompose.yaml, y Docker encuentra por sí solo el contenedor con ese nombre. La líneainsecure: truedesactiva el cifrado, porque el intercambio se hace dentro de la red de Docker en un mismo servidor. El exportadordebugimprime todo lo recibido en el registro de Collector, y el modobasicmuestra una línea por cada lote de datos. - service reúne las canalizaciones. Para cada señal se indica de dónde tomar los datos, cómo procesarlos y adónde enviarlos. Las trazas van tanto a Jaeger como al registro, y las métricas y los registros, por ahora, solo al registro.
No quites la dirección 0.0.0.0
Si en el receptor no indicas endpoint, Collector escucha solo en la dirección interna del contenedor 127.0.0.1, y los puertos publicados dejan de funcionar. Lo hemos comprobado en el servidor de pruebas, los detalles están en la sección de preguntas frecuentes.
Nombres de componentes en guías antiguas
En la versión 0.163.1 el exportador de OTLP por gRPC se llama otlp_grpc, y el receptor de métricas del servidor, host_metrics. En las guías antiguas se encuentran los nombres otlp y hostmetrics. La comprobación de la configuración también los acepta, pero nosotros usamos los nombres nuevos.
Paso 3. Comprueba la configuración e inicia
Antes de iniciar Collector, comprueba el archivo de ajustes. El comando de abajo ejecuta un contenedor de Collector de un solo uso con tu archivo y simplemente informa de si la configuración es correcta.
docker compose run --rm --no-deps collector validate --config=/etc/otelcol-contrib/config.yaml
echo $?Si todo está en orden, el comando no imprimirá nada y echo $? mostrará 0. Si hay un error en el archivo, verás su texto, y hay ejemplos en la sección de preguntas frecuentes. Ahora inicia los contenedores.
docker compose up -dEn el primer arranque, Docker descargará primero las imágenes. Pasados unos segundos, mira la última línea del registro de Collector.
docker logs otel-collector 2>&1 | tail -n 1Debe aparecer la línea Everything is ready. Begin running and processing data., que significa que los receptores están en marcha.

La configuración es correcta, Collector está en marcha y listo para recibir datos
Las líneas del registro de Collector son largas, porque al final de cada una hay un bloque de servicio con la descripción del propio Collector. Lo que nos importa es el principio de la línea y los números del final.
Paso 4. Envía trazas de prueba
Para comprobar Collector antes de conectar una aplicación real, usaremos la herramienta telemetrygen. Forma parte del proyecto OpenTelemetry y sabe generar trazas, métricas y registros de prueba. La ejecutaremos en un contenedor en la red del servidor y enviaremos tres trazas.
docker run --rm --network host \
ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:latest \
traces --otlp-insecure --traces 3 2>&1 | grep -E "generated|export:"--network hostda al contenedor la red del propio servidor, por eso la herramienta ve el puerto127.0.0.1:4317.--otlp-insecuredesactiva el cifrado. Nuestro Collector no lo tiene, y sin este parámetro la herramienta intentará conectarse por TLS y recibirá un error.--traces 3fija el número de trazas. La herramienta crea una traza por segundo, así que tres trazas tardan unos 5 segundos en enviarse.- El filtro
grepdeja solo la línea final y las líneas con errores de envío. La herramienta es muy habladora.
La línea traces generated solo significa que la herramienta ha creado los datos. Si han llegado hasta Collector, lo mostrará su registro.
docker logs otel-collector 2>&1 | grep Traces | tail -n 2
Collector ha recibido las trazas y las ha escrito en el registro
En cada línea Traces aparece "spans": 2. La herramienta crea una traza de dos spans, y Collector recibe las trazas a medida que aparecen e imprime una línea por cada lote.
La misma prueba se puede hacer también por HTTP. Para ello, añade el parámetro --otlp-http e indica el puerto 4318.
docker run --rm --network host \
ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:latest \
traces --otlp-http --otlp-insecure --otlp-endpoint localhost:4318 --service telemetrygen-http --traces 2El parámetro --service fija el nombre del servicio con el que las trazas aparecerán en Jaeger, así es fácil distinguirlas de las primeras.
Paso 5. Abre Jaeger a través de un túnel SSH
La interfaz web de Jaeger está cerrada a internet, por eso hay que conectarse a ella mediante un túnel SSH.
En tu ordenador, abre una ventana nueva del terminal y crea el túnel. Sustituye
IP-del-servidorpor la dirección de tu VPS.bashssh -L 16686:127.0.0.1:16686 root@IP-del-servidorSin cerrar esa ventana, abre en el navegador de tu ordenador la dirección
http://127.0.0.1:16686.En la lista Service elige
telemetrygeny pulsa Find Traces.
Se abrirá una página con las trazas encontradas. Hemos enviado tres trazas y las tres se ven en la tabla. Cada una tiene dos spans, y la duración es de unos 123 microsegundos, porque telemetrygen crea por defecto spans muy cortos.

Trazas de prueba de telemetrygen en Jaeger
Haz clic en el nombre de cualquier traza y se abrirá su línea de tiempo con los spans dentro.
Jaeger guarda las trazas solo en memoria
En el modo de nuestro archivo Compose, Jaeger no escribe los datos en el disco. Según su documentación, es un almacenamiento temporal en memoria. Lo hemos comprobado reiniciando el contenedor, y después las trazas anteriores desaparecieron. El almacenamiento permanente de trazas no se configura en este artículo, para familiarizarse y depurar basta con la memoria.
Paso 6. Conecta una aplicación en Python
Los datos de prueba sirven para comprobar, pero la utilidad real empieza cuando es tu aplicación la que envía la telemetría. Tomaremos una pequeña aplicación web en Flask y le conectaremos OpenTelemetry sin cambios en la lógica. Como base hemos usado el ejemplo de la guía oficial de OpenTelemetry para Python, al que hemos añadido un span propio y una pequeña pausa para que la traza resulte más clara.
Instala el paquete para entornos virtuales de Python. En Debian 12 no viene incluido en la instalación estándar.
bashapt install -y python3-venvCrea la carpeta de la aplicación y el entorno virtual, y después actívalo.
bashmkdir -p /opt/otel/app cd /opt/otel/app python3 -m venv venv source venv/bin/activateAl principio de la línea del terminal aparecerá la marca
(venv).Instala Flask, OpenTelemetry y el exportador de OTLP. Después ejecuta
opentelemetry-bootstrap, que encuentra en el entorno las bibliotecas ya instaladas, por ejemplo Flask, y les instala los paquetes de instrumentación.bashpip install flask opentelemetry-distro opentelemetry-exporter-otlp opentelemetry-bootstrap -a installEn nuestro servidor se instalaron Flask 3.1.3, OpenTelemetry SDK 1.45.1 y opentelemetry-distro 0.66b1.
Crea el archivo de la aplicación
app.py.bashcat > app.py <<'EOF' import logging import time from random import randint from flask import Flask, request from opentelemetry import trace app = Flask(__name__) logger = logging.getLogger(__name__) tracer = trace.get_tracer("diceroller.tracer") @app.route("/rolldice") def roll_dice(): player = request.args.get("player", default=None, type=str) result = str(roll()) if player: logger.warning("%s is rolling the dice: %s", player, result) else: logger.warning("Anonymous player is rolling the dice: %s", result) return result def roll(): # Tramo propio de la traza dentro de la petición with tracer.start_as_current_span("roll") as span: time.sleep(0.3) # simulación de trabajo value = randint(1, 6) span.set_attribute("roll.value", value) return value EOFLa aplicación responde en la dirección
/rolldicecon un número aleatorio del 1 al 6. La única referencia a OpenTelemetry que contiene es el spanroll, que hemos creado nosotros para ver un tramo anidado. Todo lo demás lo hará la instrumentación automática.Inicia la aplicación mediante
opentelemetry-instrument.bashopentelemetry-instrument --service_name dice-server --logs_exporter otlp flask run -p 8000--service_name dice-serverfija el nombre del servicio en Jaeger.--logs_exporter otlpactiva el envío de registros. Las trazas y las métricas, según la documentación, van por OTLP/gRPC alocalhost:4317incluso sin este parámetro.flask run -p 8000es el comando habitual para iniciar la aplicación. Si el puerto 8000 lo tienes ocupado, elige otro.
La ventana quedará casi vacía, y es lo normal. Las entradas del registro de la aplicación ahora las recoge OpenTelemetry y las envía a Collector, y en el terminal no se muestran.
Abre una segunda ventana de SSH y haz varias peticiones a la aplicación.
bashfor p in Anna Boris Clara; do curl "http://127.0.0.1:8000/rolldice?player=$p"; echo; doneCada respuesta se retrasa aproximadamente un tercio de segundo por la pausa del código. Pasados unos segundos, comprueba el registro de Collector.
bashdocker logs otel-collector 2>&1 | grep -E "Traces|Logs" | tail -n 2

Tres peticiones dieron seis spans y entradas del registro
Las tres peticiones dieron seis spans, es decir, dos por cada una, y las entradas del registro de la aplicación. Collector ha recibido las tres señales.
Ahora vuelve a Jaeger, actualiza la página, en la lista Service elige dice-server y pulsa Find Traces. Abre cualquier traza GET /rolldice y haz clic en el span anidado roll.

La petición y el span anidado roll, abajo están sus atributos
La barra superior es toda la petición GET /rolldice, la ha creado la biblioteca de instrumentación de Flask. Debajo está el span anidado roll, que hemos creado a mano. Ocupa casi todo el tiempo de la petición, unos 300 de 301 milisegundos, porque es justo ahí donde está nuestra pausa. En los atributos del span se ve roll.value con el número obtenido. Así, en la traza se ve enseguida en qué parte de la petición se va el tiempo.
Detén la aplicación con la combinación Ctrl + C en la primera ventana.
Envío por HTTP en lugar de gRPC
La aplicación se puede cambiar a OTLP/HTTP con una variable de entorno. Añade antes del comando de inicio OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf, y los datos irán al puerto 4318. Si necesitas otra dirección de Collector, la fija la variable OTEL_EXPORTER_OTLP_ENDPOINT. Hemos probado las dos formas, y con HTTP el servicio apareció en Jaeger igual que con gRPC.
Paso 7. Añade las métricas del servidor
Por ahora Collector recibe datos de las aplicaciones, pero él mismo no mide nada. Vamos a enseñarle a recoger las métricas del servidor con el receptor host_metrics. Sabe tomar la carga del procesador, la memoria, los discos y la red.
Vuelve a la carpeta del proyecto.
cd /opt/otelAbre el archivo de ajustes de Collector.
bashnano otel-config.yamlGuarda los cambios con la combinación Ctrl + O, luego Enter, y sal del editor con la combinación Ctrl + X.
Añade en la sección
receiversun receptor nuevo justo después deotlp, con la misma sangría de dos espacios.yaml# Métricas del propio servidor host_metrics: root_path: /hostfs collection_interval: 10s scrapers: cpu: memory: load: filesystem: network:scrapersenumera qué se va a medir exactamente. Hemos tomado el procesador, la memoria, la carga media, el sistema de archivos y la red. En la documentación del receptor hay otros, por ejemplo discos y procesos.collection_intervalfija la frecuencia de las mediciones. Por defecto es de un minuto, y nosotros hemos puesto 10 segundos para ver antes el resultado.root_pathhace falta porque Collector funciona en un contenedor. Para que vea el servidor y no su propio contenedor, la raíz del servidor se monta en la carpeta/hostfs, y enroot_pathse indica esa carpeta.
En el mismo archivo, añade el nuevo receptor a la canalización de métricas.
yamlmetrics: receivers: [otlp, host_metrics]Cambia el modo del exportador
debugpara que en el registro aparezcan no solo los contadores, sino también los valores. Sustituye en la secciónexportersla líneaverbosity: basicporverbosity: detailedy guarda el archivo.Abre el archivo
compose.yamly monta la raíz del servidor en modo lectura en la secciónvolumesdel serviciocollector.bashnano compose.yamlyamlvolumes: - ./otel-config.yaml:/etc/otelcol-contrib/config.yaml:ro - /:/hostfs:ro # sistema de archivos del servidor para las métricas del hostComprueba la configuración y vuelve a crear el contenedor de Collector.
bashdocker compose run --rm --no-deps collector validate --config=/etc/otelcol-contrib/config.yaml docker compose up -d
Espera unos 20 segundos a que Collector haga las primeras mediciones y mira qué métricas ha recogido.
docker logs otel-collector --since 12s 2>&1 | grep "Name: system" | sort -uPara asegurarnos de que las cifras son reales, tomaremos del registro el valor de la memoria ocupada y lo compararemos con el comando free.
docker logs otel-collector --since 12s 2>&1 | grep -A12 system.memory.usage | grep -E "state|Value" | head -2
free -b | head -2
Collector recoge las métricas del servidor, y la memoria ocupada coincide con la salida de free
Collector ha mostrado 13 métricas con nombres que empiezan por system.. Por ejemplo, system.cpu.load_average.1m es la carga media del último minuto, system.memory.usage es la memoria en bytes por estados, system.filesystem.usage es el espacio ocupado en los discos y system.network.io es el volumen de datos transmitidos. La memoria ocupada de la métrica se acerca a la columna used de la salida de free, y la pequeña diferencia se debe a que los valores se tomaron en momentos distintos.
Vuelve al modo basic
El modo detallado escribe en el registro unos 90 KB cada minuto, es decir, unos 130 MB al día. Cuando hayas mirado lo suficiente, devuelve en otel-config.yaml la línea verbosity: basic y reinicia Collector con el comando docker compose restart collector.
Por ahora las métricas solo llegan al registro. Para guardarlas y dibujar gráficos hay que enviarlas a un almacén de métricas, y Collector tiene para ello exportadores, por ejemplo prometheus. Es tema de un artículo aparte.
Comandos útiles
docker compose ps # estado de los contenedores y puertos
docker compose logs -f collector # registro de Collector en tiempo real
docker compose restart collector # volver a leer el archivo de ajustes
docker compose run --rm --no-deps collector validate --config=/etc/otelcol-contrib/config.yaml # comprobar los ajustes
docker compose down # detener y eliminar los contenedoresDespués de detenerlo con el comando docker compose down, las trazas de Jaeger desaparecerán, ya que se guardan solo en memoria.
Preguntas frecuentes
¿Hay que abrir los puertos 4317, 4318 y 16686 en UFW?
No. Las aplicaciones de este mismo servidor se conectan a 127.0.0.1, y Jaeger lo abrimos a través de un túnel SSH. Los puertos en Compose están publicados solo en 127.0.0.1, y hemos comprobado desde otro ordenador que en la dirección externa del servidor la conexión con ellos no se establece. No sustituyas 127.0.0.1:4317:4317 por 4317:4317 sin necesidad. El cortafuegos UFW no cierra los puertos de Docker, y Collector sin contraseñas quedaría abierto a todo internet.
La aplicación funciona, pero no hay nada en Collector ni en Jaeger, y en el registro aparece Failed to auto initialize OpenTelemetry
No está instalado el exportador de OTLP. Ejecutamos la aplicación solo con opentelemetry-distro y obtuvimos estas líneas.
RuntimeError: Requested component 'otlp_proto_grpc' not found in entry point 'opentelemetry_traces_exporter'
Failed to auto initialize OpenTelemetryLa propia aplicación sigue respondiendo a las peticiones, por eso el error pasa fácilmente desapercibido. Detén la aplicación, ejecuta pip install opentelemetry-exporter-otlp e inicia de nuevo.
Jaeger está vacío y en el terminal de la aplicación no hay ningún error
Los errores de envío no se muestran en el terminal, porque las entradas del registro, incluidos los mensajes del propio OpenTelemetry, las recoge la instrumentación automática. Hemos comprobado que con el parámetro --logs_exporter console esas mismas entradas, junto con los errores de exportación, aparecen en la pantalla. Inicia la aplicación así.
opentelemetry-instrument --service_name dice-server --logs_exporter console flask run -p 8000En la consola empezarán a mostrarse tanto las entradas de la propia aplicación como los mensajes de errores de exportación, de los que se ven ejemplos en las dos preguntas siguientes.
Failed to export traces to localhost:4318 … Trying to connect an http1.x server
La aplicación envía los datos por gRPC, pero la dirección apunta al puerto HTTP. Reprodujimos el error iniciando la aplicación con la dirección de Collector http://localhost:4318 mediante la variable OTEL_EXPORTER_OTLP_ENDPOINT.
Failed to export traces to localhost:4318, error code: StatusCode.UNAVAILABLE, error details: failed to connect to all addresses; last error: INTERNAL: ipv4:127.0.0.1:4318: Failed parsing HTTP/2 (Expected SETTINGS frame as the first frame, got frame type 80) (Trying to connect an http1.x server (HTTP status 400))Hay dos salidas. O devuelves el puerto de gRPC 4317, o cambias la aplicación a HTTP con la variable OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf. En la herramienta telemetrygen, el mismo error se ve como http2: frame too large, note that the frame header looked like an HTTP/1.1 header.
BadStatusLine … encountered while exporting spans batch, malformed HTTP response
Es la situación inversa. La aplicación o la herramienta envían los datos por HTTP, pero la dirección apunta al puerto de gRPC. Iniciamos la aplicación con las variables OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf y OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317.
Transient error ('Connection aborted.', BadStatusLine('\x00\x00\x06\x04\x00\x00\x00\x00\x00\x00\x05\x00\x00@\x00')) encountered while exporting spans batch, retrying in 0.82s.
Failed to export spans batch due to timeout, max retries or shutdown.Recuerda la correspondencia. El puerto 4317 acepta solo gRPC y el puerto 4318, solo HTTP. Para HTTP no olvides tampoco el --otlp-http de telemetrygen.
transport: Error while dialing … connect: connection refused
Collector no está en marcha o la dirección está mal indicada. Obtuvimos este error al enviar trazas al puerto 4319, en el que no escucha nadie.
traces export: exporter export timeout: rpc error: code = Unavailable desc = connection error: desc = "transport: Error while dialing: dial tcp 127.0.0.1:4319: connect: connection refused"Comprueba con el comando docker compose ps que el contenedor funciona, y que los puertos están a la escucha en el servidor.
ss -tln | grep -E ":(4317|4318|16686) "En la salida deben aparecer tres líneas con la dirección 127.0.0.1.
tls: first record does not look like a TLS handshake
La herramienta telemetrygen se conecta por defecto mediante TLS, y nuestro Collector recibe los datos sin cifrado. Obtuvimos este error al ejecutar la herramienta sin el parámetro --otlp-insecure.
transport: authentication handshake failed: tls: first record does not look like a TLS handshakeAñade el parámetro --otlp-insecure.
error reading server preface: connection reset by peer
El puerto está publicado, pero Collector dentro del contenedor escucha en una dirección que no es la correcta. Pasa si en el receptor otlp no está indicado endpoint. Comprobamos la configuración con grpc: y http: vacíos, y en el registro de Collector aparecieron estas direcciones.
"endpoint": "127.0.0.1:4317"
"endpoint": "127.0.0.1:4318"En ese caso, telemetrygen responde con un error.
traces export: exporter export timeout: rpc error: code = Unavailable desc = connection error: desc = "error reading server preface: connection reset by peer"Indica en la configuración endpoint: 0.0.0.0:4317 para gRPC y endpoint: 0.0.0.0:4318 para HTTP, como en el paso 2, y reinicia Collector.
references exporter "jaeger" which is not configured
En la canalización se indica un componente que no existe en su sección de la configuración. Obtuvimos este error al escribir en la canalización de trazas jaeger en lugar de otlp_grpc/jaeger.
Error: service::pipelines::traces: references exporter "jaeger" which is not configuredEl nombre en la sección service debe coincidir con el nombre en exporters, incluida la parte después de la barra. Collector también se queja del mismo modo de los receptores y procesadores sin describir.
receivers::host_metrics: invalid root_path: stat /hostfs: no such file or directory
En compose.yaml no está montada la raíz del servidor en la carpeta /hostfs, y en los ajustes del receptor host_metrics esa carpeta sí está indicada.
Error: receivers::host_metrics: invalid root_path: stat /hostfs: no such file or directoryAñade en la sección volumes la línea - /:/hostfs:ro, como en el paso 7.
Nuestros recursos
Canal de Telegram: https://t.me/f3cloud_news
Web del hosting: https://f3cloud.com