OpenTelemetry перерос фазу «вот спецификация, дальше сами». Сегодня это стандарт де-факто для телеметрии, а его центральная деталь — Collector: один процесс между вашими приложениями и бэкендом для метрик, трейсов и логов. Он снимает вендор-локин и даёт control plane над сигналами. Разберёмся, как он устроен, и соберём минимальную конфигурацию, которую не стыдно отправить в прод.
Содержание
Открыть содержание
- Зачем вообще нужен Collector
- receivers, processors, exporters — три слова
- Что коллектор даёт на самом деле
- Agent против Gateway
- Semantic conventions: имена важнее значений
- Три сигнала, один процесс
- Сравнение в одной таблице
- Что нужно, чтобы собрать минимальный setup
- Как проверить, что всё работает
- Ловушки
- От contrib к своему дистрибутиву
- Итог
Зачем вообще нужен Collector
Можно ведь слать телеметрию из приложения прямо в бэкенд — зачем лишний процесс посередине? Затем, что без него каждое приложение жёстко привязано к конкретному вендору: эндпоинт, формат, ключ API зашиты в код. Решили сменить Datadog на Grafana — переката́йте все сервисы. Хотите резать чувствительные поля или семплировать трейсы — делайте это в каждом приложении отдельно.
Collector разрывает эту связь. Приложение знает только один адрес — локальный коллектор — и один протокол, OTLP. Всё остальное (куда слать, что отфильтровать, как батчить и семплировать) решается в конфиге коллектора, а не в коде. Это тот же принцип, что и обратный прокси для HTTP: маленький процесс посередине, который даёт точку контроля.
Есть и более приземлённые причины. Без коллектора каждый сервис сам отвечает за ретраи, буферизацию и поведение при недоступном бэкенде — а это значит, что при сбое наблюдаемости вы рискуете уронить и само приложение, забившее очередь неотправленной телеметрии. Коллектор берёт эту грязную работу на себя: приложение делает быстрый локальный вызов и забывает, а надёжная доставка, ретраи и сглаживание всплесков — забота отдельного процесса, который не делит судьбу с бизнес-логикой.
receivers, processors, exporters — три слова
Вся работа коллектора описывается тремя видами компонентов, и, поняв их, вы поняли OTel Collector целиком.
- receivers — как сигнал попадает внутрь. Чаще всего это OTLP-приёмник (gRPC или HTTP), но бывают и приёмники под Prometheus, Kafka, хост-метрики.
- processors — что с сигналом делать по дороге: батчить, семплировать, вырезать чувствительные атрибуты, добавлять метаданные, ограничивать память.
- exporters — куда отправить наружу: OTLP в Tempo/Grafana, в Prometheus, в вендорский бэкенд.
Эти компоненты собираются в pipeline — отдельный для каждого типа сигнала: один для traces, один для metrics, один для logs. Внутри pipeline сигнал идёт строго receivers → processors → exporters.
Что коллектор даёт на самом деле
Четыре вещи, ради которых его и ставят:
- Decoupling. Приложения не знают про вендора. Смена бэкенда — это правка одного exporter-а в одном конфиге, а не релиз всех сервисов.
- Batching. Коллектор копит сигналы и отправляет пачками. Меньше сетевых запросов, меньше нагрузка на бэкенд, ниже стоимость.
- Sampling. Хранить 100% трейсов дорого и не нужно. Коллектор может оставлять, скажем, все ошибочные и медленные трейсы и процент от остальных — это серьёзно режет счёт за наблюдаемость.
- Redaction. Чувствительные данные (токены, e-mail, номера) вырезаются централизованно, до того как уедут к вендору. Один процессор вместо ревизии кода всех команд.
Agent против Gateway
Коллектор разворачивают в двух топологиях, и обычно — в обеих сразу.
Agent — коллектор рядом с приложением, как правило DaemonSet на каждой ноде. Он принимает сигналы локально (низкая задержка), добавляет хост- и kubernetes-метаданные и снимает с приложения заботу о ретраях.
Gateway — центральный коллектор (Deployment с автоскейлом), куда агенты пересылают уже собранное. На нём удобно делать тяжёлые вещи: tail-based sampling по всему трейсу, дедупликацию, единый выход наружу.
Маленькому проекту хватает одного коллектора, но даже тогда стоит держать в голове это разделение: оно подсказывает, какие процессоры где должны жить.
Semantic conventions: имена важнее значений
Отдельный, но критичный момент: OTel задаёт semantic conventions — стандартные имена атрибутов. HTTP-метод называется http.request.method, а не method, verb или httpMethod на вкус каждой команды. Это скучно ровно до того момента, как вы захотите построить один дашборд по всем сервисам или сравнить латентность двух приложений. Если имена разъезжаются, никакой запрос не сошьёт данные вместе. Поэтому значение телеметрии во многом определяется дисциплиной именования — и коллектор помогает её держать, нормализуя атрибуты процессорами.
Три сигнала, один процесс
Большая ценность коллектора в том, что метрики, трейсы и логи проходят через одну и ту же абстракцию. Добавить метрики к нашему трейс-pipeline — это ещё несколько строк в service.pipelines:
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [otlp/tempo]
metrics:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [prometheus]
Логика та же — меняется только exporter под бэкенд конкретного сигнала. Это и есть смысл «единого control plane»: вместо трёх разных агентов (один под метрики, другой под трейсы, третий под логи) у вас один процесс с тремя pipeline-ами и единой политикой батчинга, семплирования и редакции. Когда завтра понадобится перенаправить логи в другое хранилище или добавить вырезание персональных данных во все три сигнала сразу — это правки в одном файле, а не три отдельных проекта.
Важно и обратное: pipeline-ы независимы. Можно агрессивно семплировать трейсы, но хранить 100% метрик; можно резать атрибуты только в логах. Коллектор не навязывает единую политику на всё — он даёт место, где эти политики удобно описать рядом.
Сравнение в одной таблице
| Вендор-агент в каждом поде | OTel Collector | |
|---|---|---|
| Привязка к вендору | жёсткая, в коде | одна строка в конфиге |
| Смена бэкенда | релиз всех сервисов | правка exporter-а |
| Sampling / redaction | в каждом приложении | централизованно |
| Протокол | проприетарный | OTLP (открытый) |
| Батчинг | как повезёт | встроенный |
| Сигналы | часто только свой набор | traces, metrics, logs |
Что нужно, чтобы собрать минимальный setup
Поднимем локально связку из трёх контейнеров: приложение (шлёт OTLP), коллектор и бэкенд для трейсов — возьмём Grafana Tempo. Начнём с конфига коллектора.
# otel-collector.yaml
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
memory_limiter:
check_interval: 1s
limit_percentage: 80 # не дать коллектору съесть всю память
batch:
timeout: 5s # копим 5 секунд и шлём пачкой
exporters:
otlp/tempo:
endpoint: tempo:4317
tls:
insecure: true # локально, в проде — настоящий TLS
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [otlp/tempo]
Конфиг читается как фраза: принять OTLP → ограничить память и сбатчить → отправить в Tempo. Теперь docker-compose, который связывает всё вместе:
services:
tempo:
image: grafana/tempo:latest
command: ["-config.file=/etc/tempo.yaml"]
volumes:
- ./tempo.yaml:/etc/tempo.yaml
otel-collector:
image: otel/opentelemetry-collector-contrib:latest
command: ["--config=/etc/otel-collector.yaml"]
volumes:
- ./otel-collector.yaml:/etc/otel-collector.yaml
ports:
- "4317:4317" # OTLP gRPC
- "4318:4318" # OTLP HTTP
depends_on: [tempo]
app:
image: your-app:latest
environment:
OTEL_EXPORTER_OTLP_ENDPOINT: "http://otel-collector:4317"
OTEL_SERVICE_NAME: "demo-app"
depends_on: [otel-collector]
Приложению достаточно двух переменных окружения — куда слать и как называться. Никаких вендорских SDK: большинство языков имеют OTel-инструментирование, которое читает OTEL_EXPORTER_OTLP_ENDPOINT само.
Берём именно opentelemetry-collector-contrib, а не базовый дистрибутив: в contrib собраны почти все receivers/processors/exporters, включая вендорские. Для прода потом можно собрать свой урезанный дистрибутив только из нужных компонентов.
Как проверить, что всё работает
Поднимаем стек и смотрим логи коллектора — он на старте печатает, какие pipeline собрались:
docker compose up -d
docker compose logs otel-collector | grep -i "Everything is ready"
Дальше прогоняем через приложение пару запросов и проверяем, что трейсы доехали до Tempo. Удобнее всего — добавить в коллектор временный отладочный exporter и увидеть сигналы прямо в логах:
exporters:
debug:
verbosity: detailed
# и добавить debug в exporters нужного pipeline
Если в логах коллектора видны спаны с вашим service.name=demo-app, а в Tempo находится трейс по этому имени — связка работает. После проверки debug-exporter убираем, чтобы не засорять логи в проде.
Ловушки
- Без
memory_limiterколлектор падает под нагрузкой. При всплеске телеметрии он раздувается по памяти и его убивает OOM.memory_limiterдолжен быть первым процессором в pipeline. batch— это не опция, а норма. Без батчинга вы шлёте каждый спан отдельным запросом: дорого и для сети, и для бэкенда. Батч-процессор нужен почти всегда.latestв проде. Удобно для демо, но в прод пиньте конкретную версию образа коллектора — поведение между релизами меняется.- Tail-based sampling и агент. Семплирование «по всему трейсу» корректно работает только там, где виден весь трейс целиком — то есть на gateway, а не на агентах нод. На агенте можно делать только head-based.
- Разъехавшиеся имена атрибутов. Самая коварная — данные есть, а сшить их в дашборд не получается. Держитесь semantic conventions с самого начала.
От contrib к своему дистрибутиву
Для старта opentelemetry-collector-contrib идеален: в нём есть всё, и не нужно думать, какой компонент откуда взять. Но в прод тащить образ со всеми существующими receivers и exporters необязательно — это лишний размер и лишняя поверхность атаки. Когда конфиг устоялся и понятно, какие именно компоненты нужны, имеет смысл собрать свой урезанный дистрибутив через OpenTelemetry Collector Builder (ocb): вы перечисляете в манифесте только нужные модули, и на выходе получаете минимальный бинарник ровно под свой pipeline.
По ресурсам коллектор скромен: один экземпляр-агент обычно укладывается в десятки-сотни мегабайт памяти, а нагрузка масштабируется батчингом и горизонтально (gateway за HPA). Главное — не забыть memory_limiter: именно он превращает «коллектор иногда падает под всплеском» в «коллектор корректно подтормаживает приём и выживает».
Итог
OTel Collector — это «nginx для телеметрии»: маленький процесс посередине, который снимает вендор-локин и даёт единую точку контроля над метриками, трейсами и логами. Минимальный боевой setup — это буквально один OTLP-приёмник, два процессора (memory_limiter + batch) и один exporter. С него стоит начинать любую новую систему наблюдаемости: тянуть проприетарный агент в каждый под в 2026-м — анахронизм, а перейти на открытый протокол позже будет в разы дороже, чем заложить его сразу.