TL;DR — Resumen Rápido
OpenTelemetry unifica trazas, métricas y logs bajo un estándar neutral. Domina el Collector de OTel, la instrumentación automática y los backends con Grafana.
OpenTelemetry (OTel) es el estándar CNCF de código abierto que unifica la recolección de trazas, métricas y logs bajo una API, SDK y protocolo de red neutral respecto al proveedor. Antes de OTel, cada proveedor de observabilidad — Datadog, New Relic, Dynatrace, Elastic — requería su propio agente propietario, obligando a los equipos a instrumentar el código múltiples veces y creando una dependencia difícil de romper. OTel elimina esa dependencia: instrumenta una vez, envía a cualquier backend. Esta guía cubre la arquitectura completa de OTel, la configuración del Collector, la instrumentación automática y manual, el rastreo distribuido, los patrones de despliegue en Kubernetes y un ejemplo práctico de microservicios Node.js + Python con Grafana.
Requisitos Previos
- Familiaridad con Docker y Kubernetes básico (para las secciones de Kubernetes)
- Una aplicación objetivo en Node.js, Python, Java, .NET o Go
- Docker Compose o un clúster de Kubernetes para el ejemplo práctico
- Comprensión básica de qué son las trazas, métricas y logs conceptualmente
kubectlconfigurado si usas las secciones del operador de Kubernetes
Arquitectura de OpenTelemetry
OTel se organiza en tres capas claramente separadas:
API — Interfaces específicas del lenguaje (p. ej., tracer.startSpan()). Esto es lo que el código de tu aplicación llama. La API sola no hace nada; necesita una implementación SDK inyectada en tiempo de ejecución, lo que permite a los autores de bibliotecas instrumentar código sin imponer una versión específica de OTel.
SDK — La implementación de la API. El SDK gestiona las decisiones de muestreo, la detección de recursos (nombre del host, nombre del pod de k8s, región cloud) y la exportación de telemetría a un backend via OTLP. Se configura con variables de entorno o programáticamente.
Collector — Un binario independiente (o contenedor Docker) que actúa como pipeline de telemetría: recibe de las apps → procesa → exporta a backends. Las aplicaciones envían OTLP al Collector; éste distribuye a múltiples backends.
OTLP (OpenTelemetry Line Protocol) es el formato de red canónico: protobuf sobre gRPC (puerto 4317) o HTTP/JSON (puerto 4318). Casi todos los backends de observabilidad modernos hablan OTLP de forma nativa.
El Collector de OpenTelemetry
El Collector es el componente más poderoso en un despliegue OTel de producción. Funciona como un pipeline con tres etapas:
Receptores
receivers:
otlp:
protocols:
grpc:
endpoint: "0.0.0.0:4317"
http:
endpoint: "0.0.0.0:4318"
prometheus:
config:
scrape_configs:
- job_name: "mi-app"
static_configs:
- targets: ["app:8080"]
filelog:
include: ["/var/log/app/*.log"]
operators:
- type: json_parser
hostmetrics:
collection_interval: 30s
scrapers:
cpu: {}
memory: {}
disk: {}
network: {}
Procesadores
processors:
memory_limiter:
check_interval: 1s
limit_mib: 400
batch:
send_batch_size: 10000
timeout: 10s
attributes:
actions:
- key: environment
value: production
action: insert
filter:
traces:
span:
- 'attributes["http.target"] == "/health"'
tail_sampling:
decision_wait: 10s
policies:
- name: politica-errores
type: status_code
status_code: {status_codes: [ERROR]}
- name: politica-trazas-lentas
type: latency
latency: {threshold_ms: 1000}
- name: politica-probabilistica
type: probabilistic
probabilistic: {sampling_percentage: 10}
tail_sampling se ejecuta en el Collector y toma decisiones después de ver la traza completa, capturando todos los errores mientras muestrea solo una fracción de las trazas saludables y rápidas.
Exportadores y Pipelines
exporters:
otlp/tempo:
endpoint: "tempo:4317"
tls:
insecure: true
prometheus:
endpoint: "0.0.0.0:9464"
loki:
endpoint: "http://loki:3100/loki/api/v1/push"
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, batch, tail_sampling]
exporters: [otlp/tempo]
metrics:
receivers: [otlp, prometheus, hostmetrics]
processors: [memory_limiter, batch, attributes]
exporters: [prometheus]
logs:
receivers: [otlp, filelog]
processors: [memory_limiter, batch]
exporters: [loki]
Auto-Instrumentación
La auto-instrumentación añade OTel sin modificar el código de la aplicación, interceptando llamadas HTTP, de base de datos y de mensajería automáticamente.
Java — Ejecuta el agente Java de OTel como flag de la JVM:
java -javaagent:/otel-javaagent.jar \
-Dotel.service.name=mi-servicio \
-Dotel.exporter.otlp.endpoint=http://collector:4317 \
-jar mi-app.jar
Python — Usa opentelemetry-instrument:
pip install opentelemetry-distro opentelemetry-exporter-otlp
opentelemetry-bootstrap --action=install
opentelemetry-instrument \
--service_name mi-servicio \
--exporter_otlp_endpoint http://collector:4317 \
python app.py
Node.js — Importa el SDK antes que cualquier código de la aplicación:
// tracing.js — debe cargarse primero con --require
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-grpc');
const sdk = new NodeSDK({
serviceName: 'mi-servicio-node',
traceExporter: new OTLPTraceExporter({ url: 'http://collector:4317' }),
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();
Instrumentación Manual
La auto-instrumentación captura operaciones a nivel de framework. La instrumentación manual añade contexto de negocio.
Creación de Spans
const opentelemetry = require('@opentelemetry/api');
const tracer = opentelemetry.trace.getTracer('mi-servicio', '1.0.0');
async function procesarPedido(pedidoId) {
return tracer.startActiveSpan('procesarPedido', async (span) => {
try {
span.setAttribute('pedido.id', pedidoId);
span.setAttribute('pedido.region', 'us-east-1');
const resultado = await cobrarPago(pedidoId);
span.addEvent('pago.cobrado', { 'pago.monto': resultado.monto });
return resultado;
} catch (err) {
span.recordException(err);
span.setStatus({ code: opentelemetry.SpanStatusCode.ERROR });
throw err;
} finally {
span.end();
}
});
}
Creación de Métricas
const meter = opentelemetry.metrics.getMeter('mi-servicio', '1.0.0');
// Counter — crecimiento monotónico (solicitudes, errores)
const contadorSolicitudes = meter.createCounter('http.server.requests');
// Histogram — distribuciones de latencia, tamaños
const histogramaLatencia = meter.createHistogram('http.server.duration', { unit: 'ms' });
// UpDownCounter — valores que suben y bajan (profundidad de cola)
const conexionesActivas = meter.createUpDownCounter('db.connections.active');
// Gauge observable — lecturas puntuales (CPU, memoria)
meter.createObservableGauge('process.memory.usage').addCallback((result) => {
result.observe(process.memoryUsage().heapUsed);
});
Propagación de Contexto
OTel utiliza los encabezados W3C TraceContext (traceparent, tracestate) para conectar spans entre servicios. La auto-instrumentación maneja la inyección y extracción de encabezados automáticamente.
Conceptos de Rastreo Distribuido
Un trace es un grafo acíclico dirigido de spans que representa una solicitud de extremo a extremo. Cada span tiene: TraceId, SpanId, ParentSpanId, SpanKind (SERVER, CLIENT, PRODUCER, CONSUMER, INTERNAL), atributos, eventos y estado.
Muestreo basado en cabeza — decide en el span raíz, simple pero no puede preferir trazas lentas o con error porque decide antes de que terminen. Muestreo basado en cola (via el procesador tail_sampling del Collector) — recolecta la traza completa primero, luego decide si conservarla capturando el 100% de los errores y solo el 10% del tráfico saludable.
Métricas y Correlación de Logs
Los exemplars vinculan observaciones de histogramas de Prometheus a TraceIds específicos de Tempo, habilitando la navegación con un clic desde un pico de latencia P99 hasta una traza representativa.
La API Log Bridge conecta las bibliotecas de logging existentes (Winston, Pino, logging de Python, Log4j) al SDK de OTel sin reemplazarlas, adjuntando trace_id y span_id a cada línea de log emitida mientras un span está activo.
Patrones de Despliegue en Kubernetes
Modo agente — Un Collector por nodo host (DaemonSet). Las aplicaciones envían OTLP a localhost:4317. Modo gateway — Collector centralizado (Deployment escalado horizontalmente). Todos los agentes reenvían al gateway que ejecuta el tail sampling. Modo sidecar — Un contenedor Collector por Pod, aislamiento completo.
El OTel Operator gestiona Collectors y la inyección de auto-instrumentación vía CRDs de Kubernetes:
apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
name: otel-instrumentacion
spec:
exporter:
endpoint: http://otel-agente:4317
propagators: [tracecontext, baggage]
sampler:
type: parentbased_traceidratio
argument: "0.1"
nodejs:
image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-nodejs:latest
python:
image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-python:latest
Anota un Deployment para inyectar el agente Node.js automáticamente:
annotations:
instrumentation.opentelemetry.io/inject-nodejs: "true"
Backends y Comparativa
| Backend | Señales | Almacenamiento |
|---|---|---|
| Jaeger | Solo trazas | Cassandra/Elasticsearch |
| Prometheus | Solo métricas | TSDB local |
| Grafana Loki | Solo logs | Almacenamiento de objetos |
| Grafana Tempo | Solo trazas | Almacenamiento de objetos |
| SigNoz | Trazas + Métricas + Logs | ClickHouse |
| Plataforma | Lock-in | Modelo de costo |
|---|---|---|
| OpenTelemetry + Grafana | Ninguno | Gratuito (autoalojado) |
| Datadog | Alto | Por host + ingesta |
| New Relic | Medio | Por GB ingerido |
| Dynatrace | Muy alto | Por unidad de host |
| Elastic APM | Medio | Por GB |
Ejemplo Práctico: Node.js + Python con Grafana Stack
Una configuración con dos servicios: un gateway API en Node.js que llama a un servicio de pedidos en Python. Toda la telemetría fluye a través de un único Collector hacia Tempo, Prometheus y Loki, visualizados en Grafana.
services:
collector:
image: otel/opentelemetry-collector-contrib:latest
volumes:
- ./collector-config.yaml:/etc/otel/config.yaml
ports: ["4317:4317", "4318:4318", "9464:9464"]
node-api:
environment:
- OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4317
- OTEL_SERVICE_NAME=node-api
python-pedidos:
environment:
- OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4317
- OTEL_SERVICE_NAME=python-pedidos
El servicio Node.js usa auto-instrumentación via --require ./tracing.js. El servicio Python usa opentelemetry-instrument. En Grafana: abre Explore → Tempo → busca por service.name=node-api → haz clic en una traza → usa el enlace de exemplar para ver la métrica P95 de Prometheus correlacionada → haz clic en el enlace de Loki para ver la línea de log exacta de esa solicitud.
Errores Comunes y Casos Extremos
Deriva de reloj — Las trazas distribuidas dependen de relojes sincronizados. Una deriva NTP mayor a 1ms causa problemas de ordenación de spans. Usa chrony o sincronización de tiempo nativa de la nube.
Tail sampling requiere enrutamiento consistente por TraceId — Si ejecutas múltiples réplicas del Collector gateway y usas tail_sampling, todos los spans de la misma traza deben llegar a la misma instancia. Usa el exportador loadbalancing con routing_key: traceId.
Explosión de cardinalidad — Nunca uses valores de alta cardinalidad (IDs de usuario, IDs de pedido) como valores de etiquetas de métricas. Los atributos OTel en spans son seguros a cualquier cardinalidad; las dimensiones de métricas no.
Orden de inicialización del SDK — En Node.js, el start() del SDK debe completarse antes de que cualquier módulo instrumentado sea requerido (require()). Usa --require para cargar el archivo de trazas.
Resumen
- OpenTelemetry proporciona una API, SDK y protocolo de red (OTLP) únicos y neutrales para trazas, métricas y logs
- El OTel Collector desacopla las aplicaciones de los backends; usa agentes (DaemonSet) que alimentan un gateway (Deployment) con tail sampling en producción
- La auto-instrumentación añade trazas sin cambios de código; la instrumentación manual añade contexto de negocio con
startActiveSpanycreateCounter/createHistogram - Los encabezados W3C TraceContext propagan la identidad de traza entre servicios automáticamente con auto-instrumentación
- Los exemplars vinculan observaciones de métricas de Prometheus a TraceIds de Tempo para drill-down con un clic
- El Grafana Stack (Tempo + Prometheus + Loki + Grafana) es la combinación de backends OTel de código abierto más popular