linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
  • Введение
  • Уроки
  • How it works
  • Симулятор
  • База знаний
  • Собеседование
Index
Categories
All entries
Footer
linuxlab-УчебникиЦеныО платформеКонфиденциальность и куки
Copyright © 2026 LinuxLab. Все права защищены.
home/linux/kb/Observability и мониторинг/tracing-basics

kb/observability ── Observability и мониторинг ── intermediate

Distributed tracing: span, context propagation, sampling

Tracing - граф spans (parent-child) одного логического запроса через сервисы. Context передаётся HTTP-header traceparent (W3C). Sampling: head (на edge, дёшево) или tail (в Collector, точнее). Backend: Jaeger, Tempo, Zipkin.

view as markdownaka: distributed-tracing, span, traceparent, w3c-trace-context, trace-sampling

Зачем distributed tracing

Монолит: stack trace показывает весь путь запроса. Распределённая система: запрос идёт через 5-15 микросервисов. Stack trace одного сервиса не помогает, где именно время потерялось?

Distributed tracing склеивает все участки в один граф:

POST /checkout (4.2s) [edge]
 ├─ cart-service.fetch (12ms)
 ├─ payment-service.charge (4.1s) ◄── вот где тормозит
 │   ├─ stripe-api.call (4.0s timeout) ◄── а это первоисточник
 │   └─ db.update (8ms)
 └─ inventory-service.reserve (45ms)

Это и есть trace, дерево spans с одним общим trace_id.

Терминология

  • Span, единица работы: HTTP-handler, DB-query, RPC-call. Имеет start_time, end_time, name, attributes, status.
  • Trace, все spans с одним trace_id, организованные в дерево через parent_span_id.
  • Trace context, (trace_id, span_id, flags) который передаётся через сетевые границы.
  • Sampling decision, keep или drop эту трассу. Принимается на edge (head) или в Collector (tail).

Span structure (OTLP):

{
  trace_id: "4bf92f3577b34da6a3ce929d0e0e4736",  // 16 bytes hex
  span_id:  "00f067aa0ba902b7",                   //  8 bytes hex
  parent_span_id: "0ef3...",                      // null если root
  name: "HTTP POST /checkout",
  start_time_unix_nano: 1683456789123456789,
  end_time_unix_nano:   1683456793321456789,
  attributes: {
    "http.method": "POST",
    "http.status_code": 500,
    "http.route": "/checkout",
    "service.name": "edge"
  },
  events: [
    {time: ..., name: "exception", attributes: {"exception.type": "TimeoutError"}}
  ],
  status: {code: ERROR, message: "stripe timeout"}
}

W3C Trace Context, context propagation

Без context propagation каждый сервис генерил бы свой trace_id невозможно склеить.

W3C Trace Context (стандарт 2020), два HTTP-header'а:

traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
              ^^   ^trace_id (32 hex)                 ^span_id (16) ^^
              version                                                flags
tracestate: vendor1=value1,vendor2=value2
  • flags=01 = sampled (downstream должен tracing'овать)
  • flags=00 = not sampled (можно skip)

Edge сервис генерит traceparent. Каждый downstream сервис:

  1. Парсит входящий traceparent
  2. Создаёт child span под parent_span_id = span_id из header'а
  3. Генерит новый span_id для своего span'а
  4. Передаёт обновлённый traceparent (свой span_id) дальше

Auto-instrumentation [[opentelemetry|OpenTelemetry SDK]] делает это автоматически для HTTP/[[grpc-basics|gRPC]]/Kafka. Через [[http2-internals|HTTP/2]] header'ы передаются в HPACK-сжатии.

B3 (Zipkin) headers, legacy

До W3C был B3 Propagation от Zipkin:

X-B3-TraceId: 4bf92f3577b34da6a3ce929d0e0e4736
X-B3-SpanId: 00f067aa0ba902b7
X-B3-ParentSpanId: 05e3...
X-B3-Sampled: 1

В 2025 поддерживай оба для совместимости со старыми клиентами:

OTEL_PROPAGATORS=tracecontext,b3

Sampling, head vs tail

100% traces неподъёмны: 10K req/s × 5 spans × 5KB = 250 MB/s. Sampling обязателен.

Head-based sampling

Решение в самом начале трассы (на edge):

python
if random() < 0.01:        # 1% sampling
    flags = 0x01           # sampled, всё дерево соберём
else:
    flags = 0x00           # not sampled
  • Плюс: предсказуемо, дёшево, downstream не нужны лишние данные
  • Минус: рандомно теряем error traces (99% потеряли)

Tail-based sampling

Все spans собираем в [[opentelemetry|OTel Collector]], держим в памяти 5-30s, ждём всех children. Потом решаем по полной картине:

yaml
processors:
  tail_sampling:
    decision_wait: 30s
    num_traces: 100000
    policies:
      - name: errors
        type: status_code
        status_code: {status_codes: [ERROR]}
      - name: slow
        type: latency
        latency: {threshold_ms: 1000}
      - name: random
        type: probabilistic
        probabilistic: {sampling_percentage: 1}

Сохраняем 100% errors, 100% slow, 1% rest. Точно нужное.

  • Плюс: видим все ошибки и аномалии
  • Минус: Collector держит full traces в RAM 30s, нужно ~3 GB на 10K rps. Sharding по trace_id обязателен в кластере.

Adaptive sampling

Динамически меняет rate в зависимости от volume. Реализован в Datadog, Honeycomb. В open-source, guardrail в Tempo (выкидывает если backend перегружен).

Backend storage

BackendStorageQueryКогда
JaegerCassandra/ESUI + APIclassic, in-memory dev
Tempo (Grafana)object (S3)TraceQL, Grafana UIдёшево, scale
ZipkinMySQL/CassandraUI + APIстарый, простой
HoneycombproprietaryBubbleUphosted, ML-driven
Datadog APMproprietaryUIhosted, integrated

Tempo, рекомендованный для self-hosted: S3-backed (как Loki), не нужно поддерживать Cassandra/ES, дешёвое retention (~$0.05/GB).

Correlation: traces + logs + metrics

Сила трассировки, в связке:

  • Лог пишем {"trace_id": "abc", "level": "error", ...}, клик из Jaeger/Tempo прыгает в [[loki-grafana-logging|Loki]] с фильтром {trace_id="abc"}
  • Метрика histogram с exemplar (p99=2.3s, trace_id="abc") клик из Grafana прыгает в Tempo на конкретную медленную трассу
  • Span'у link ссылается на другую trace (для batch jobs)

Всё держится на двух идентификаторах: trace_id и span_id. Они должны быть во всех логах edge-сервиса.

TraceQL, query language Tempo

Tempo 2.0+ ввёл TraceQL, SQL-like для трасс:

{ resource.service.name = "checkout" && duration > 1s &&
  span.http.status_code = 500 }

Возвращает trace_id'ы. Можно сложнее:

{ service.name = "edge" } >> { service.name = "payment" &&
  status = error }

>> означает "ancestor of", все трассы где edge вызвал payment, и payment упал. Полезно для root-cause анализа.

Когда что-то пошло не так

  • Trace разорван, child span'ов нет, context не передан. Проверь traceparent приходит на downstream (curl -v). Часто причина: HTTP client без instrumentation, или middleware стрипает headers.
  • trace_id есть, но в Tempo не видно, head sampling 0.01 уронил. Включи tail sampling с keep-on-error policy.
  • Span'ы из Kafka не связаны, message-based propagation отдельно. Передавай traceparent как Kafka header в producer, парси в consumer.
  • Ridiculously deep traces (1000+ spans), instrumented каждая DB-query внутри loop'а. Sample inside loop или upper-bound.
  • Clock skew между сервисами → spans с end_time < start_time Jaeger/Tempo визуализирует как кривая. Поправь NTP (chrony-and-ntp).
  • Trace context прыгает через async boundary, setTimeout, Promise.then, goroutine. Используй context-passing (Go: ctx, Node: AsyncLocalStorage, Python: contextvars).
  • Browser → backend trace не связан, frontend не отправляет traceparent. Подключи @opentelemetry/instrumentation-fetch.

Anti-patterns

  • Tracing на каждый mysql.query в hot path, overhead. Sample выше, в HTTP-handler.
  • Span на каждую внутреннюю функцию, это работа [[pyroscope-continuous-profiling|profiler'а]], не tracer'а. Tracing для cross-service границ.
  • Огромные attributes (whole JSON body), exporter затыкается, storage растёт. Атрибуты должны быть короткие.

§ команды

bash
curl -H 'traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01' http://api/v1/test

Ручной trigger trace - downstream должен подцепить trace_id и span_id из header'а

bash
curl -s tempo:3200/api/traces/4bf92f3577b34da6a3ce929d0e0e4736 | jq '.batches[].scopeSpans[].spans[] | {name, durationNanos}'

Tempo trace by ID - все spans с длительностью

bash
curl -s 'tempo:3200/api/search?tags=service.name%3Dcheckout&minDuration=1s'

Tempo search API - найти медленные трассы checkout-сервиса

bash
OTEL_TRACES_SAMPLER=parentbased_traceidratio OTEL_TRACES_SAMPLER_ARG=0.05 ./app

Head sampling 5% с inheritance от parent (consistent через все сервисы)

bash
otelcol --config=tail-sampling.yaml

Запуск Collector с tail sampling - keep errors+slow+1pct random

bash
jaeger-query --query.base-path=/jaeger

Jaeger UI на :16686 - поиск traces по service/operation/tags

§ см. также

  • opentelemetryOpenTelemetry: signals, OTLP, Collector pipelineOpenTelemetry - CNCF-стандарт для metrics+traces+logs в одном SDK. OTLP протокол (gRPC или HTTP). Collector принимает, фильтрует, роутит в Prom/Tempo/Loki/Jaeger. Auto-instrumentation без code change.
  • http2-internalsHTTP/2 internals - binary framing, HPACK, stream multiplexingHTTP/2 - бинарный мультиплексинг поверх одного TCP-соединения. HPACK сжимает headers через индексированный словарь. Streams независимы. Server push deprecated. На loss-friendly link HoL-blocking - проблема, которую решил QUIC.
  • grpc-basicsgRPC - HTTP/2 + Protobuf RPC frameworkgRPC = HTTP/2 + Protocol Buffers + кодогенерация. Четыре типа RPC: unary (как REST), server-stream, client-stream, bidirectional. Сильная типизация, бинарный wire format, multi-language. grpcurl как curl для gRPC.
  • loki-grafana-loggingLoki: label-based логи, LogQL, Promtail/Vector pipelineLoki - log aggregation с label-based индексом (не full-text как Elastic). Дёшево на S3-storage. Promtail/Vector как агенты. LogQL похож на PromQL: фильтр + parse + aggregation. Cardinality - враг.
  • pyroscope-continuous-profilingContinuous profiling: Pyroscope, eBPF, flame graphs в продеContinuous profiling - always-on CPU/memory profiler в проде через eBPF. 1-2% overhead. Flame graphs показывают hot path. Pyroscope (Grafana), Parca, Polar Signals. Замена ad-hoc perf для production debug.
Footer
linuxlab-
Copyright © 2026 LinuxLab. Все права защищены.
Учебники
Цены
О платформе
Конфиденциальность и куки