awesome-everything EN
↑ Обратно к восхождению

Наблюдаемость

Согласованность sampling и tier tail-sampling Collector

Суть Consistent sampling использует детерминированный хэш trace-id: все сервисы договариваются о keep/drop без координации. Tail-sampling Collector требует load-balancing exporter, RAM-бюджета и cap для защиты от OOM.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на senior-высоте — в орбите
◷ 14 min

Tail-sampling Collector OOM’ит каждые несколько часов в пиковый трафик. Команда поднимает memory limit — и снова OOM. Никто не спросил: почему растёт память, что её контролирует и что её ограничивает?

Согласованность sampling между сервисами

«Consistent sampling» означает: все span’ы одного трейса либо все сэмплированы, либо все дропнуты. Никаких частичных трейсов — либо backend имеет полный трейс, либо ничего.

Механизм: вероятностные head-sampler’ы используют детерминированный хэш trace-id по модулю 100 для решения, какие трейсы хранить. Потому что хэш детерминированный и trace-id одинаков во всех сервисах (пропагируется traceparent), каждый сервис независимо приходит к тому же решению keep/drop. Координации не нужно.

W3C Trace Context Level 2 (2024) формализует это через флаг random-trace-id (бит 1, значение 02): когда установлен, trace-id гарантированно равномерно случаен, делая hash-based consistent sampling надёжным. Consistent hash-based sampler’ы читают этот флаг как предусловие; при его отсутствии могут откатиться к более простому подходу.

Почему частичные трейсы хуже отсутствия трейсов:

  • Трейс без span’а сервиса B выглядит как прямой вызов из A в C. Атрибуция latency неверна.
  • Расчёты перцентилей по частичным трейсам дают систематическое смещение — короткие span’ы перепредставлены.
  • On-call-инженеры диагностируют по тому, что видят; частичные трейсы ведут к уверенным неверным диагнозам.

Модель памяти tail-sampler’а

Tail-sampling Collector держит все span’ы каждого активного трейса в памяти до закрытия decision-окна. Использование памяти:

RAM = active_traces × avg_spans_per_trace × bytes_per_span
    = (in_flight_request_rate × decision_window_seconds) × avg_spans × span_size

При 10k req/s, 30с окно, 10 span’ов/трейс, 1 KB/span: 10 000 × 30 × 10 × 1 024 ≈ 3 GB на Collector instance

Каждый фактор — независимый рычаг OOM:

ФакторЧто раздуваетСмягчение
active_tracesTraffic-всплеск, слишком длинное окноМасштабировать реплики, сократить окно
spans_per_traceLong-running batch-job’ы, рекурсивная инструментацияЛомать длинные трейсы через span-link’и
bytes_per_spanБольшие значения атрибутов, избыточные метаданныеЛимиты размера значений атрибутов
decision_windowКонсервативно слишком длинноеНастраивать per-service SLA; 30с — типично

Требование load-balancing exporter

Несколько реплик Collector’а — стандарт в production (5–20 реплик типично). Без роутинга span’ы одного трейса случайно разбрасываются по репликам — ни одна не видит полный трейс и ни одна не может принять корректное policy-решение.

OTel Collector поставляется с exporter’ом loadbalancing, хэширующим по trace-id и направляющим на фиксированную реплику. Механизмы discovery: DNS round-robin, статический список или Kubernetes endpoint API.

Операционные подвохи:

  • Scale-события: когда Collector-реплика добавляется или удаляется, hash ring перебалансируется. Span’ы в полёте для трейсов, хэшированных на перебалансированный shard, могут прийти на новую реплику до более ранних span’ов — tail sampler видит частичный трейс и может принять решение преждевременно. Смягчение: короткие decision-окна (30с), graceful drain при scale-down.
  • Потеря реплики: если Collector-реплика умирает в середине decision-окна, её in-flight трейсы теряются. Tail sampling по умолчанию не персистирует на диск. Это приемлемо: потеря трейсов при сбое Collector’а — известный операционный trade-off.

Защита от OOM tail-sampler’а

Tail-sampler без cap’а будет буферизовать неограниченно до OOM процесса. Процессор tail_sampling предоставляет num_traces как жёсткий cap на число активных трейсов. При достижении cap’а sampler начинает вытеснять самые старые in-progress трейсы (незавершённые трейсы отбрасываются).

Инцидент Slack 2023: tail-sampling Collector’ы уронили tracing-pipeline во время крупного инцидента, потому что num_traces был без cap’а. Postmortem добавил cap, отдельный high-priority always-keep tier для критических трейсов и alert на otelcol_processor_dropped_spans_rate.

Полный паттерн конфигурации:

processors:
  tail_sampling:
    decision_wait: 30s
    num_traces: 100000           # жёсткий cap — вытеснять старейшие при превышении
    policies:
      - name: errors
        type: status_code
        status_code:
          status_codes: [ERROR]
      - name: slow-traces
        type: latency
        latency:
          threshold_ms: 2000
      - name: probabilistic
        type: probabilistic
        probabilistic:
          sampling_percentage: 1
Alert-метрикаЧто означаетПорог
otelcol_processor_tail_sampling_count_traces_on_memoryАктивные in-flight трейсыAlert при 80% num_traces cap
otelcol_processor_dropped_spansSpan’ы дропнуты из-за cap’аAlert при любой ненулевой частоте
container_memory_working_set_bytes (Collector pod)Реально используемая RAMAlert при 85% memory limit
Проследи
1/5

Tail-sampling Collector OOM'ит каждые несколько часов. Проследи root cause.

1
Step 1 of 5
Шаг 1: tail-sampler OOM — какая модель памяти?
2
Locked
Шаг 2: проверяем метрики — какой фактор растёт?
3
Locked
Шаг 3: spans-per-trace раздулось — типичная причина?
4
Locked
Шаг 4: число трейсов раздулось — типичная причина?
5
Locked
Шаг 5: устойчивый фикс?
Референсные числа tail-sampling Collector
W3C Trace Context Level 1
Recommendation 2020-02
W3C Trace Context Level 2 (флаг random-trace-id)
Recommendation 2024
Типичное tail decision-окно
30с
RAM Collector при 50k трейсов × 100 span × 1 KB
~5 GB
Типичных реплик Collector в production
5–20
Ключ роутинга load-balancing exporter
trace-id (детерминированный хэш)
Викторина

Сервис эмитирует 5% orphan span'ов для внутренних (не entry-point) сервисов. Команда добавляет tail sampling. Orphan rate остаётся. Что не так понято?

Викторина

Реплика Collector'а удаляется при scale-down. Что происходит с трейсами, чьё decision-окно ещё не закрылось на этой реплике?

Вспомните перед уходом
  1. 01
    Как детерминированное хэширование trace-id достигает consistent sampling без координации между сервисами?
  2. 02
    Объясни failure mode OOM tail-sampler'а и три независимых смягчения.
  3. 03
    Почему load-balancing exporter обязан использовать хэширование по trace-id, а не round-robin или least-connections?
Итог

Consistent sampling использует детерминированный hash(trace-id) mod 100, чтобы все сервисы независимо договорились о keep/drop — частичные трейсы никогда не возникают. Флаг random-trace-id W3C Level 2 делает это безопасным, утверждая равномерное распределение trace-id. RAM tail-sampling Collector’а — active_traces × span'ы/трейс × байт/span × decision_window; каждый фактор — независимый OOM-рычаг. Жёсткий cap num_traces предотвращает неограниченный рост; alert’ы на otelcol_processor_dropped_spans ловят срабатывание cap’а. Load-balancing exporter обязан использовать хэширование по trace-id — round-robin разбрасывает span’ы трейса по репликам, делая policy-решения неверными. Long-running трейсы (batch-job’ы, накапливающие тысячи span’ов) нужно ломать на под-трейсы через span-link’и, чтобы вмещаться в decision-окно.

Связанные уроки
Продолжить восхождение ↑Async context на разных языках, service mesh, миграция B3 и безопасность
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.