中文翻译暂不可用,显示俄语原文。

Настроить distributed tracing через OpenTelemetry

ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Настроить distributed tracing через OpenTelemetry

1. Цель задачи

Разработать и развернуть систему распределённого трассирования (distributed tracing) для многокомпонентной агентной системы с использованием OpenTelemetry. В результате инженер получит визуализацию потоков выполнения меж-агентного взаимодействия, сможет измерять latency отдельных вызовов и отслеживать ошибки. Ключевой результат работающий дашборд в Grafana, который показывает задержки (latency) и частоту ошибок (errors) для ключевых операций агентов.


2. Исходные данные

Что нужноОткуда взять
Агентная система (рабочая или тестовая)Собранный пет-проект (например, Multi-Agent Assistant на LangChain/AutoGen) или существующая система с вызовами между агентами
OpenTelemetry SDK (Python)pip install opentelemetry-api opentelemetry-sdk opentelemetry-exporter-jaeger
Jaeger (хранилище трейсов и UI)Docker-образ jaegertracing/all-in-one:1.60
Grafana (визуализация)Docker-образ grafana/grafana:latest
OpenTelemetry Collector (опционально)otel/opentelemetry-collector-contrib:0.108.0 (может быть пропущен)
Готовый дашборд для Jaeger в GrafanaJSON-экспорт из дашбордов сообщества или создание с нуля

Если нет реального инструмента — симулируем:

  1. Создать простой агент на Python, который обращается к LLM (например, через dummy-функцию, возвращающую фиксированный ответ) и выполняет цепочку вызовов нескольких суб-агентов.
  2. Каждый вызов обёрнуть в span с помощью OpenTelemetry.
  3. Запустить Jaeger и Grafana на локальной машине через Docker Compose.

3. Технологический стек

КомпонентИнструментыНазначение
Язык программированияPython 3.11+Реализация агента
OpenTelemetry SDKopentelemetry-api, opentelemetry-sdk, opentelemetry-exporter-jaegerИнструментирование кода, создание и экспорт трейсов
JaegerJaeger All-In-One (Docker)Приём, хранение и визуализация трейсов
GrafanaGrafana (Docker)Построение дашбордов с latency и errors
КонтейнеризацияDocker + Docker ComposeЗапуск Jaeger и Grafana
LLM (симуляция)Fake LLM-класс (возвращает задержку и ошибку с вероятностью)Имитация реальных вызовов
Python-библиотекиrandom, time, uvicorn (если веб-сервер), httpxГенерация трейсов и HTTP-транспорт

4. Этапы выполнения

Этап 1: Подготовка окружения (30-40 минут)

Действия

  1. Создать директорию проекта distributed-tracing-lab и инициализировать Poetry / requirements.txt.
  2. Установить зависимости
    pip install opentelemetry-api opentelemetry-sdk opentelemetry-exporter-jaeger
    
  3. Подготовить Docker Compose для Jaeger и Grafana:
    version: '3.8'
    services:
      jaeger:
        image: jaegertracing/all-in-one:1.60
        ports:
          - "16686:16686"   # UI
          - "4317:4317"     # OTLP gRPC
          - "4318:4318"     # OTLP HTTP
        environment:
          - COLLECTOR_OTLP_ENABLED=true
    
      grafana:
        image: grafana/grafana:latest
        ports:
          - "3000:3000"
        environment:
          - GF_AUTH_ANONYMOUS_ENABLED=true
          - GF_INSTALL_PLUGINS=jaeger-datasource
    
  4. Запустить контейнеры docker compose up -d

Ожидаемый результат этапа Jaeger UI доступен на http://localhost:16686, Grafana UI на http://localhost:3000. В Grafana установлен плагин Jaeger.


Этап 2: Инструментирование агентной системы (1–1.5 часа)

Действия

  1. Создать Tracer Provider в корневом модуле tracing.py:

    from opentelemetry import trace
    from opentelemetry.exporter.jaeger.thrift import JaegerExporter
    from opentelemetry.sdk.resources import SERVICE_NAME, Resource
    from opentelemetry.sdk.trace import TracerProvider
    from opentelemetry.sdk.trace.export import BatchSpanProcessor
    
    resource = Resource.create({SERVICE_NAME: "multi-agent-system"})
    provider = TracerProvider(resource=resource)
    processor = BatchSpanProcessor(JaegerExporter(
        agent_host_name="localhost",
        agent_port=6831,
    ))
    provider.add_span_processor(processor)
    trace.set_tracer_provider(provider)
    tracer = trace.get_tracer(__name__)
    
  2. Написать агент с несколькими этапами:

    import time
    import random
    
    class Agent:
        def __init__(self, name, delay_range=(0.1, 0.5)):
            self.name = name
            self.delay_range = delay_range
    
        def process(self, input_data):
            with tracer.start_as_current_span(f"{self.name}.process") as span:
                span.set_attribute("agent.name", self.name)
                time.sleep(random.uniform(*self.delay_range))
                if random.random() < 0.1:  # 10% error
                    span.set_status(trace.Status(trace.StatusCode.ERROR))
                    raise RuntimeError(f"{self.name} failed")
                return f"{self.name} processed: {input_data}"
    
  3. Создать главный оркестратор main.py, который вызывает нескольких агентов последовательно:

    from tracing import tracer
    from agent import Agent
    
    def orchestrate():
        with tracer.start_as_current_span("orchestrate") as span:
            agent_a = Agent("AgentA", delay_range=(0.2, 0.4))
            agent_b = Agent("AgentB", delay_range=(0.3, 0.6))
            # Последовательный вызов
            span.add_event("Starting AgentA")
            result_a = agent_a.process("Task1")
            span.add_event("Starting AgentB")
            result_b = agent_b.process(result_a)
            span.set_attribute("result", result_b)
            return result_b
    
    if __name__ == "__main__":
        import random
        random.seed(42)
        orchestrate()
    
  4. Запустить скрипт несколько раз для генерации трейсов: python main.py

Ожидаемый результат этапа В Jaeger UI появляются трейсы с иерархией спанов orchestrate → AgentA.process → AgentB.process. Видны задержки и метки ошибок.


Этап 3: Подключение к Grafana (30–40 минут)

Действия

  1. Войти в Grafana (login: admin/admin, при первом входе пропустить смену пароля или указать свои).
  2. Добавить источник данных Jaeger
    • Нажать Configuration → Data sources → Add data source.
    • Выбрать Jaeger.
    • URL: http://jaeger:16686 (внутри Docker Compose) или http://localhost:16686 (снаружи).
    • Сохранить и проверить («Save & test»).
  3. Установить дашборд (опционально): импортировать готовый шаблон 13323 (Jaeger System Dashboard) из сообщества.
  4. Создать простой дашборд вручную
    • Нажать Create → DashboardAdd new panel.
    • В Query выбрать источник Jaeger.
    • Использовать встроенный редактор для отображения Traces (с фильтром по service multi-agent-system).

Ожидаемый результат этапа В Grafana отображаются трейсы, можно просматривать отдельные spans.


Этап 4: Построение дашборда latency и errors (45–60 минут)

Действия

  1. Добавить панель «Latency»:

    • Тип: Time series.
    • Query: {service_name="multi-agent-system"} → метрика trace:span:duration_seconds (через Prometheus style, если настроен OTel Collector) или используйте встроенные переменные Grafana для Jaeger. Примечание: для метрик latency проще всего использовать экспорт в Prometheus через OTel Collector. На этом этапе допустимо использовать Jaeger’s built-in metrics (Grafana плагин Jaeger поддерживает метрики из трейсов).
    • Альтернативно: добавить панель Trace list, отфильтрованную по service, и визуализировать среднее время выполнения.
  2. Добавить панель «Error rate»:

    • Используйте Jaeger query: {service_name="multi-agent-system", status="error"} или через переменную status в дашборде.
    • Формула: count(status=error) / count(status=ok).
  3. Создать переменные дашборда (опционально):

    • service (тип: query, источник Jaeger) — для выбора сервисов.
    • operation — для фильтрации по конкретным операциям (агентам).
  4. Настроить временной диапазон (последние 15 минут или 1 час).

  5. Сохранить дашборд с именем «Multi-Agent Tracing».

Ожидаемый результат этапа Дашборд показывает:

  • График latency по спанам с процентилями (p50, p95, p99).
  • График error rate в процентах или absolute count.
  • Возможность drill-down до конкретного трейса.

Этап 5: Тестирование и верификация (20–30 минут)

Действия

  1. Запустить скрипт многократно (например, 50 раз) с разными seed, чтобы накопить данные.
  2. Проверить Grafana-дашборд визуально убедиться, что latency колеблется, ошибки видны (около 10%).
  3. Проверить Jaeger UI найти трейс с ошибкой, посмотреть атрибуты спана.
  4. Экспортировать дашборд (JSON) в файл grafana-dashboard.json для сохранения в репозитории.
  5. Закоммитить код, Docker Compose и дашборд в Git.

Ожидаемый результат этапа Полная воспроизводимость: docker compose up + python main.py даёт трейсы, дашборд отображается без дополнительных настроек.


5. Критерии приемки (Definition of Done)

  • В Jaeger UI видны трейсы с иерархией спанов от оркестратора к суб-агентам.
  • Каждый span содержит атрибуты (имя агента, результат, статус).
  • Ошибочные спаны помечены статусом ERROR и отображаются в Jaeger.
  • В Grafana настроен источник данных Jaeger.
  • Создан дашборд с панелями «Latency» (хотя бы p95) и «Error rate».
  • Дашборд экспортирован в JSON и добавлен в репозиторий.
  • Docker Compose поднимает оба сервиса (Jaeger, Grafana) без ошибок.
  • Код агента содержит инструкции по инструментированию (обёртка span).
  • При запуске main.py трейсы отправляются в Jaeger без потерь (все вызовы зафиксированы).

6. Ожидаемый результат

Основные артефакты

  • tracing.py — конфигурация TracerProvider и экспортёра.
  • agent.py — класс агента с созданием вложенных спанов.
  • main.py — оркестратор, запускающий цепочку вызовов.
  • docker-compose.yml — описание Jaeger + Grafana.
  • grafana-dashboard.json — экспорт дашборда.

Содержимое дашборда панели latency (гистограмма/график) и error rate (процент/число) с возможностью фильтрации по сервису и операции.
Дополнительно инструкция README.md с шагами запуска.


7. Возможные сложности и их решение

СложностьРешение
Jaeger не принимает трейсы (ошибка подключения)Проверить порты в Docker Compose, использовать gRPC (4317) вместо Thrift compact. В JaegerExporter указать agent_host_name="localhost" и agent_port=6831 (Thrift UDP) или collector_endpoint="http://localhost:14268/api/traces" (HTTP).
Трейсы не отображаются в Jaeger UIУбедиться, что resource содержит правильный SERVICE_NAME. Проверить, что BatchSpanProcessor успешно передаёт данные. Увеличить BATCH_TIMEOUT (по умолч. 5 сек) или вызвать provider.force_flush().
Grafana не может подключиться к JaegerИспользовать имя контейнера jaeger внутри Docker Compose; указать URL http://jaeger:16686. При запуске Grafana с хоста — http://localhost:16686. Возможно, требуется отключить авторизацию (уже сделано через GF_AUTH_ANONYMOUS_ENABLED=true).
Панели latency/errors пустыУбедиться, что плагин Jaeger установлен. Для метрик можно использовать встроенный редактор «TraceQL» или переключиться на Prometheus метрики через OpenTelemetry Collector (дополнительная настройка).
Трейсы с ошибками не маркируются как ERRORЯвно вызывать span.set_status(trace.Status(StatusCode.ERROR)) в блоке except. Учесть, что исключение может быть перехвачено на верхнем уровне.
В дашборде отображаются только последние трейсыНастроить временной диапазон дашборда на «last 5 minutes» или увеличить количество прогонов.

8. Бюджет времени (оценка)

ЭтапВремя
Этап 1: Подготовка окружения30-40 мин
Этап 2: Инструментирование60-90 мин
Этап 3: Подключение к Grafana30-40 мин
Этап 4: Дашборд latency и errors45-60 мин
Этап 5: Тестирование и верификация20-30 мин
Итого3-4 часа

Примечание Для первого раза может потребоваться 4-5 часов, особенно при отладке сетевых настроек Jaeger-Grafana. Рекомендуется выполнять задачу в один подход или разбить на 2 сессии.


9. Связанные вопросы из базы знаний

ВопросТема
1Основы OpenTelemetry (spans, traces, context propagation)
5Настройка Jaeger All-In-One
89Инструментирование Python-приложений с OpenTelemetry
234Объединение трейсов из нескольких микросервисов
511Best practices для naming spans
613Связывание логов и трейсов через trace_id
722Dashboards as Code (Grafana JSON-модели)
801Мониторинг latency с процентилями (p50, p95, p99)
855Использование OpenTelemetry Collector как proxy
900Troubleshooting: трейсы не отображаются

10. Чек-лист самопроверки

  • Я проверил, что Jaeger UI показывает хотя бы один трейс с моим service name.
  • Я убедился, что при возникновении exception span помечается как ERROR.
  • Я добавил в дашборд график latency с p95 и error rate.
  • Я экспортировал дашборд в JSON и сохранил рядом с docker-compose.yml.
  • Я запустил python main.py не менее 5 раз после настройки дашборда и убедился, что данные обновляются.