Перейти к содержимому
Hogin Hogin
Назад

OpenTelemetry Collector: минимальный setup, который не стыдно в прод

8 мин чтения

OpenTelemetry перерос фазу «вот спецификация, дальше сами». Сегодня это стандарт де-факто для телеметрии, а его центральная деталь — Collector: один процесс между вашими приложениями и бэкендом для метрик, трейсов и логов. Он снимает вендор-локин и даёт control plane над сигналами. Разберёмся, как он устроен, и соберём минимальную конфигурацию, которую не стыдно отправить в прод.

Содержание

Открыть содержание

Зачем вообще нужен Collector

Можно ведь слать телеметрию из приложения прямо в бэкенд — зачем лишний процесс посередине? Затем, что без него каждое приложение жёстко привязано к конкретному вендору: эндпоинт, формат, ключ API зашиты в код. Решили сменить Datadog на Grafana — переката́йте все сервисы. Хотите резать чувствительные поля или семплировать трейсы — делайте это в каждом приложении отдельно.

Collector разрывает эту связь. Приложение знает только один адрес — локальный коллектор — и один протокол, OTLP. Всё остальное (куда слать, что отфильтровать, как батчить и семплировать) решается в конфиге коллектора, а не в коде. Это тот же принцип, что и обратный прокси для HTTP: маленький процесс посередине, который даёт точку контроля.

Есть и более приземлённые причины. Без коллектора каждый сервис сам отвечает за ретраи, буферизацию и поведение при недоступном бэкенде — а это значит, что при сбое наблюдаемости вы рискуете уронить и само приложение, забившее очередь неотправленной телеметрии. Коллектор берёт эту грязную работу на себя: приложение делает быстрый локальный вызов и забывает, а надёжная доставка, ретраи и сглаживание всплесков — забота отдельного процесса, который не делит судьбу с бизнес-логикой.

receivers, processors, exporters — три слова

Вся работа коллектора описывается тремя видами компонентов, и, поняв их, вы поняли OTel Collector целиком.

Эти компоненты собираются в pipeline — отдельный для каждого типа сигнала: один для traces, один для metrics, один для logs. Внутри pipeline сигнал идёт строго receivers → processors → exporters.

Pipeline коллектора: receivers, processors, exporters

Что коллектор даёт на самом деле

Четыре вещи, ради которых его и ставят:

Agent против Gateway

Коллектор разворачивают в двух топологиях, и обычно — в обеих сразу.

Agent — коллектор рядом с приложением, как правило DaemonSet на каждой ноде. Он принимает сигналы локально (низкая задержка), добавляет хост- и kubernetes-метаданные и снимает с приложения заботу о ретраях.

Gateway — центральный коллектор (Deployment с автоскейлом), куда агенты пересылают уже собранное. На нём удобно делать тяжёлые вещи: tail-based sampling по всему трейсу, дедупликацию, единый выход наружу.

Agent против Gateway

Маленькому проекту хватает одного коллектора, но даже тогда стоит держать в голове это разделение: оно подсказывает, какие процессоры где должны жить.

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 убираем, чтобы не засорять логи в проде.

Ловушки

От 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-м — анахронизм, а перейти на открытый протокол позже будет в разы дороже, чем заложить его сразу.


Поделиться:

Предыдущая статья
Flux за вечер: GitOps для одного небольшого кластера
Следующая статья
eBPF без боли: Cilium и сетевая наблюдаемость в Kubernetes