OpenTelemetry помогает увидеть, что происходит внутри приложения и на сервере. Программа отправляет в одно место трассы запросов, метрики и записи журнала, а оттуда их можно передать в любое хранилище и рассмотреть на временных шкалах и графиках. Главный посредник в этой схеме называется OpenTelemetry Collector, и именно его мы поднимем на VPS. В этой статье мы запустим Collector и Jaeger через Docker Compose, отправим тестовые данные, подключим приложение на Python без правки его кода, откроем трассу в браузере и добавим метрики самого сервера. Все шаги мы повторили на тестовом сервере с Debian 12, версия Collector на момент проверки 0.163.1, версия Jaeger 2.22.0.
Как устроен OpenTelemetry
Пока приложение состоит из одного процесса, разобраться в его поведении обычно помогает журнал. Но когда запрос проходит через несколько сервисов, например через веб-приложение, очередь и базу данных, журнала перестаёт хватать. Хочется увидеть путь конкретного запроса и узнать, сколько времени он провёл на каждом шаге, а заодно понять, не упирается ли сам сервер в память или диск. Данные, которые программы и серверы выдают о собственной работе, называют телеметрией. OpenTelemetry, или сокращённо OTel, – это открытый набор спецификаций, программных интерфейсов и инструментов, который описывает, как телеметрию создавать, передавать и собирать. Он не привязан к конкретному поставщику, поэтому одни и те же данные можно отправить и в Jaeger, и в Prometheus, и в коммерческий сервис. При этом стоит сразу увидеть границу. По определению из официальной документации, OpenTelemetry не является системой наблюдаемости, то есть сам он данные не хранит и графики не рисует, а хранение и показ оставляет другим программам.
Телеметрия делится на сигналы трёх основных видов. Трасса (по-английски trace, в разговоре её часто называют трейсом) описывает путь одного запроса через приложение. Она состоит из спанов, и каждый спан представляет отдельную единицу работы внутри запроса, например обработку HTTP-запроса или обращение к базе данных. У спана есть имя, время начала и конца, атрибуты и идентификаторы. Все спаны одной трассы несут общий идентификатор трассы, а у вложенного спана указан идентификатор родителя, и именно по этим связям Jaeger потом рисует дерево вызовов, которое мы увидим в шаге 5. Метрика – это измерение, снятое во время работы, например занятая память или число запросов. В отличие от трассы она описывает поведение системы в целом, а не одного запроса, и поэтому хорошо подходит для графиков и оповещений. Лог – это запись о событии. Чтобы не заставлять отказываться от привычных библиотек журналирования, OpenTelemetry подключается к ним, и когда в приложении включён SDK или автоматическое инструментирование, к записям журнала сами добавляются идентификаторы активной трассы и спана. Благодаря этому строку журнала можно связать с запросом, в ходе которого она появилась.
Теперь посмотрим, как данные покидают приложение. В самом коде используется API, который задаёт, как создавать телеметрию, а за его реализацию на конкретном языке отвечает SDK. SDK собирает созданные спаны и метрики, обрабатывает их и экспортирует. Писать трассировку вручную для каждого фреймворка не приходится, потому что существуют библиотеки инструментирования, которые сами создают спаны для запросов популярных библиотек, а в языках, где это поддерживается, их подключают вообще без правки кода. Именно так мы поступим с приложением на Flask. Данные SDK отправляет по протоколу OTLP, и у него два варианта передачи. OTLP/gRPC по умолчанию использует порт 4317, а OTLP/HTTP порт 4318, причём по HTTP данные уходят на адреса /v1/traces, /v1/metrics и /v1/logs. Это два разных протокола на двух разных портах, и перепутать их очень легко. В разделе с частыми вопросами мы воспроизвели, какие ошибки при этом появляются.
Приложение можно настроить так, чтобы оно отправляло данные прямо в хранилище, и для знакомства с OpenTelemetry или небольшого стенда это допустимо. Однако в большинстве случаев рядом с сервисами запускают Collector, отдельную программу-посредник, которая принимает телеметрию, обрабатывает её и пересылает дальше. Приложению так проще, ведь оно быстро отдаёт данные и забывает о них, а повторные попытки отправки, группировку в пачки, шифрование и фильтрацию чувствительных данных берёт на себя Collector. Внутри он устроен как набор конвейеров, по одному для каждого сигнала. Конвейер начинается с приёмников (receivers), которые получают данные, например по OTLP или из метрик сервера. Затем идут процессоры (processors), которые меняют данные по пути, например складывают их в пачки. Заканчивается конвейер экспортёрами (exporters), которые отправляют результат в одно или несколько хранилищ. Есть тонкость, о которую спотыкаются новички. Компонент недостаточно описать в своём разделе конфигурации, его нужно ещё перечислить в разделе service, иначе он не будет включён. Мы увидим это в конфигурации шага 2. Collector выпускают в нескольких сборках, и мы возьмём contrib, в которой приёмников намного больше, чем в базовой. В образах версии 0.163.1 приёмников 108 против 7.
Роль Jaeger в схеме
Jaeger – это открытая система для хранения и просмотра трасс. В нашей схеме он играет роль хранилища и веб-интерфейса, а к самому OpenTelemetry не относится. Jaeger 2.22.0 принимает данные по OTLP, в его логе видны строки Starting GRPC server и Starting HTTP server, поэтому Collector будет передавать ему трассы тем же протоколом.
Что понадобится
- Сервер с Debian 12 и не меньше 2 ГБ памяти, например тариф DE-RZ9-1. На нашем сервере Collector занял от 45 до 70 МБ памяти, Jaeger от 20 до 60 МБ, а демонстрационное приложение на Flask около 120 МБ. Образы Docker заняли на диске около 750 МБ, виртуальное окружение Python ещё 67 МБ. Если сервера ещё нет, закажите его по инструкции.
- Подключение по SSH, например через Termius. Команды выполняются от имени
root. - Docker и Docker Compose. Как их поставить, рассказано в отдельной статье. У нас стояли Docker 29.9.2 и Compose 5.6.1.
- Python 3 для демонстрационного приложения. В Debian 12 он уже есть, наша версия 3.11.2.
- Терминал и браузер на вашем компьютере, чтобы открыть Jaeger через SSH-туннель.
Шаг 1. Подготовьте папку и файл Compose
Создайте папку проекта и перейдите в неё.
mkdir -p /opt/otel
cd /opt/otelТеперь создайте файл compose.yaml. Скопируйте блок ниже целиком, вместе со строкой cat в начале и EOF в конце, и вставьте в окно терминала.
cat > compose.yaml <<'EOF'
# Jaeger хранит и показывает трассы, Collector принимает телеметрию
services:
jaeger:
image: cr.jaegertracing.io/jaegertracing/jaeger:2.22.0
container_name: otel-jaeger
restart: unless-stopped
ports:
- "127.0.0.1:16686:16686" # веб-интерфейс Jaeger, только с самого сервера
collector:
image: otel/opentelemetry-collector-contrib:0.163.1
container_name: otel-collector
restart: unless-stopped
volumes:
- ./otel-config.yaml:/etc/otelcol-contrib/config.yaml:ro
ports:
- "127.0.0.1:4317:4317" # OTLP gRPC
- "127.0.0.1:4318:4318" # OTLP HTTP
depends_on:
- jaeger
EOFВ файле описаны два контейнера.
- jaeger хранит трассы и показывает их в веб-интерфейсе. Его порт 16686 опубликован только на внутреннем адресе сервера
127.0.0.1. - collector принимает телеметрию. Его порты 4317 (OTLP/gRPC) и 4318 (OTLP/HTTP) тоже опубликованы только на
127.0.0.1, поэтому приложения на самом сервере до него дотянутся, а из интернета он недоступен.
Файл настроек Collector подключается внутрь контейнера только на чтение, мы создадим его на следующем шаге. Версии образов указаны явно, чтобы после перезапуска вы получили ту же версию, а не новую.
Почему порты только на 127.0.0.1
У Collector из этой статьи нет паролей, и данные от него принимает любой, кто смог подключиться к порту. Официальная документация OpenTelemetry советует публиковать порт Collector в Docker только на 127.0.0.1, если им пользуются программы с того же сервера. Кроме того, брандмауэр UFW порты Docker сам не закрывает, об этом подробно рассказано в статье про Docker. Мы проверили с домашнего компьютера, что соединения с портами 4317, 4318 и 16686 на внешнем адресе сервера не устанавливаются и завершаются по тайм-ауту.
Шаг 2. Напишите конфигурацию Collector
Создайте файл otel-config.yaml тем же способом, скопировав блок целиком.
cat > otel-config.yaml <<'EOF'
receivers:
# Приём телеметрии от приложений по протоколу OTLP
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
# Собирает данные в пачки, так отправка эффективнее
batch:
exporters:
# Трассы уходят в Jaeger
otlp_grpc/jaeger:
endpoint: jaeger:4317
tls:
insecure: true
# Всё, что принял Collector, печатается в его лог
debug:
verbosity: basic
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlp_grpc/jaeger, debug]
metrics:
receivers: [otlp]
processors: [batch]
exporters: [debug]
logs:
receivers: [otlp]
processors: [batch]
exporters: [debug]
EOFРазберём файл по разделам.
- receivers описывает приёмники. Приёмник
otlpждёт данные на портах 4317 (gRPC) и 4318 (HTTP). Адрес0.0.0.0означает, что приём идёт на всех адресах внутри контейнера, а доступ снаружи ограничен тем, как мы опубликовали порты вcompose.yaml. - processors описывает процессоры. Процессор
batchскладывает данные в пачки перед отправкой. - exporters описывает экспортёры. Имя
otlp_grpc/jaegerсостоит из типа и собственного названия после косой черты, так можно держать несколько экспортёров одного типа. Адресjaeger:4317это имя сервиса изcompose.yaml, Docker сам находит контейнер по такому имени. Строкаinsecure: trueотключает шифрование, потому что обмен идёт внутри сети Docker на одном сервере. Экспортёрdebugпечатает всё полученное в лог Collector, режимbasicвыводит по одной строке на пачку данных. - service собирает конвейеры. Для каждого сигнала указано, откуда брать данные, как их обработать и куда отправить. Трассы идут и в Jaeger, и в лог, а метрики и логи пока только в лог.
Не убирайте адрес 0.0.0.0
Если у приёмника не указать endpoint, Collector слушает только внутренний адрес контейнера 127.0.0.1, и опубликованные порты перестают работать. Мы проверили это на тестовом сервере, подробности в разделе с частыми вопросами.
Названия компонентов в старых инструкциях
В версии 0.163.1 экспортёр OTLP по gRPC называется otlp_grpc, а приёмник метрик сервера host_metrics. В старых инструкциях встречаются имена otlp и hostmetrics. Проверка конфигурации принимает и их, но мы используем новые названия.
Шаг 3. Проверьте конфигурацию и запустите
Прежде чем запускать Collector, проверьте файл настроек. Команда ниже запускает одноразовый контейнер Collector с вашим файлом и просто сообщает, верна ли конфигурация.
docker compose run --rm --no-deps collector validate --config=/etc/otelcol-contrib/config.yaml
echo $?Если всё в порядке, команда ничего не напечатает, а echo $? покажет 0. При ошибке в файле вы увидите её текст, примеры есть в разделе с частыми вопросами. Теперь запустите контейнеры.
docker compose up -dПри первом запуске Docker сначала скачает образы. Через несколько секунд посмотрите последнюю строку лога Collector.
docker logs otel-collector 2>&1 | tail -n 1Нужна строка Everything is ready. Begin running and processing data., она означает, что приёмники запущены.

Конфигурация верна, Collector запущен и готов принимать данные
Строки лога Collector длинные, потому что в конце каждой стоит служебный блок с описанием самого Collector. Для нас важно начало строки и числа в конце.
Шаг 4. Отправьте тестовые трассы
Чтобы проверить Collector до подключения настоящего приложения, возьмём утилиту telemetrygen. Она входит в проект OpenTelemetry и умеет генерировать тестовые трассы, метрики и логи. Запустим её в контейнере в сети сервера и отправим три трассы.
docker run --rm --network host \
ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:latest \
traces --otlp-insecure --traces 3 2>&1 | grep -E "generated|export:"--network hostдаёт контейнеру сеть самого сервера, поэтому утилита видит порт127.0.0.1:4317.--otlp-insecureотключает шифрование. У нашего Collector его нет, и без этого параметра утилита попробует подключиться по TLS и получит ошибку.--traces 3задаёт число трасс. Утилита создаёт по одной трассе в секунду, поэтому три трассы уходят около 5 секунд.- Фильтр
grepоставляет только итоговую строку и строки с ошибками отправки. Утилита очень разговорчива.
Строка traces generated означает лишь то, что утилита создала данные. Дошли ли они до Collector, покажет его лог.
docker logs otel-collector 2>&1 | grep Traces | tail -n 2
Collector принял трассы и записал их в лог
В каждой строке Traces указано "spans": 2. Утилита создаёт трассу из двух спанов, а Collector получает трассы по мере появления и печатает по строке на каждую пачку.
Тот же тест можно выполнить и по HTTP. Для этого добавьте параметр --otlp-http и укажите порт 4318.
docker run --rm --network host \
ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:latest \
traces --otlp-http --otlp-insecure --otlp-endpoint localhost:4318 --service telemetrygen-http --traces 2Параметр --service задаёт имя сервиса, под которым трассы появятся в Jaeger, так их легко отличить от первых.
Шаг 5. Откройте Jaeger через SSH-туннель
Веб-интерфейс Jaeger закрыт от интернета, поэтому подключаться к нему нужно через SSH-туннель.
На своём компьютере откройте новое окно терминала и создайте туннель. Замените
IP-адрес-серверана адрес вашего VPS.bashssh -L 16686:127.0.0.1:16686 root@IP-адрес-сервераНе закрывая это окно, откройте в браузере на своём компьютере адрес
http://127.0.0.1:16686.В списке Service выберите
telemetrygenи нажмите Find Traces.
Откроется страница с найденными трассами. Мы отправили три трассы, и все три видны в таблице. У каждой по два спана, а длительность около 123 микросекунд, потому что telemetrygen по умолчанию создаёт очень короткие спаны.

Тестовые трассы из telemetrygen в Jaeger
Нажмите на название любой трассы, и откроется её временная шкала со спанами внутри.
Jaeger хранит трассы только в памяти
В режиме из нашего файла Compose Jaeger не пишет данные на диск. Согласно его документации, это временное хранилище в памяти. Мы проверили это, перезапустив контейнер, после чего прежние трассы исчезли. Постоянное хранение трасс в этой статье не настраивается, для знакомства и отладки хватает памяти.
Шаг 6. Подключите приложение на Python
Тестовые данные хороши для проверки, но настоящая польза начинается, когда телеметрию присылает ваше приложение. Мы возьмём небольшое веб-приложение на Flask и подключим к нему OpenTelemetry без изменений в логике. За основу взят пример из официального руководства OpenTelemetry для Python, к которому мы добавили собственный спан и небольшую паузу, чтобы трасса получилась наглядной.
Установите пакет для виртуальных окружений Python. В Debian 12 он не входит в стандартную поставку.
bashapt install -y python3-venvСоздайте папку приложения и виртуальное окружение, а затем активируйте его.
bashmkdir -p /opt/otel/app cd /opt/otel/app python3 -m venv venv source venv/bin/activateВ начале строки терминала появится пометка
(venv).Установите Flask, OpenTelemetry и экспортёр OTLP. Затем запустите
opentelemetry-bootstrap, он находит в окружении уже установленные библиотеки, например Flask, и ставит для них пакеты инструментирования.bashpip install flask opentelemetry-distro opentelemetry-exporter-otlp opentelemetry-bootstrap -a installНа нашем сервере установились Flask 3.1.3, OpenTelemetry SDK 1.45.1 и opentelemetry-distro 0.66b1.
Создайте файл приложения
app.py.bashcat > app.py <<'EOF' import logging import time from random import randint from flask import Flask, request from opentelemetry import trace app = Flask(__name__) logger = logging.getLogger(__name__) tracer = trace.get_tracer("diceroller.tracer") @app.route("/rolldice") def roll_dice(): player = request.args.get("player", default=None, type=str) result = str(roll()) if player: logger.warning("%s is rolling the dice: %s", player, result) else: logger.warning("Anonymous player is rolling the dice: %s", result) return result def roll(): # Свой участок трассы внутри запроса with tracer.start_as_current_span("roll") as span: time.sleep(0.3) # имитация работы value = randint(1, 6) span.set_attribute("roll.value", value) return value EOFПриложение отвечает на адрес
/rolldiceслучайным числом от 1 до 6. Единственное обращение к OpenTelemetry в нём это спанroll, который мы создали сами, чтобы увидеть вложенный участок. Всё остальное сделает автоматическое инструментирование.Запустите приложение через
opentelemetry-instrument.bashopentelemetry-instrument --service_name dice-server --logs_exporter otlp flask run -p 8000--service_name dice-serverзадаёт имя сервиса в Jaeger.--logs_exporter otlpвключает отправку логов. Трассы и метрики по документации уходят по OTLP/gRPC наlocalhost:4317и без этого параметра.flask run -p 8000обычная команда запуска приложения. Если порт 8000 у вас занят, выберите другой.
Окно останется почти пустым, и это нормально. Записи журнала приложения теперь собирает OpenTelemetry и отправляет в Collector, а в терминал они не выводятся.
Откройте второе окно SSH и обратитесь к приложению несколько раз.
bashfor p in Anna Boris Clara; do curl "http://127.0.0.1:8000/rolldice?player=$p"; echo; doneКаждый ответ задерживается примерно на треть секунды из-за паузы в коде. Через несколько секунд проверьте лог Collector.
bashdocker logs otel-collector 2>&1 | grep -E "Traces|Logs" | tail -n 2

Три запроса дали шесть спанов и записи журнала
Три запроса дали шесть спанов, то есть по два на каждый, и записи журнала приложения. Collector принял все три сигнала.
Теперь вернитесь в Jaeger, обновите страницу, в списке Service выберите dice-server и нажмите Find Traces. Откройте любую трассу GET /rolldice и нажмите на вложенный спан roll.

Запрос и вложенный спан roll, внизу его атрибуты
Верхняя полоса это весь запрос GET /rolldice, его создала библиотека инструментирования Flask. Под ней вложенный спан roll, который мы создали вручную. Он занимает почти всё время запроса, около 300 из 301 миллисекунды, потому что именно там находится наша пауза. В атрибутах спана видно roll.value с выпавшим числом. Так на трассе сразу видно, в какой части запроса уходит время.
Остановите приложение сочетанием Ctrl + C в первом окне.
Отправка по HTTP вместо gRPC
Приложение можно переключить на OTLP/HTTP переменной окружения. Добавьте перед командой запуска OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf, и данные уйдут на порт 4318. Если нужен другой адрес Collector, его задаёт переменная OTEL_EXPORTER_OTLP_ENDPOINT. Мы проверили оба способа, при HTTP сервис появился в Jaeger так же, как и при gRPC.
Шаг 7. Добавьте метрики сервера
Пока Collector принимает данные от приложений, но сам ничего не измеряет. Научим его собирать метрики сервера с помощью приёмника host_metrics. Он умеет снимать загрузку процессора, память, диски и сеть.
Вернитесь в папку проекта.
cd /opt/otelОткройте файл настроек Collector.
bashnano otel-config.yamlСохраните изменения сочетанием Ctrl + O, затем Enter, а выйдите из редактора сочетанием Ctrl + X.
Добавьте в раздел
receiversновый приёмник сразу послеotlp, с таким же отступом в два пробела.yaml# Метрики самого сервера host_metrics: root_path: /hostfs collection_interval: 10s scrapers: cpu: memory: load: filesystem: network:scrapersперечисляет, что именно измерять. Мы взяли процессор, память, среднюю нагрузку, файловую систему и сеть. В документации приёмника есть и другие, например диски и процессы.collection_intervalзадаёт частоту измерений. По умолчанию это одна минута, мы поставили 10 секунд, чтобы быстрее увидеть результат.root_pathнужен, потому что Collector работает в контейнере. Чтобы он видел сервер, а не собственный контейнер, корень сервера подключают в папку/hostfs, а вroot_pathуказывают её.
В том же файле добавьте новый приёмник в конвейер метрик.
yamlmetrics: receivers: [otlp, host_metrics]Измените режим экспортёра
debug, чтобы в логе были не только счётчики, но и сами значения. Замените в разделеexportersстрокуverbosity: basicнаverbosity: detailedи сохраните файл.Откройте файл
compose.yamlи подключите корень сервера на чтение в разделvolumesсервисаcollector.bashnano compose.yamlyamlvolumes: - ./otel-config.yaml:/etc/otelcol-contrib/config.yaml:ro - /:/hostfs:ro # файловая система сервера для метрик хостаПроверьте конфигурацию и пересоздайте контейнер Collector.
bashdocker compose run --rm --no-deps collector validate --config=/etc/otelcol-contrib/config.yaml docker compose up -d
Подождите около 20 секунд, пока Collector сделает первые измерения, и посмотрите, какие метрики он собрал.
docker logs otel-collector --since 12s 2>&1 | grep "Name: system" | sort -uЧтобы убедиться, что цифры настоящие, возьмём значение занятой памяти из лога и сравним его с командой free.
docker logs otel-collector --since 12s 2>&1 | grep -A12 system.memory.usage | grep -E "state|Value" | head -2
free -b | head -2
Collector собирает метрики сервера, а занятая память совпадает с выводом free
Collector выдал 13 метрик с именами, начинающимися на system.. Например, system.cpu.load_average.1m это средняя нагрузка за минуту, system.memory.usage это память в байтах по состояниям, system.filesystem.usage это занятое место на дисках, а system.network.io это объём переданных данных. Занятая память из метрики близка к столбцу used в выводе free, небольшая разница получилась из-за того, что значения сняты в разные моменты.
Верните режим basic
Подробный режим выводит в лог около 90 КБ каждую минуту, а это примерно 130 МБ в сутки. Когда насмотритесь, верните в otel-config.yaml строку verbosity: basic и перезапустите Collector командой docker compose restart collector.
Пока метрики попадают только в лог. Чтобы хранить их и рисовать графики, нужно отправить их в хранилище метрик, и в Collector для этого есть экспортёры, например prometheus. Это тема отдельной статьи.
Полезные команды
docker compose ps # состояние контейнеров и порты
docker compose logs -f collector # лог Collector в реальном времени
docker compose restart collector # перечитать файл настроек
docker compose run --rm --no-deps collector validate --config=/etc/otelcol-contrib/config.yaml # проверить настройки
docker compose down # остановить и удалить контейнерыПосле остановки командой docker compose down трассы в Jaeger пропадут, так как они хранятся только в памяти.
Частые вопросы
Нужно ли открывать порты 4317, 4318 и 16686 в UFW
Нет. Приложения на этом же сервере подключаются к 127.0.0.1, а Jaeger мы открываем через SSH-туннель. Порты в Compose опубликованы только на 127.0.0.1, и мы проверили с другого компьютера, что на внешнем адресе сервера соединение с ними не устанавливается. Не заменяйте 127.0.0.1:4317:4317 на 4317:4317 без необходимости. Брандмауэр UFW порты Docker не закрывает, и Collector без паролей окажется открытым всему интернету.
Приложение работает, но в Collector и Jaeger ничего нет, а в логе Failed to auto initialize OpenTelemetry
Не установлен экспортёр OTLP. Мы запускали приложение с одним только opentelemetry-distro и получили такие строки.
RuntimeError: Requested component 'otlp_proto_grpc' not found in entry point 'opentelemetry_traces_exporter'
Failed to auto initialize OpenTelemetryСамо приложение при этом продолжает отвечать на запросы, поэтому ошибка легко остаётся незамеченной. Остановите приложение, выполните pip install opentelemetry-exporter-otlp и запустите его снова.
В Jaeger пусто, а в терминале приложения нет ни одной ошибки
Ошибки отправки в терминал не выводятся, потому что записи журнала, в том числе сообщения самого OpenTelemetry, собирает автоматическое инструментирование. Мы проверили, что с параметром --logs_exporter console те же записи вместе с ошибками экспорта появляются на экране. Запустите приложение так.
opentelemetry-instrument --service_name dice-server --logs_exporter console flask run -p 8000В консоль начнут выводиться и записи самого приложения, и сообщения об ошибках экспорта, примеры которых разобраны в следующих двух вопросах.
Failed to export traces to localhost:4318 … Trying to connect an http1.x server
Приложение отправляет данные по gRPC, а адрес указывает на порт HTTP. Мы воспроизвели ошибку, запустив приложение с адресом Collector http://localhost:4318 через переменную OTEL_EXPORTER_OTLP_ENDPOINT.
Failed to export traces to localhost:4318, error code: StatusCode.UNAVAILABLE, error details: failed to connect to all addresses; last error: INTERNAL: ipv4:127.0.0.1:4318: Failed parsing HTTP/2 (Expected SETTINGS frame as the first frame, got frame type 80) (Trying to connect an http1.x server (HTTP status 400))Есть два выхода. Либо верните порт gRPC 4317, либо переключите приложение на HTTP переменной OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf. У утилиты telemetrygen та же ошибка выглядит как http2: frame too large, note that the frame header looked like an HTTP/1.1 header.
BadStatusLine … encountered while exporting spans batch, malformed HTTP response
Обратная ситуация. Приложение или утилита отправляют данные по HTTP, а адрес указывает на порт gRPC. Мы запустили приложение с переменными OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf и OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317.
Transient error ('Connection aborted.', BadStatusLine('\x00\x00\x06\x04\x00\x00\x00\x00\x00\x00\x05\x00\x00@\x00')) encountered while exporting spans batch, retrying in 0.82s.
Failed to export spans batch due to timeout, max retries or shutdown.Запомните соответствие. Порт 4317 принимает только gRPC, порт 4318 только HTTP. Для HTTP не забывайте и про --otlp-http у telemetrygen.
transport: Error while dialing … connect: connection refused
Collector не запущен или адрес указан неверно. Мы получили эту ошибку, отправив трассы на порт 4319, на котором никто не слушает.
traces export: exporter export timeout: rpc error: code = Unavailable desc = connection error: desc = "transport: Error while dialing: dial tcp 127.0.0.1:4319: connect: connection refused"Проверьте, что контейнер работает, командой docker compose ps, и что порты слушаются на сервере.
ss -tln | grep -E ":(4317|4318|16686) "В выводе должны быть три строки с адресом 127.0.0.1.
tls: first record does not look like a TLS handshake
Утилита telemetrygen по умолчанию подключается по TLS, а наш Collector принимает данные без шифрования. Мы получили эту ошибку, запустив утилиту без параметра --otlp-insecure.
transport: authentication handshake failed: tls: first record does not look like a TLS handshakeДобавьте параметр --otlp-insecure.
error reading server preface: connection reset by peer
Порт опубликован, а Collector внутри контейнера слушает не тот адрес. Так бывает, если в приёмнике otlp не указан endpoint. Мы проверили конфигурацию с пустыми grpc: и http:, и в логе Collector появились такие адреса.
"endpoint": "127.0.0.1:4317"
"endpoint": "127.0.0.1:4318"Telemetrygen при этом отвечает ошибкой.
traces export: exporter export timeout: rpc error: code = Unavailable desc = connection error: desc = "error reading server preface: connection reset by peer"Укажите в конфигурации endpoint: 0.0.0.0:4317 для gRPC и endpoint: 0.0.0.0:4318 для HTTP, как в шаге 2, и перезапустите Collector.
references exporter "jaeger" which is not configured
В конвейере указан компонент, которого нет в своём разделе конфигурации. Мы получили эту ошибку, написав в конвейере трасс jaeger вместо otlp_grpc/jaeger.
Error: service::pipelines::traces: references exporter "jaeger" which is not configuredИмя в разделе service должно совпадать с именем в exporters, включая часть после косой черты. Так же Collector ругается и на неописанные приёмники и процессоры.
receivers::host_metrics: invalid root_path: stat /hostfs: no such file or directory
В compose.yaml не подключён корень сервера в папку /hostfs, а в настройках приёмника host_metrics она указана.
Error: receivers::host_metrics: invalid root_path: stat /hostfs: no such file or directoryДобавьте в раздел volumes строку - /:/hostfs:ro, как в шаге 7.
Наши ресурсы
Telegram-канал: https://t.me/f3cloud_news
Официальный сайт: https://f3cloud.com