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
  • kubectl configurado 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

BackendSeñalesAlmacenamiento
JaegerSolo trazasCassandra/Elasticsearch
PrometheusSolo métricasTSDB local
Grafana LokiSolo logsAlmacenamiento de objetos
Grafana TempoSolo trazasAlmacenamiento de objetos
SigNozTrazas + Métricas + LogsClickHouse
PlataformaLock-inModelo de costo
OpenTelemetry + GrafanaNingunoGratuito (autoalojado)
DatadogAltoPor host + ingesta
New RelicMedioPor GB ingerido
DynatraceMuy altoPor unidad de host
Elastic APMMedioPor 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 startActiveSpan y createCounter/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

Artículos Relacionados