Собираем курс в одно приложение: трёхсервисная система с полным циклом трассировки — от SDK до прод-конфигурации с tail-сэмплированием и SPM.
Архитектура проекта
shop-web ──► orders ──► payments
│ │
│ └──► PostgreSQL
└──► Redis (сессии)
│
Kafka (orders.events) ──► notifier (консюмер)
│
всё выше: OTel SDK ─► OTel Collector ─► Jaeger (Elasticsearch)
Чек-лист реализации
- Инструментирование: автоинструментирование HTTP/DB/Kafka в трёх сервисах;
ручной спан бизнес-операции
place_orderс атрибутами order.id, tenant, total; - Контекст: трейс сшит shop-web → orders → payments и через Kafka в notifier (publish → process); baggage с tenant.id виден в notifier;
- Логи: trace_id/span_id в каждом лог-порядке; переход «лог → трейс» работает вручную;
- Collector: memory_limiter + filter (healthchecks) + batch; атрибуты db.statement удаляются;
- Сэмплирование: head-based parentbased_traceidratio 10% (dev-день 1), затем tail_sampling: errors 100%, latency > 500мс 100%, base 5% (день 2) — сравните объёмы;
- Хранилище: Elasticsearch + rollover по 24ч + lookback 7 дней + крон-очистка с алертом;
- Наблюдаемость: дашборд Collector/Jaeger (приток, потери, латентность) + SPM по операциям; алерты из урока 5.4;
- Аварии: воспроизведите N+1, ретраи внешнего вызова и «пустой промежуток» из модуля 4 — найдите каждый по трейсам за ≤ 5 минут.
Критерии «зачтено»
| Критерий | Проверка |
|---|---|
| Сквозной трейс 4 сервисов | один trace ID в waterfall от shop-web до notifier |
| Трейсы не ломают бизнес | остановлен Jaeger под нагрузкой: RPS и латентность не деградируют |
| Ошибки гарантированно видны | создана ошибка в 3% запросов — все попали в хранилище (tail) |
| Объём под контролем | замер ГБ/сутки соответствует расчёту ±30% |
| Инцидент-минута | по поиску находите «медленный checkout» и называете сегмент-виновник |
Что дальше
- Документация: opentelemetry.io (SDK, Collector, семантические конвенции), jaegertracing.io (архитектура, хранилища, API);
- Книга: «Distributed Systems Observability» (C. Sridharan, бесплатно) — короткая и лучшая вводная по философии;
- OpenTelemetry в Kubernetes: Operator, автоинъекция instrumentation, k8sattributes;
- Профилирование: четвёртый столб наблюдаемости — continuous profiling (Pyroscope/Parca) рядом с трейсом;
- eBPF-трассировка (Grafana Beyla и аналоги): трейсы без изменения кода — растущее направление.
Самопроверка курса (развернуть)
1. Что передаётся в traceparent? — версия, trace-id, parent span-id, флаги.
2. Почему parentbased — дефолт сэмплера? — сохраняет целостность трейса: решение одно, у корня.
3. Зачем OTel Collector, если Jaeger принимает OTLP? — батчинг, фильтры, PII, tail-сэмплинг, маршрутизация, развязка бэкенда.
4. Головная боль tail-сэмплинга? — память под ожидание трейса + LB по trace ID между репликами.
5. N+1 в трейсе выглядит как… — серия одинаковых последовательных спанов БД.
6. Как найти трейс по логу? — trace_id из лога в поле Trace ID UI.
7. messaging: спаны producer/process + контекст в свойствах сообщения.
8. Exemplar — что связывает? — метрику Prometheus с примером трейса.
9. Почему in-memory нельзя в проде? — данные исчезают при рестарте, объём = память процесса.
10. Первое при «трейсы пропали»? — метрики потерь: dropped/failed spans на пути SDK→Collector→Jaeger.