2. OpenTelemetry
Архитектура унифицированного стандарта телеметрии
«Стандарты замечательны тем, что их существует великое множество на любой вкус».
— Эндрю Таненбаум
В предыдущей статье мы увидели, как распределенная трассировка собирает воедино разорванные сетевыми вызовами цепочки событий. Однако на заре развития микросервисов индустрия погрузилась в хаос несовместимых стандартов: одна библиотека поддерживала OpenTracing, другая требовала OpenCensus от Google, а проприетарные APM-системы (Datadog, New Relic, Dynatrace) навязывали собственные закрытые агенты и форматы.
В 2019 году под эгидой Cloud Native Computing Foundation (CNCF) произошло историческое объединение OpenTracing и OpenCensus в единый проект — OpenTelemetry (OTel).
Сегодня OpenTelemetry — это абсолютный промышленный стандарт второго уровня после Kubernetes по активности сообщества. Для разработчика на Go это универсальный интерфейс и библиотека, позволяющая инструментировать код один раз и отправлять три ключевых столпа телеметрии (метрики, логи и трейсы) в любую выбранную систему хранения без вендор-лока (vendor lock-in).
Единый стандарт телеметрии
Главная цель OpenTelemetry — навсегда разделить процесс инструментирования кода и процесс транспортировки данных.
Код вашего Go-сервиса не должен знать, куда улетают метрики — в локальный Prometheus, в облачный Google Cloud Monitoring или в VictoriaMetrics. Он не должен знать, кто визуализирует трейсы — открытый Jaeger, Grafana Tempo или коммерческий Dynatrace. Приложение просто генерирует события в рамках стандартизированного API.
Архитектура OpenTelemetry
Архитектура OTel построена по принципу строгой модульности и разделения ответственности:
flowchart TD
subgraph A ["Ваше приложение"]
A1["API<br>Трассировка/Метрики/Логи"]
end
subgraph B ["SDK OpenTelemetry"]
direction TB
B1["TracerProvider"]
B2["MeterProvider"]
B3["LoggerProvider"]
B4["Processors<br>Batch/Simple"]
B5["Exporters<br>OTLP/Stdout"]
end
subgraph C ["Infrastructure"]
C1["OTel Collector"]
end
subgraph D ["Backend"]
D1["Jaeger / Tempo"]
D2["Prometheus"]
D3["Loki"]
end
A -->|"Вызовы API"| B
B -->|"Данные"| B4
B4 -->|"Трансформация"| B5
B5 -->|"OTLP Protocol"| C1
C1 -->|"Маршрутизация"| D1
C1 -->|"Маршрутизация"| D2
style A fill:#1e293b,stroke:#38bdf8,stroke-width:2px,color:#fff
style B fill:#312e81,stroke:#818cf8,stroke-width:2px,color:#fff
style C fill:#78350f,stroke:#f59e0b,stroke-width:2px,color:#fff
style D fill:#0f172a,stroke:#475569,color:#fff
1. API и SDK
Архитектура четко разделена на два слоя:
- API (
go.opentelemetry.io/otel): Это тонкие интерфейсы, которые вы используете в бизнес-коде (Tracer,Meter). Они содержат нулевую реализацию (no-op) по умолчанию. Если приложение не зарегистрировало провайдер, вызовы API мгновенно завершаются без каких-либо накладных расходов. - SDK: Это конкретная реализация. Она управляет сэмплированием (sampling), буферизацией и отправкой данных.
Библиотека баз данных может объявить tracer.Start(ctx, "db.query"), не заставляя приложение тащить за собой десятки мегабайт зависимостей Jaeger или gRPC-клиентов.
2. OTLP (OpenTelemetry Protocol)
OTel представил OTLP — бинарный протокол на основе Protocol Buffers (gRPC или HTTP/2).
- Производительность: Binary формат кодируется и декодируется быстрее JSON.
- Универсальность: Один формат для Метрик, Логов и Трейсов.
В отличие от устаревших текстовых форматов или JSON, бинарный Protobuf в OTLP минимизирует объем передаваемого по сети трафика и не требует тяжелых парсеров с обилием строковых аллокаций.
Under the Hood: Как это работает в Go
Разберем путь создания спана в Go-приложении.
1. TracerProvider
Это корневой объект. Он хранит конфигурацию и создает Tracer'ы.
В Go его принято создавать при старте приложения и закрывать (Shutdown) при graceful shutdown. Shutdown критически важен: он сбрасывает (flush) буферы процессоров, чтобы не потерять последние данные.
tp := trace.NewTracerProvider(
trace.WithBatcher(exporter),
trace.WithResource(res),
)
defer func() {
if err := tp.Shutdown(context.Background()); err != nil {
log.Printf("Error shutting down tracer provider: %v", err)
}
}()
otel.SetTracerProvider(tp)
2. SpanProcessor
Это механизм обработки спанов.
- BatchSpanProcessor (Рекомендуемый): Собирает спаны в пачки (batch) и отправляет их раз в N секунд или при достижении лимита размера.
- Почему? Сетевые вызовы дороги. Отправлять каждый спан по сети — убить производительность. Батчинг экономит CPU и сеть. Внутри используется кольцевой буфер и фоновая горутина-сборщик.
- SimpleSpanProcessor: Отправляет спан сразу после завершения. Используется только для отладки или в короткоживущих CLI-утилитах.
3. Exporter
Отвечает за отправку данных в конкретный бэкенд.
Чаще всего используют OTLP Exporter, который шлет данные в OpenTelemetry Collector.
OpenTelemetry Collector
В продакшене приложение не должно знать адреса Jaeger, Prometheus или Loki. Оно должно знать только адрес otel-collector:4317.
Collector — это промежуточный прокси-сервис (написан на Go), который принимает данные в формате OTLP и маршрутизирует их.
Возможности Collector:
- Рецепшн (Receivers): Принимает данные (OTLP, Jaeger, Zipkin, Prometheus).
- Процессоры (Processors): Обрабатывает данные на лету.
memory_limiter: Защищает коллектор от OOM.batch: Повторный батчинг.attributes: Добавление/удаление атрибутов (например, удалить sensitive данные).
- Экспортеры (Exporters): Отправляет в бэкенды (Jaeger, ClickHouse, S3).
Semantic Conventions (Семантические соглашения)
OpenTelemetry ввел стандарты именования атрибутов. Это делает трейсы универсальными для всех инструментов.
http.method: "GET"db.system: "postgresql"db.statement: "SELECT * FROM users"net.peer.name: "database-server"
Если вы назовете атрибут my_custom_http_method, бэкенд (например, Grafana Tempo) не поймет, что это HTTP-метод, и не сможет построить правильные дашборды.
Итог
OpenTelemetry — это "USB" для телеметрии.
- Единый API: Вы пишете код под один интерфейс, а бэкенд меняется через конфиг.
- OTLP: Эффективный бинарный протокол.
- Collector: Централизованная точка управления потоком данных и их обработки.
- Semantic Conventions: Словарь общего языка для мониторинга.
В следующей статье мы детально рассмотрим строительные блоки трейса — Спаны и Трейсы, и как правильно с ними работать в Go: 3. Спаны и трейсы.