Dos siglas que llevaban compitiendo sin necesidad, hasta que dejaron de hacerlo
El 21 de mayo de 2026, en la Observability Summit de Minneapolis, la Cloud Native Computing Foundation anunció que OpenTelemetry se convertía en su proyecto «graduado» con la trayectoria de crecimiento más rápida de toda la fundación, solo por detrás de Kubernetes: más de 12.000 colaboradores, más de 2.800 empresas participando, y sus paquetes de JavaScript y Python superando cada uno los 1.300 millones de descargas en los doce meses anteriores. «Graduado» es la categoría más alta de madurez que otorga la CNCF, reservada a proyectos que considera estables, ampliamente adoptados y listos para producción a gran escala.
Siete años antes, en mayo de 2019, OpenTelemetry ni siquiera existía como proyecto único. Sergey Kanzhelev, ingeniero de Microsoft, anunciaba entonces algo mucho más modesto pero que resultaría decisivo: la fusión de dos estándares de telemetría que llevaban un tiempo compitiendo entre sí sin que nadie saliera realmente ganando, OpenCensus (impulsado por Google) y OpenTracing (nacido bajo el paraguas de la propia CNCF). Cada uno tenía sus propias librerías de instrumentación, su propio formato de exportación, su propia comunidad — y cualquiera que quisiera instrumentar una aplicación tenía que apostar por uno de los dos, sabiendo que la mitad del ecosistema seguiría el camino contrario.
Si alguna vez te has sentido perdido ante la sopa de letras de la observabilidad moderna —OTLP, OTel Collector, Alloy, Prometheus, Loki, Tempo, exporters, receivers, y la lista sigue— vale la pena empezar por aquí, porque esa sensación de fragmentación no es nueva ni imaginaria: es, literalmente, el problema que este ecosistema lleva desde 2019 tratando de resolver. Y entender cómo se llegó desde una fusión de dos estándares en pugna hasta convertirse en el segundo proyecto de más rápido crecimiento de toda la computación nativa en la nube ayuda mucho a entender por qué existe una herramienta como Grafana Alloy, y qué problema resuelve de verdad.
El caos de un agente por señal
Antes de hablar de soluciones, merece la pena describir el problema tal como se veía —y como muchos todavía lo ven— en un clúster de Kubernetes real. Durante años, «hacer observabilidad» significó, en la práctica, instalar un agente distinto por cada tipo de señal:
- Para logs, Promtail: un agente ligero de Grafana Labs cuya única función era leer los ficheros de log de cada Pod y enviarlos a Loki, con su propia configuración de scrape y sus propias reglas de reetiquetado.
- Para métricas, algún combinado de Prometheus Node Exporter (métricas del sistema operativo de cada nodo), kube-state-metrics (estado de los objetos de Kubernetes) y, a menudo, el propio Prometheus Agent o un
ServiceMonitorgestionado por el Prometheus Operator. - Para trazas, un Jaeger Agent o un colector específico del backend de trazas elegido, normalmente desplegado como sidecar o como DaemonSet aparte.
Cada uno de estos agentes tenía su propio formato de configuración, su propio ciclo de release, sus propios permisos de RBAC dentro del clúster, y su propio consumo de CPU y memoria — multiplicado por cada nodo del clúster. Si querías correlacionar una traza lenta con las métricas del Pod que la generó y con los logs de ese mismo instante, tenías que hacerlo tú, a mano, cruzando tres sistemas que nunca fueron diseñados para hablar entre sí. Y cualquier cambio de infraestructura —añadir una etiqueta, cambiar un endpoint— significaba tocar tres configuraciones distintas, con tres sintaxis distintas.
RECOLECCIÓN FRAGMENTADA (el modelo "un agente por señal")
══════════════════════════════════════════════════════════
Pod A Pod B Nodo (kubelet/cAdvisor)
│ │ │
│ logs │ logs │ métricas de sistema
▼ ▼ ▼
┌────────────────────┐ ┌──────────────────┐
│ Promtail (x N) │ │ Node Exporter │
│ 1 config, 1 ciclo │ │ (x N nodos) │
│ de release propio │ └─────────┬─────────┘
└──────────┬──────────┘ │
│ ┌─────────┴─────────┐
▼ │ Prometheus Agent │
┌───────┐ │ (otra config, │
│ Loki │ │ otro ciclo) │
└───────┘ └─────────┬──────────┘
▼
App instrumentada ┌──────────┐
│ trazas │Prometheus│
▼ └──────────┘
┌──────────────┐
│ Jaeger Agent │ ← tercera config, tercer formato,
│ (otro más) │ ningún cruce automático entre
└──────┬───────┘ logs, métricas y trazas
▼
┌────────┐
│ Jaeger │
└────────┘
RECOLECCIÓN UNIFICADA (Grafana Alloy / OTel Collector)
══════════════════════════════════════════════════════
Pod A Pod B Nodo (kubelet/cAdvisor)
│ │ │
│ logs │ trazas │ métricas
│ + │ + │
│métricas │ logs │
▼ ▼ ▼
┌───────────────────────────────────┐
│ Grafana Alloy │
│ (1 binario, 1 config declarativa, │
│ componentes: receivers → │
│ processors → exporters) │
└───────┬──────────────┬────────────┘
│ │
▼ ▼
Loki/Tempo cualquier backend
/Prometheus OTLP (Dynatrace,
(o los tres) Grafana Cloud...)
OTLP: el idioma universal que hizo posible dejar de pelearse
La pieza que permite pasar del primer diagrama al segundo no es Grafana Alloy en sí —eso viene después—, es un protocolo: OTLP, el OpenTelemetry Protocol. Conviene ser precisos con qué es exactamente, porque se confunde a menudo con «una librería» o «una herramienta», y no es ninguna de las dos cosas: es una especificación de formato de datos y de transporte, agnóstica de lenguaje de programación y de backend de destino.
OTLP define cómo se estructuran las tres señales de telemetría (métricas, trazas y logs) usando Protocol Buffers como formato de serialización, y cómo se transportan, ya sea sobre gRPC (el transporte por defecto, pensado para streaming eficiente y de baja latencia, típicamente sobre el puerto 4317) o sobre HTTP con el cuerpo codificado en protobuf o en JSON (típicamente sobre el puerto 4318). La analogía que mejor funciona aquí no es la de «un formato de fichero», sino la de un idioma franco: antes de OTLP, cada backend de observabilidad tenía su propio protocolo de ingesta propietario, y cada SDK de instrumentación tenía que aprender a hablar el dialecto de cada backend por separado. Con OTLP, un SDK de aplicación —o un colector— solo necesita aprender a hablar un idioma, y cualquier backend que entienda ese mismo idioma puede recibir los datos, sin acoplamiento directo entre quien genera la telemetría y quien la consume.
Esta neutralidad de proveedor es, de hecho, el objetivo explícito que se marcó el propio proyecto OpenTelemetry desde su fusión en 2019: hacer que la telemetría «robusta y portátil» fuera una característica integrada de cualquier software, de forma que la elección de backend de observabilidad —Grafana Cloud, Dynatrace, Datadog, lo que sea— pudiera cambiarse sin tener que reinstrumentar una sola línea de código de la aplicación. En la Parte II de esta serie vamos a ver, con un caso concreto, hasta qué punto eso es cierto en la práctica y dónde aparecen matices reales.
OpenTelemetry Collector: la pieza que faltaba entre las apps y los backends
Con un idioma común ya resuelto (OTLP), quedaba un problema práctico: alguien tiene que recibir esa telemetría, procesarla (filtrar, enriquecer, transformar, agrupar en lotes) y reenviarla a uno o varios backends, sin que cada aplicación tenga que gestionar directamente esa complejidad. Ese «alguien» es el OpenTelemetry Collector: un componente de infraestructura, normalmente desplegado como proceso o contenedor independiente, cuya única función es recibir, procesar y exportar telemetría.
El Collector se configura mediante un fichero YAML con tres bloques fundamentales que conviene interiorizar porque van a aparecer una y otra vez en todo este ecosistema:
- Receivers (receptores): cómo entra la telemetría — puede ser OTLP (push desde una aplicación instrumentada), o un scraper que va a buscar activamente métricas a un endpoint (por ejemplo, al estilo Prometheus).
- Processors (procesadores): qué se hace con los datos una vez dentro — agruparlos en lotes para no saturar la red, añadir o eliminar atributos, muestrear trazas, filtrar métricas ruidosas.
- Exporters (exportadores): a dónde sale la telemetría ya procesada — que puede ser, de nuevo, OTLP hacia un backend externo, o un formato específico de un backend concreto.
Grafana Alloy: una distribución de OTel Collector, no un competidor
Aquí es donde entra Grafana Alloy, y aquí conviene deshacer un malentendido habitual: Alloy no es una alternativa al OTel Collector, es una distribución de él. Grafana Labs lo describe explícitamente así en su propio anuncio: Alloy es «nuestra nueva distribución open source del OpenTelemetry Collector, 100% compatible con OTLP», que añade además pipelines nativos para el formato Prometheus (algo que el Collector estándar no trae integrado de la misma forma) y componentes propios para Loki y Tempo.
Esto tiene una implicación práctica importante: cualquier componente otelcol.* que exista en la especificación de OpenTelemetry (receptores, procesadores, exportadores) está disponible en Alloy con el mismo comportamiento, así que la migración entre uno y otro —o la convivencia de ambos en la misma infraestructura, algo que Grafana Labs deja explícitamente como opción válida— no exige reescribir la lógica de los pipelines desde cero.
Alloy nace además para sustituir, en un único binario, a tres herramientas que Grafana Labs mantenía por separado: Grafana Agent (el recolector de métricas y trazas, con su modo «Flow» declarativo), Grafana Agent Operator (su integración con Kubernetes) y Promtail (el agente de logs para Loki, cuyo propio equipo de mantenimiento ha señalado públicamente que su ciclo de vida está llegando a su fin en favor de Alloy). En vez de tres binarios, tres configuraciones y tres superficies de RBAC distintas en tu clúster, tienes una sola.
Un lenguaje declarativo, no una lista de YAML
Aquí llega el segundo matiz importante, y es el que más sorprende a quien viene del mundo del OTel Collector «puro»: Alloy no se configura con el YAML estándar de OpenTelemetry, sino con un lenguaje de configuración propio, inspirado en HCL (el lenguaje de Terraform), que originalmente se llamó River cuando nació para el modo Flow de Grafana Agent. Desde que Grafana Agent se convirtió en Alloy, River se trasladó al propio repositorio de Alloy y pasó a llamarse oficialmente sintaxis de configuración de Grafana Alloy — el lenguaje sigue siendo, en esencia, el mismo, con el mismo espíritu de diseño: rápido de evaluar, legible, y sobre todo, depurable en tiempo real.
La diferencia de fondo frente al YAML tradicional del Collector no es solo estética. El YAML del OTel Collector describe una lista estática de componentes y de qué pipeline pertenece cada uno; Alloy, en cambio, modela la configuración como un grafo de componentes vivos, donde cada bloque expone valores de salida (output) que otros bloques pueden referenciar directamente como sus entradas. Eso permite, entre otras cosas, ver en tiempo real —en la propia interfaz web que Alloy expone— qué datos está recibiendo y emitiendo cada componente del pipeline, algo mucho más incómodo de depurar en una configuración YAML puramente declarativa.
Un ejemplo mínimo, para que se entienda la forma antes de entrar en detalle en la Parte II — un receptor OTLP que recibe telemetría y la reenvía, sin procesar nada todavía, a un exportador de depuración que la imprime en consola:
// Recibe telemetría OTLP por gRPC (puerto 4317) y HTTP (puerto 4318)
otelcol.receiver.otlp "entrada" {
grpc {}
http {}
output {
metrics = [otelcol.exporter.debug.consola.input]
logs = [otelcol.exporter.debug.consola.input]
traces = [otelcol.exporter.debug.consola.input]
}
}
// Exportador de depuración: imprime lo que recibe, útil mientras
// se construye un pipeline nuevo antes de apuntar a un backend real
otelcol.exporter.debug "consola" {}
Fíjate en el detalle: otelcol.receiver.otlp no «llama» a otelcol.exporter.debug, sino que declara en su bloque output una lista de destinos, cada uno identificado como <tipo>.<etiqueta>.input. Es una referencia directa dentro del propio lenguaje de configuración, no una cadena de texto que Alloy tiene que interpretar — de ahí la rapidez de evaluación y la facilidad de depuración que menciona la documentación oficial.
El viaje de un dato: de un Pod de Kubernetes hasta la salida del colector
Con las piezas ya sobre la mesa, conviene recorrer el camino completo que hace un dato de telemetría real dentro de un clúster de Kubernetes, porque hay dos caminos distintos que conviene no confundir:
Camino 1 — push, para aplicaciones instrumentadas. Una aplicación que incluye un SDK de OpenTelemetry (en el lenguaje que sea: Java, Go, Python, Node…) genera directamente spans de traza, métricas o logs estructurados, y los envía activamente por OTLP —gRPC o HTTP— hacia un endpoint de Alloy, normalmente desplegado como DaemonSet (una instancia por nodo, así la aplicación habla con el Alloy de su propio nodo sin salir de él) o como servicio central dentro del propio clúster. El bloque otelcol.receiver.otlp visto arriba es exactamente la puerta de entrada para este camino.
Camino 2 — pull, para todo lo que no está instrumentado directamente. No todo en un clúster emite OTLP por sí mismo: las métricas del propio kubelet, de cAdvisor, o de un Service expuesto en formato Prometheus se recogen activamente mediante scraping, usando componentes como discovery.kubernetes (que descubre automáticamente qué Pods y Services existen en el clúster, con qué etiquetas) combinado con prometheus.scrape o otelcol.receiver.prometheus. Este es el «pipeline nativo de Prometheus» que mencionábamos antes como una de las señas de identidad de Alloy frente al Collector estándar.
Ambos caminos convergen en el mismo colector, y a partir de ahí da igual si el dato llegó por push o por pull: pasa por los mismos procesadores (agregación en lotes, filtrado, enriquecimiento con etiquetas del propio Kubernetes que aporta discovery.kubernetes, como el nombre del namespace o el propio Pod) y sale por los mismos exportadores. Esa convergencia —métricas, logs y trazas entrando por caminos distintos pero saliendo por un pipeline unificado y con el mismo vocabulario de componentes— es, en el fondo, la respuesta concreta y de infraestructura al problema abstracto que describíamos al principio: la fragmentación de 2019 resuelta a nivel de protocolo, y ahora también a nivel de agente.
Cierre: ya sabemos cómo entra la telemetría, falta saber cómo sale de verdad
Hasta aquí hemos hablado de cómo se recibe y organiza la telemetría dentro del colector: qué es OTLP, por qué Alloy es una distribución de OTel Collector y no una alternativa, cómo se estructura su lenguaje de configuración, y cómo confluyen los datos que llegan por push y por pull. Es la mitad conceptual del problema.
La mitad práctica —la que de verdad se juega en producción— es la otra: ¿cómo se configura, paso a paso, un pipeline que recoge telemetría de un clúster real y la reenvía de forma segura hacia un backend externo de pago, como Dynatrace? ¿Cómo se gestionan las credenciales sin dejarlas escritas en texto plano en un repositorio Git? ¿Y cómo se evita que una configuración descuidada dispare la factura de ingesta del backend en el primer día? Esa es exactamente la guía práctica de la Parte II.
Serie: De OTel Collector a Grafana Alloy
- Parte I (este artículo): qué es OTLP, por qué Grafana Alloy es una distribución de OpenTelemetry Collector, y cómo viaja un dato de telemetría desde un Pod hasta la salida del colector.
- Parte II: Pipelines prácticos y exportación OTLP hacia backends Enterprise — anatomía de un pipeline real en Alloy, autenticación segura, filtrado de ruido y buenas prácticas de producción con Dynatrace como caso de estudio.
Fuentes:
- Cloud Native Computing Foundation Announces OpenTelemetry’s Graduation
- Announcing OpenTelemetry: the merger of OpenCensus and OpenTracing — Microsoft Open Source Blog
- Introducing an OpenTelemetry Collector distribution with built-in Prometheus pipelines: Grafana Alloy
- From Agent to Alloy: Why we transitioned to the Alloy collector — Grafana Labs
- river/README.md — grafana/river en GitHub
- otelcol.receiver.otlp — Grafana Alloy documentation