Guía Completa de Monitoreo con opentelemetry
La observabilidad se ha convertido en un pilar fundamental para operar sistemas distribuidos de forma confiable. Sin embargo, durante años, cada herramienta de monitoreo impuso su propio formato de instrumentación, creando silos de datos y dependencia de proveedores. OpenTelemetry surge como la respuesta a este problema: un estandar abierto, respaldado por la CNCF, que unifica la recolección de trazas, métricas y logs bajo un mismo framework. En esta guía cubrimos desde los conceptos fundamentales hasta la configuración práctica del Collector y la integración con backends populares.
Que es OpenTelemetry
OpenTelemetry (OTel) es un proyecto open source de la Cloud Native Computing Foundation que proporciona un conjunto estandarizado de APIs, SDKs, bibliotecas de instrumentación y herramientas para generar, recolectar y exportar datos de telemetria. Nacio en 2019 de la fusion de dos proyectos anteriores: OpenCensus (impulsado por Google) y OpenTracing (bajo la CNCF).
El objetivo principal es ofrecer un único estandar vendor-neutral para instrumentar aplicaciones, eliminando la necesidad de reescribir código cuando se cambia de plataforma de observabilidad. Esto significa que puedes instrumentar tu aplicación una vez y enviar los datos a Jaeger, Prometheus, Datadog, Grafana Cloud o cualquier otro backend compatible.
Las tres señales de observabilidad
OpenTelemetry trabaja con tres tipos de datos de telemetria, conocidos como las tres señales:
- Trazas (Traces): Representan el recorrido completo de una solicitud a traves de multiples servicios. Cada traza se compone de spans que capturan operaciones individuales con su duración, atributos y relación jerarquica.
- Métricas (Metrics): Mediciones numericas agregadas en el tiempo, como contadores de requests, histogramas de latencia o gauges de uso de memoria. OTel soporta los tipos Counter, UpDownCounter, Histogram y Gauge.
- Logs: Registros de eventos con marca de tiempo. Aunque es la señal mas reciente en alcanzar estabilidad, OTel permite correlacionar logs con trazas a traves del trace context.
Arquitectura de OpenTelemetry
La arquitectura se divide en tres capas principales que trabajan juntas para llevar los datos desde tu aplicación hasta el backend de observabilidad.
Instrumentación (SDK y APIs)
La capa de instrumentación vive dentro de tu aplicación. La API define las interfaces para crear spans, registrar métricas y emitir logs. El SDK implementa esas interfaces y se encarga de procesar, muestrear y exportar los datos.
Existen dos formas de instrumentar:
- Instrumentación manual: Tu código crea spans y registra métricas explicitamente usando la API de OTel.
- Auto-instrumentación: Bibliotecas que interceptan frameworks y librerias populares (HTTP, gRPC, bases de datos) sin modificar código de aplicación.
OpenTelemetry Collector
El Collector es un componente independiente que recibe, procesa y exporta datos de telemetria. Actua como proxy entre tus aplicaciones y los backends, permitiendo transformar datos, hacer batching, filtrar y enrutar sin tocar el código de las aplicaciones.
Backends de almacenamiento y visualización
OTel no almacena datos por si mismo. Los exporta a backends especializados como Jaeger (trazas), Prometheus (métricas), Loki (logs) o soluciones comerciales como Datadog y New Relic.
Configuración del SDK en Python
A continuación se muestra como configurar OpenTelemetry en una aplicación Python con trazas y métricas.
Instalación de dependencias
pip install opentelemetry-api \
opentelemetry-sdk \
opentelemetry-exporter-otlp \
opentelemetry-instrumentation-flask \
opentelemetry-instrumentation-requests
Configuración básica de trazas
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.resources import Resource
# Definir el recurso que identifica al servicio
resource = Resource.create({
"service.name": "mi-api-pedidos",
"service.version": "1.2.0",
"deployment.environment": "production"
})
# Configurar el TracerProvider con el exporter OTLP
provider = TracerProvider(resource=resource)
otlp_exporter = OTLPSpanExporter(endpoint="http://otel-collector:4317")
provider.add_span_processor(BatchSpanProcessor(otlp_exporter))
trace.set_tracer_provider(provider)
# Obtener un tracer para crear spans
tracer = trace.get_tracer("mi-api-pedidos")
Instrumentación manual de una operación
def procesar_pedido(pedido_id):
with tracer.start_as_current_span("procesar-pedido") as span:
span.set_attribute("pedido.id", pedido_id)
span.set_attribute("pedido.tipo", "ecommerce")
# Validar pedido
with tracer.start_as_current_span("validar-pedido"):
validar(pedido_id)
# Procesar pago
with tracer.start_as_current_span("procesar-pago") as pago_span:
resultado = cobrar(pedido_id)
pago_span.set_attribute("pago.estado", resultado.estado)
span.set_status(trace.StatusCode.OK)
Auto-instrumentación para Flask
from opentelemetry.instrumentation.flask import FlaskInstrumentor
from opentelemetry.instrumentation.requests import RequestsInstrumentor
app = Flask(__name__)
# Instrumentar automaticamente Flask y requests
FlaskInstrumentor().instrument_app(app)
RequestsInstrumentor().instrument()
Con auto-instrumentación, cada request HTTP entrante a Flask y cada llamada saliente con requests genera spans automáticamente, sin modificar tu código de negocio.
Configuración del OpenTelemetry Collector
El Collector se configura mediante un archivo YAML que define receivers, processors y exporters organizados en pipelines.
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
batch:
timeout: 5s
send_batch_size: 1024
memory_limiter:
check_interval: 1s
limit_mib: 512
spike_limit_mib: 128
attributes:
actions:
- key: environment
value: production
action: upsert
exporters:
otlp/jaeger:
endpoint: jaeger:4317
tls:
insecure: true
prometheus:
endpoint: 0.0.0.0:8889
namespace: otel
loki:
endpoint: http://loki:3100/loki/api/v1/push
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, batch, attributes]
exporters: [otlp/jaeger]
metrics:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [prometheus]
logs:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [loki]
Despliegue del Collector con Docker Compose
services:
otel-collector:
image: otel/opentelemetry-collector-contrib:0.96.0
command: ["--config=/etc/otel/config.yaml"]
volumes:
- ./otel-config.yaml:/etc/otel/config.yaml
ports:
- "4317:4317" # gRPC OTLP
- "4318:4318" # HTTP OTLP
- "8889:8889" # Prometheus metrics
depends_on:
- jaeger
- prometheus
Integración con Jaeger, Prometheus y Grafana
Una de las ventajas principales de OTel es que puedes enviar cada señal al backend mas adecuado y luego correlacionar todo en Grafana.
Flujo tipico de datos
- Las aplicaciones envian trazas, métricas y logs al Collector via OTLP.
- El Collector procesa y enruta: trazas a Jaeger, métricas a Prometheus, logs a Loki.
- Grafana conecta con los tres backends como datasources.
- En Grafana puedes navegar de una métrica a la traza asociada usando el trace ID correlacionado.
Correlación entre señales
La correlación es el verdadero poder de OTel. Al propagar el trace context entre servicios, cada métrica y log puede vincularse a una traza específica. En Grafana, esto permite hacer click en un pico de latencia en un dashboard y saltar directamente a la traza que lo causo.
Mejores prácticas de implementación
Estrategia de sampling
No es necesario capturar el 100% de las trazas en producción. Configura sampling basado en reglas:
from opentelemetry.sdk.trace.sampling import TraceIdRatioBased, ParentBasedTraceIdRatio
# Capturar el 10% de las trazas en produccion
sampler = ParentBasedTraceIdRatio(0.1)
provider = TracerProvider(resource=resource, sampler=sampler)
Para entornos de desarrollo o staging, usa ALWAYS_ON. En producción, ajusta el ratio segun tu volumen de tráfico y presupuesto de almacenamiento.
Naming conventions
Usa nombres descriptivos y consistentes para spans y métricas:
- Spans:
servicio.operación(por ejemplo,pedidos.procesar,inventario.consultar) - Métricas:
servicio_operacion_unidad(por ejemplo,http_request_duration_seconds) - Atributos semanticos: sigue las convenciones de OTel Semantic Conventions para HTTP, DB, messaging, etc.
Recursos y atributos
Siempre configura service.name, service.versión y deployment.environment como atributos de recurso. Esto permite filtrar y agrupar datos en los backends.
Rendimiento y overhead
- Usa
BatchSpanProcessoren lugar deSimpleSpanProcessorpara reducir el impacto en la aplicación. - Configura
memory_limiteren el Collector para evitar que consuma demasiada memoria bajo carga. - Mide el overhead de la instrumentación en tus servicios críticos antes de habilitar todo en producción.
Errores comunes a evitar
- No configurar resource attributes: Sin
service.name, los datos llegan al backend sin contexto y son dificiles de filtrar. - Sampling demasiado agresivo: Si capturas muy pocas trazas, pierdes visibilidad en errores infrecuentes. Considera tail-based sampling en el Collector.
- Ignorar la propagación de contexto: Si un servicio intermedio no propaga el trace context, las trazas se fragmentan y pierdes la vision end-to-end.
- Usar el Collector en modo standalone para producción: En entornos productivos, despliega el Collector como sidecar o DaemonSet para mayor resiliencia.
Conclusion
OpenTelemetry representa un cambio fundamental en como instrumentamos y observamos sistemas distribuidos. Al adoptar un estandar abierto y vendor-neutral, los equipos DevOps ganan flexibilidad para elegir backends, consistencia en la instrumentación y la capacidad de correlacionar trazas, métricas y logs en una sola vista. La inversion en configurar correctamente el SDK, el Collector y las integraciones con Jaeger, Prometheus y Grafana se traduce en menor tiempo de resolución de incidentes y mayor confiabilidad operativa.