Skip to content
PПромтбук
RUEN
08Observability

Rollout distributed tracing

OpenTelemetry instrumentation, head vs tail sampling, что обязательно span'ить, как читать traces и не убить latency.

Действуй как Distributed Systems Engineer. Спроектируй rollout distributed tracing на {{services_count}} сервисов. Текущее состояние: {{current_tracing}}.

Что сделать

  1. Стандарт: OpenTelemetry (OTel). Не вендорский SDK. OTel = lingua franca tracing, переключение бэкенда (Jaeger → Tempo → Datadog → Honeycomb) без переписывания инструментации.

    • OTel SDK + OTel Collector (агент / sidecar / DaemonSet). Сервис отдаёт OTLP в Collector, Collector экспортирует туда, куда нужно.
    • Auto-instrumentation для популярных libs (HTTP, gRPC, SQL, Redis, Kafka) — лови ~80% spans бесплатно.
    • Manual instrumentation для бизнес-операций (process_order, compute_recommendation) — без них trace не отражает доменную логику.
  2. Sampling: head vs tail. Без сэмплинга tracing 1:1 = огромная стоимость + latency overhead. С сэмплингом — ловишь только то, что важно.

    • Head-based sampling: решение в начале trace (по request_id), все spans в trace либо собираются, либо нет. Дёшево, predictable. Минус: можно потерять редкий, но важный slow request.
    • Tail-based sampling: Collector копит N секунд spans в памяти, потом решает (slow → keep, error → keep, иначе drop 99%). Лучше signal, дороже compute, требует Collector с памятью под буфер.
    • Гибрид: 1% head sampling baseline (для baseline metrics) + 100% tail-keep для (error || latency > p99 || feature_flag=verbose).
    • Не сэмплируй один сервис из цепочки — получишь broken trace. Sampling решение распространяется через trace context (W3C traceparent).
  3. Что обязательно span'ить. Принцип: каждая boundary = span. Внутри сервиса — span на «значимую» операцию.

    • Network boundaries: входящий HTTP/gRPC, исходящий HTTP/gRPC, queue send/receive, DB query, cache get/set. Auto-instrumentation покрывает.
    • Process boundaries: subprocess, async job dispatch / handle. Manual instrumentation + context propagation через очередь (trace_id в message headers).
    • Доменные операции: charge_card, apply_discount, render_pdf. Manual span с атрибутами (order.amount, discount.code).
    • Атрибуты на span: semantic conventions OTel (http.method, db.statement, messaging.system). Не делай свои названия — ломает интеграции.
    • Без PII в атрибутах (email, токены). Redaction в Collector processor.
  4. Как читать traces для диагностики. Trace без навыка чтения = просто красивая waterfall.

    • Flame graph view: где длинный span? Critical path запроса.
    • Service map: какие сервисы в trace, какие dependencies, error rate на ребро.
    • Span attributes: db.statement, http.status_code — фильтрация и группировка.
    • Pattern matching: «slow checkout = всегда span X > 500ms» → root cause в X.
    • Trace + logs correlation: trace_id в каждом логе → from trace jump to logs by trace_id, не grep.
    • Anomaly detection: диффинг между «нормальный» trace и «slow» trace — что добавилось / удлинилось.
  5. Latency overhead — как не убить prod. Tracing не бесплатен.

    • Budget: instrumentation overhead < 1% latency p95. Замерь до/после на canary.
    • Async export: Collector batches и шлёт в background, не блокирует request. BatchSpanProcessor, не SimpleSpanProcessor.
    • Span attributes count: ≤ 20 атрибутов на span. Иначе serialization overhead + drop в Collector.
    • Cardinality: не клади high-cardinality (user_id) в attributes, которые потом группируются — взрывает backend index.
    • Off-CPU работа: Collector в sidecar / DaemonSet, не in-process, чтобы не делить CPU с app.
    • Network back-pressure: Collector с persistent queue (file-based) на случай, если backend timeout'ит.

Anti-patterns

  • ❌ Custom format вместо OTel — каждые 2 года переписывать всё.
  • ❌ 100% sampling «потому что нам нужны все traces» — миллион $/мес на storage, никто не читает 99% trace'ов.
  • ❌ Один сервис без context propagation — все downstream trace'ы broken («missing parent span»).
  • SimpleSpanProcessor в проде — sync export на критическом пути, +50ms latency.
  • ❌ Tracing без correlation с логами и метриками — три параллельные системы, switch cost при инциденте.
  • ❌ Rollout «все сервисы сразу» — нет baseline, не понять, где регрессия.

Формат вывода

## Architecture
`App → OTel SDK → OTel Collector (sidecar/DaemonSet) → Backend`
(mermaid diagram)

## Sampling strategy
| Layer | Policy | Sample rate | Rationale |
| Head (SDK) | always-on | 100% (context only) | propagate context |
| Tail (Collector) | error || slow || baseline 1% | ... |

## Mandatory spans (per service)
| Span | Type | Attributes (semconv) | Notes |

## Instrumentation rollout plan
| Phase | Services | Type (auto/manual) | Owner | Done by |
| 1 (canary) | 1 service | auto + manual | ... | week 1 |
| 2 | top-5 by traffic | auto | ... | week 3 |

## Backend choice
| Tool | Pros | Cons | Cost estimate |

## Latency budget
| Phase | p95 before | p95 after | Δ |

## Trace-log-metric correlation
- trace_id в логах: ✓ / ✗
- exemplars в metrics: ✓ / ✗

Принцип: tracing — это «как связаны куски системы». Без него distributed system = чёрный ящик. Но плохо настроенный tracing = чёрный ящик + большой счёт.

К подразделу «Observability»
Похожие промты