Controla costos con muestreo: configuración del SDK y del Collector
Decide cuándo muestrear en el SDK y cuándo usar tail sampling en un Collector de dos niveles.
Conservar cada traza se vuelve caro rápidamente, y la mayoría son peticiones rutinarias que nadie abrirá nunca. El muestreo conserva un subconjunto representativo o interesante. OpenTelemetry ofrece dos lugares donde decidir: en el SDK cuando empieza una traza (head sampling) y en el Collector cuando la traza está completa (tail sampling). Ambos son pura configuración. Esta guía muestra cada uno y cómo elegir.
Head sampling en el SDK
La decisión se toma cuando se crea el span raíz, así que es barata y no necesita infraestructura adicional. No puede saber si la petición fallará o será lenta, porque todavía no ha ocurrido.
export OTEL_TRACES_SAMPLER=parentbased_traceidratio
export OTEL_TRACES_SAMPLER_ARG=0.1
Los nombres de sampler definidos por la especificación incluyen always_on, always_off, traceidratio, parentbased_always_on (el predeterminado), parentbased_always_off y parentbased_traceidratio. Usa las variantes parentbased_: siguen la decisión de muestreo del padre de la petición entrante, de modo que una traza se conserva o se descarta como un todo entre servicios. Un traceidratio simple en cada servicio tomaría decisiones independientes y produciría trazas rotas.
En el archivo de configuración declarativa, la misma idea se ve así:
tracer_provider:
sampler:
parent_based:
root:
trace_id_ratio_based:
ratio: 0.1
Consulta la documentación de tu SDK para los nombres de sampler que soporta en el formato de archivo.
Tail sampling en el Collector
El tail sampling espera hasta que una traza está completa y luego aplica políticas. Te permite conservar cada error y cada traza lenta más una pequeña parte del resto, algo que el head sampling no puede hacer. El costo es más infraestructura y más memoria.
processors:
tail_sampling:
decision_wait: 10s
num_traces: 50000
expected_new_traces_per_sec: 100
policies:
- name: keep-errors
type: status_code
status_code:
status_codes: [ERROR]
- name: keep-slow
type: latency
latency:
threshold_ms: 2000
- name: sample-the-rest
type: probabilistic
probabilistic:
sampling_percentage: 5
Cómo funciona:
decision_waites cuánto espera el processor tras ver el primer span de una traza antes de decidir. Debe ser mayor que la duración de tu traza más lenta, o decidirás sobre trazas parciales.num_traceses cuántas trazas se mantienen en memoria mientras se espera. Dimensiónalo aproximadamente como tu tasa de llegada de trazas pordecision_wait, con margen.policiesse evalúan para cada traza. Una traza se conserva si cualquier política dice que se conserve, así que las tres de arriba significan “conserva los errores, conserva las lentas y además conserva el 5 por ciento de todo”.- Otros tipos de política incluyen
string_attribute(coincidir con un valor como el nivel de un cliente),numeric_attribute,always_sample,and(combinar políticas) ycomposite(asignar tasas entre políticas).
tail_sampling está en la distribución contrib, y los nombres de sus opciones evolucionan, así que consulta su README para tu versión.
La trampa: una traza debe llegar a un solo Collector
El tail sampling solo funciona si todos los spans de una traza llegan a la misma instancia del Collector. Con varias réplicas detrás de un balanceador de carga simple, los spans de una traza se dispersan y las decisiones se toman sobre fragmentos. La solución estándar son dos niveles:
- Nivel 1 (capa de balanceo de carga) usa el exporter
loadbalancingpara enrutar los spans por trace ID a una instancia consistente del nivel 2. - Nivel 2 (capa de muestreo) ejecuta
tail_samplingy exporta las trazas conservadas.
Nivel 1:
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
exporters:
loadbalancing:
routing_key: traceID
protocol:
otlp:
tls:
insecure: true
resolver:
dns:
hostname: otel-sampler-headless.observability.svc.cluster.local
port: 4317
service:
pipelines:
traces:
receivers: [otlp]
exporters: [loadbalancing]
Nivel 2:
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
processors:
memory_limiter:
check_interval: 1s
limit_percentage: 80
spike_limit_percentage: 20
tail_sampling:
decision_wait: 10s
policies:
- name: keep-errors
type: status_code
status_code:
status_codes: [ERROR]
- name: sample-the-rest
type: probabilistic
probabilistic:
sampling_percentage: 10
batch:
exporters:
otlp/backend:
endpoint: tempo:4317
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, tail_sampling, batch]
exporters: [otlp/backend]
Usa un Service headless para que el DNS liste los pods del nivel 2, y ten en cuenta que cuando los pods escalan hacia arriba o hacia abajo el enrutamiento cambia, y unas pocas trazas en curso pueden decidirse con datos incompletos. Mantener las capas separadas también aísla los fallos: un problema en el muestreo no bloquea la ingesta.
Combinar head y tail
Una configuración común y sensata:
- Un head sampler modesto en el SDK (por ejemplo 50 por ciento) para reducir el volumen en el origen en servicios de tráfico muy alto.
- Tail sampling en el Collector para garantizar que los errores y las peticiones lentas sobrevivan.
Ten cuidado: lo que se descarta en el head no puede recuperarse en el tail. Si debes conservar todos los errores, no apliques head sampling agresivo a esos servicios.
Las métricas sobreviven al muestreo si las derivas primero
Descartar trazas también descarta los números que podrías calcular a partir de ellas, como las tasas de peticiones y los percentiles de latencia. Genera métricas antes del paso de muestreo con el connector spanmetrics, para que los dashboards sigan siendo precisos:
connectors:
spanmetrics:
service:
pipelines:
traces/all:
receivers: [otlp]
exporters: [spanmetrics, loadbalancing]
metrics/derived:
receivers: [spanmetrics]
processors: [batch]
exporters: [otlphttp/prometheus]
Aquí cada span alimenta el connector, mientras que solo el conjunto muestreado continúa hacia el almacenamiento.
Cómo elegir
| Objetivo | Enfoque |
|---|---|
| Reducir costos de forma simple, sin infraestructura nueva | Head sampling con parentbased_traceidratio |
| Conservar todos los errores y peticiones lentas | Tail sampling en un Collector de dos niveles |
| Mantener tasas y percentiles precisos | spanmetrics antes del muestreo |
| Pocos servicios, bajo volumen | Sin muestreo; revísalo cuando importe el costo |
Verifica
- En el nivel 2, compara los recuentos de spans de entrada y salida con las propias métricas del Collector (puerto 8888) para confirmar que la proporción coincide con tus políticas.
- Busca una petición fallida conocida y asegúrate de que su traza completa está presente.
- Vigila la memoria del nivel 2, porque mantiene trazas durante
decision_wait. Si crece, reducenum_traceso añade réplicas, y manténmemory_limiterdelante.
Guías populares
-
OpenTelemetry explicado: SDK, OTLP y el Collector Entiende las piezas móviles y los tres lugares donde vive la configuración antes de escribir nada de YAML. -
El modelo de configuración del Collector: componentes, pipelines y providers Aprende las reglas del YAML del Collector: nombres, pipelines, connectors, fusión de archivos y variables de entorno. -
Configura los SDK con variables de entorno OTEL_* Define el nombre del servicio, los exporters, el muestreo y los límites para cualquier lenguaje usando variables estándar. -
Instrumenta aplicaciones sin cambios de código: Java, Python y Node.js Adjunta agentes de OpenTelemetry solo con variables de entorno y flags de línea de comandos. -
Prepara el Collector para producción: límites de memoria, batching y reintentos Configura processors, colas y almacenamiento persistente para que la telemetría sobreviva a sobrecargas e interrupciones. -
Despliega el Collector en Kubernetes con Helm Instala el chart oficial, usa presets para logs y métricas y sobrescribe la configuración mediante archivos de valores. -
Soluciona problemas del Collector y los SDK Una lista de comprobación sistemática para cuando la telemetría no llega: exporter debug, health checks, métricas internas y errores comunes.