Kravchenko

Web Lab

АудитБлогКонтакты

Kravchenko

Web Lab

Разрабатываем сайты и автоматизацию на современных фреймворках под ключ

Услуги
ЛендингМногостраничныйВизитка
E-commerceБронированиеПортфолио
Навигация
БлогКонтактыАудит
Обратная связь
+7 921 567-11-16
info@kravlab.ru
с 09:00 до 18:00

© 2026 Все права защищены

•

ИП Кравченко Никита Владимирович

•

ОГРНИП: 324784700339743

Политика конфиденциальности

OpenTelemetry и сквозная трассировка: сокращаем MTTR на 40% без дорогих APM

Разработка и технологии23 апреля 2026 г.
Покажу, как быстро внедрить OpenTelemetry: настроим коллектор, автотрейсинг и связку логов с трейсами. В итоге команда быстрее находит корневые причины инцидентов, реже «стреляет в темноте» и тратит меньше на платные APM.
OpenTelemetry и сквозная трассировка: сокращаем MTTR на 40% без дорогих APM

Оглавление

  • Зачем бизнесу OpenTelemetry
  • Как это работает: краткая архитектура
  • Быстрый старт: трассировка за 30 минут
    • Docker Compose: Jaeger + OTel Collector
    • Node.js: автотрейсинг и привязка логов к трейсам
    • Проверка: видим цепочку запросов и trace_id в логах
  • В проде: сэмплирование, приватность и расходы
    • Сэмплирование на коллекторе: оставляем важное
    • Защита данных: вырезаем чувствительные атрибуты
  • Корреляция: трейс + логи + метрики = быстрый разбор
  • Чеклист внедрения на квартал
  • Типичные ошибки и как их избежать
  • Что показать бизнесу: KPI и дашборды
  • Расширение: очереди, задачи, фоновые джобы
  • Итоги

Зачем бизнесу OpenTelemetry

  • Минус 30–50% времени на поиск причин инцидентов. Сквозная трассировка показывает точный участок цепочки, где всё «сломалось»: какой сервис, какой запрос, к какой базе или внешнему API.
  • Меньше «магии» и вендор‑лока. OpenTelemetry — открытый стандарт. Сегодня пишете в Jaeger или Tempo, завтра — в другую систему, не переписывая код приложений.
  • Прозрачная стоимость. Собираете только то, что приносит пользу: ошибки, медленные запросы, ключевые бизнес‑потоки.
  • Быстрее релизы. Команда увереннее меняет код: видно, как новая версия влияет на задержку и ошибки по всей цепочке.

Как это работает: краткая архитектура

  • SDK в приложении автоматически создаёт трейсы (цепочки) и спаны (шаги внутри цепочки): входящий HTTP, исходящие вызовы, запросы в БД и т. п.
  • Контекст запроса передаётся по стандарту W3C Trace Context в заголовке traceparent — сквозь все сервисы и интеграции.
  • OpenTelemetry Collector принимает данные из приложений, обрабатывает (сэмплирует, обрезает поля), отправляет в хранилище/визуализацию (Jaeger, Tempo, сторонние решения).

Схема потока: Приложение → OTLP → OTel Collector → (обработка) → Jaeger/Tempo → графики и поиск → быстрая диагностика.

Быстрый старт: трассировка за 30 минут

Ниже — минимальный стенд на локалке: Jaeger для просмотра трейсов и OTel Collector как «шлюз» от приложений.

Docker Compose: Jaeger + OTel Collector

version: "3.8"
services:
  jaeger:
    image: jaegertracing/all-in-one:1.50
    ports:
      - "16686:16686"  # UI
      - "14250:14250"  # gRPC для приёма от коллектора
  otel-collector:
    image: otel/opentelemetry-collector-contrib:0.91.0
    command: ["--config=/etc/otel-collector-config.yaml"]
    volumes:
      - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml:ro
    ports:
      - "4317:4317"   # OTLP gRPC (если решите использовать)
      - "4318:4318"   # OTLP HTTP
    depends_on:
      - jaeger

Файл конфигурации коллектора otel-collector-config.yaml:

receivers:
  otlp:
    protocols:
      http:
      grpc:

processors:
  batch: {}

exporters:
  jaeger:
    endpoint: jaeger:14250
    tls:
      insecure: true
  logging:
    loglevel: info

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [jaeger, logging]

Запуск:

docker compose up -d

Откройте Jaeger: http://localhost:16686

Node.js: автотрейсинг и привязка логов к трейсам

Добавим автотрейсинг и выведем trace_id в логи, чтобы быстро прыгать из лога в нужный трейс.

Установите зависимости:

npm init -y
npm install express pino @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node \
  @opentelemetry/exporter-trace-otlp-http @opentelemetry/resources \
  @opentelemetry/semantic-conventions @opentelemetry/api

Файл tracing.js:

// tracing.js
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
const { Resource } = require('@opentelemetry/resources');
const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions');

const sdk = new NodeSDK({
  resource: new Resource({
    [SemanticResourceAttributes.SERVICE_NAME]: 'checkout-api',
    [SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]: process.env.NODE_ENV || 'dev',
  }),
  traceExporter: new OTLPTraceExporter({
    url: process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT || 'http://localhost:4318/v1/traces',
  }),
  instrumentations: [getNodeAutoInstrumentations()],
});

sdk.start().then(() => {
  // Готово к приёму трафика
}).catch((err) => {
  console.error('OTel init error', err);
});

module.exports = sdk;

Файл app.js:

// app.js
require('./tracing');
const express = require('express');
const pino = require('pino');
const https = require('https');
const { context, trace } = require('@opentelemetry/api');

const logger = pino({ level: 'info' });
const app = express();

function httpGet(url) {
  return new Promise((resolve, reject) => {
    https.get(url, (res) => {
      res.on('data', () => {});
      res.on('end', () => resolve(res.statusCode));
    }).on('error', reject);
  });
}

app.get('/checkout', async (req, res) => {
  const span = trace.getSpan(context.active());
  const traceId = span ? span.spanContext().traceId : undefined;

  logger.info({ trace_id: traceId, event: 'start_checkout' });

  // Имитируем работу
  await new Promise((r) => setTimeout(r, Math.random() * 200 + 50));

  // Исходящий вызов (попадёт в тот же трейс благодаря автотрейсингу)
  await httpGet('https://httpbin.org/status/200');

  logger.info({ trace_id: traceId, event: 'finish_checkout' });
  res.json({ ok: true, trace_id: traceId });
});

app.listen(3000, () => {
  logger.info('Server started on http://localhost:3000');
});

Запуск приложения:

node app.js

Проверка: видим цепочку запросов и trace_id в логах

  • Откройте в браузере http://localhost:3000/checkout несколько раз.
  • В логах приложения увидите строки с полем trace_id.
  • Перейдите в Jaeger (http://localhost:16686), выберите сервис checkout-api и найдите трейсы — вы увидите входящий запрос, искусственную «работу» и исходящий вызов.

Фокус: теперь любой лог события можно связать с конкретным трейсом. Это экономит минуты и часы на разбор инцидентов.

В проде: сэмплирование, приватность и расходы

В реальном трафике полная трассировка каждого запроса редко нужна — это дорого. Золотое правило: собирать всё по ошибкам и медленным операциям, а успешные и быстрые — сэмплировать.

Сэмплирование на коллекторе: оставляем важное

В OTel Collector есть «хвостовое» сэмплирование (tail sampling): решение принять/отбросить трейс принимается после завершения, когда известны итоги (ошибка, задержка). Пример: сохраняем только ошибки и медленные запросы дольше 500 мс.

processors:
  tail_sampling:
    decision_wait: 5s
    num_traces: 50000
    policies:
      - name: errors
        type: status_code
        status_code:
          status_codes: [ERROR]
      - name: slow
        type: latency
        latency:
          threshold_ms: 500

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [tail_sampling, batch]
      exporters: [jaeger]

Так вы концентрируете хранилище на проблемных запросах и существенно снижаете расходы.

Защита данных: вырезаем чувствительные атрибуты

Не отправляйте персональные данные пользователя в трейсы и логи. Удаляйте или маскируйте их на коллекторе.

processors:
  attributes:
    actions:
      - key: http.request.header.authorization
        action: delete
      - key: user.email
        action: delete
      - key: card.pan
        action: delete

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [attributes, batch]
      exporters: [jaeger]

Это снижает риски утечек и упрощает соответствие требованиям (например, локальным политикам безопасности, PCI DSS и пр.).

Корреляция: трейс + логи + метрики = быстрый разбор

  • Трейсы отвечают на вопрос «где именно замедление или ошибка в цепочке?»
  • Логи дают детализацию: входные параметры, ветку кода, уникальные маркеры.
  • Метрики фиксируют тренды и бюджеты ошибок: p95/пиковые задержки, процент ошибок, пропускная способность.

Минимальный стандарт: всегда прокидывайте trace_id в логи. Улучшение: отправляйте логи в систему, где кликом можно перейти к трейсу (например, через поле trace_id). Ещё лучше: добавьте бизнес‑метрики в спаны (стоимость корзины, тариф, тип клиента), но без персональных данных — это помогает отличать «медленно для всех» от «медленно для VIP‑сегмента».

Чеклист внедрения на квартал

  • Неделя 1–2: пилот на 1–2 сервисах, автотрейсинг, Jaeger/Tempo + OTel Collector. Цель — первые трейсы в UI и trace_id в логах.
  • Неделя 3–4: базовые дашборды и алерты: рост ошибок, всплески задержек по маршрутам, зависимые внешние API.
  • Неделя 5–6: хвостовое сэмплирование, фильтрация чувствительных полей, выделение ключевых бизнес‑потоков (checkout, оплата, регистрация).
  • Неделя 7–8: покрытие очередей/фона (см. ниже), документация для разработчиков: как смотреть трейс, как добавлять кастомные спаны.
  • Неделя 9–12: масштабирование на остальные сервисы, пересмотр порогов алертов, анализ экономии и влияния на MTTR.

Типичные ошибки и как их избежать

  • Сэмплируете «в лоб» на уровне SDK. Итог — теряются важные трейсы об ошибках. Решение: сэмплируйте «на хвосте» в коллекторе по статусу и задержке.
  • Не прокидываете контекст в очереди. В итоге фоновые задачи выпадают из цепочки. Решение: явно инжектируйте контекст в заголовки сообщений.

Пример для Node.js (облегчённо):

const { context, propagation } = require('@opentelemetry/api');

// Отправитель
const carrier = {};
propagation.inject(context.active(), carrier); // carrier кладите в заголовки сообщения
sendToQueue({ body: data, headers: carrier });

// Получатель
function onMessage(msg) {
  const ctx = propagation.extract(context.active(), msg.headers || {});
  // Запускаем работу внутри восстановленного контекста
  context.with(ctx, () => processMessage(msg.body));
}
  • Слишком детальные атрибуты с высокой изменчивостью (в логах/спанах): взрываете объём и счета. Решение: ограничивайте кардинальность, не пишите целиком большие запросы/пейлоады.
  • Нет обучения команды. Инструмент есть, пользуются единицы. Решение: 1–2 внутренних воркшопа и короткие «шпаргалки».

Что показать бизнесу: KPI и дашборды

  • MTTR (среднее время восстановления) до/после: цель — минус 30–50%.
  • p95 задержки ключевых пользовательских путей: оформление заказа, авторизация, оплата.
  • Доля ошибок по сервисам и внешним интеграциям: видно, кто «роняет» цепочку.
  • Воронка по бизнес‑спанам: на каком шаге теряем больше всего времени.

Мини‑дашборд в UI трейсера: «топ медленных маршрутов», «топ ошибок по зависимостям», «сравнение версий релизов по задержке».

Расширение: очереди, задачи, фоновые джобы

OpenTelemetry поддерживает контекст и для фоновых задач. Ключ — явная инъекция/извлечение контекста в заголовки сообщения (пример выше). Для планировщиков (cron/джобы) задавайте родительский спан вручную и связывайте шаги (скачал → обработал → сохранил → отправил уведомление). Это позволит видеть конец‑в‑конец и фоновые цепочки, которые часто скрыты от глаз.

Бонус для очередей с повторной доставкой: добавьте атрибуты попытки и причину ретрая — так вы быстрее распознаете зацикливание и неправильные пороги таймаутов.

Итоги

OpenTelemetry даёт сквозную наблюдаемость без привязки к одному вендору и без «чёрных ящиков». С относительно небольшими усилиями вы получаете:

  • быстрый разбор инцидентов и сокращение MTTR;
  • прозрачность в зависимостях между сервисами и внешними партнёрами;
  • контролируемые расходы за счёт умного сэмплирования и фильтрации полей.

Начните с пилота на одном сервисе, добавьте хвостовое сэмплирование и привяжите логи к trace_id. Через квартал у вас будет система, которая окупается за счёт экономии времени инженеров и более стабильных релизов.


наблюдаемостьOpenTelemetryтрассировкалоги