bolt Valebyte VPS от $4/мес — NVMe, запуск за 60 секунд.

Получить VPS arrow_forward
eco Начальный Туториал

Развёртывание OpenTelemetry Collector и Jaeger для distributed tracing на VPS

calendar_month Oct 27, 2026 schedule 22 мин. чтения visibility 18 просмотров
Развёртывание OpenTelemetry Collector и Jaeger для distributed tracing на VPS
info

Нужен сервер для этого гайда? Мы предлагаем выделенные серверы и VPS в 50+ странах с мгновенной настройкой.

Нужен сервер для этого гайда?

Разверните VPS или выделенный сервер за минуты.

Развёртывание OpenTelemetry Collector и Jaeger для distributed tracing на VPS

TL;DR

OpenTelemetry Collector принимает трейсы от приложений по OTLP, обрабатывает их и передаёт в Jaeger, где можно искать запросы, смотреть цепочки вызовов и находить медленные сервисы. В этом руководстве вы развернёте self-hosted distributed tracing на Ubuntu VPS через Docker Compose, закроете технические порты firewall, опубликуете Jaeger UI по HTTPS и настроите резервное копирование данных.

  • OpenTelemetry Collector будет принимать OTLP/gRPC на порту 4317 и OTLP/HTTP на 4318.
  • Jaeger будет хранить трейсы в локальном persistent-хранилище Badger и показывать их через веб-интерфейс.
  • Jaeger UI будет доступен только через HTTPS reverse proxy Caddy, а не напрямую через порт 16686.
  • Минимально для тестов достаточно 2 vCPU, 4 GB RAM и 40 GB NVMe; для production-нагрузки разумнее начинать с 4 vCPU и 8 GB RAM.
  • Трейсинговые endpoint’ы нельзя оставлять открытыми всему интернету: ограничьте источники IP-адресами вашей инфраструктуры или VPN-сетью.
  • Конфиги, данные Jaeger и Docker volumes нужно регулярно копировать во внешнее хранилище.

Что мы настраиваем и зачем

Схема: Что мы настраиваем и зачем
Схема: Что мы настраиваем и зачем

Distributed tracing нужен, когда один пользовательский запрос проходит через несколько компонентов: frontend, API, очередь, worker, базу данных, сторонний платёжный сервис или LLM API. Обычные логи покажут отдельные события, метрики покажут общую картину нагрузки, но не ответят быстро на вопрос: «Почему конкретный запрос пользователя выполнялся 8 секунд и на каком сервисе возникла задержка?»

Трейс состоит из связанных span’ов. Каждый span описывает операцию: HTTP-запрос, SQL-запрос, публикацию сообщения в Kafka, выполнение job или обращение к Redis. У span есть время начала, длительность, статус, атрибуты и идентификаторы trace_id и span_id. Если сервисы передают trace context через HTTP-заголовки или очередь, Jaeger собирает путь запроса в единое дерево.

В этой схеме OpenTelemetry Collector выступает промежуточным слоем между приложениями и хранилищем трасс. Приложения отправляют данные в Collector по стандартному протоколу OTLP. Collector принимает данные, добавляет полезные атрибуты, ограничивает память, выполняет batching и экспортирует трейсы в Jaeger. Благодаря этому приложения не зависят от конкретного backend’а: позднее Jaeger можно заменить на Tempo, OpenSearch, ClickHouse-совместимое хранилище или облачный сервис без переписывания инструментирования.

Компонент Роль Доступ извне
Инструментированное приложение Создаёт spans и передаёт контекст между сервисами Нет
OpenTelemetry Collector Contrib Принимает, фильтрует, группирует и экспортирует OTLP-трейсы Только от доверенных серверов или VPN
Jaeger all-in-one Принимает трейсы, хранит их и выдаёт API для поиска Только внутри Docker-сети
Caddy Выдаёт TLS-сертификат и публикует защищённый Jaeger UI TCP 80 и 443
Badger Локальное embedded-хранилище данных Jaeger Нет

Что получится в результате

После выполнения инструкции у вас будет домен вида traces.example.com с HTTPS-доступом к Jaeger UI. Приложения смогут отправлять OTLP/gRPC-трейсы на адрес https://otel.example.com:4317 либо на приватный IP Collector без TLS, если все сервисы находятся во внутренней сети или соединены WireGuard.

В Jaeger можно будет выбрать сервис, операцию и диапазон времени, открыть отдельный trace, увидеть waterfall-диаграмму span’ов, определить узкое место и перейти к связанным логам по trace_id. В конфигурации мы добавим атрибуты deployment.environment и host.name, чтобы production, staging и разные серверы не смешивались в одном поиске.

Self-hosted или cloud-managed tracing

У облачных платформ наблюдаемости обычно есть быстрый старт, готовые алерты, длительное хранение и масштабируемый backend. Недостатки — оплата за объём ingestion, зависимость от поставщика, передача технических метаданных третьей стороне и риск неожиданного роста счёта при всплеске трафика.

Self-hosted OpenTelemetry Collector и Jaeger на VPS подходят небольшой команде, внутренним сервисам, SaaS на ранней стадии, тестовым средам и проектам с требованиями к контролю данных. Вы самостоятельно выбираете retention, доступ, бэкапы и место размещения. Цена этой гибкости — необходимость следить за диском, обновлениями, безопасностью и ростом объёма трасс.

Jaeger с Badger в этой статье — практичный вариант для малого и среднего объёма: десятков сервисов и умеренного числа запросов. Для долгого хранения, высокой кардинальности, многотерабайтного потока и нескольких Collector-инстансов потребуется отдельный backend: OpenSearch, Elasticsearch, ClickHouse или другой поддерживаемый production storage. Архитектура с Collector сохраняется; меняется только exporter и backend Jaeger.

Какой VPS-конфиг нужен под эту задачу

Схема: Какой VPS-конфиг нужен под эту задачу
Схема: Какой VPS-конфиг нужен под эту задачу

Потребление ресурсов зависит не столько от количества сервисов, сколько от числа span’ов в секунду, среднего размера атрибутов и времени хранения. Один HTTP-запрос может создать от 3–5 span’ов в простом API до 30–100 span’ов в микросервисной системе с SQL, Redis, очередями и внешними API. Не включайте запись тела HTTP-запросов, токенов, паролей и больших payload’ов: это опасно и быстро заполняет хранилище.

Сценарий CPU RAM NVMe-диск Сеть Ожидаемый масштаб
Лаборатория, staging, 1–3 приложения 2 vCPU 4 GB 40 GB 100 Mbps До нескольких тысяч span/min
Небольшой production 4 vCPU 8 GB 100–160 GB 1 Gbps 5–30 сервисов, умеренный sampling
Активный SaaS или несколько окружений 8 vCPU 16–32 GB 300 GB+ 1 Gbps Высокий ingestion, retention 7–30 дней

Для старта production-развёртывания возьмите 4 vCPU, 8 GB RAM, 120 GB NVMe и порт 1 Gbps. Такой запас позволит Collector работать с memory limiter и очередями, Jaeger — кэшировать данные, а системе — не уходить в swap при кратковременной нагрузке. Как нейтральный вариант можно выбрать VPS с указанными характеристиками, но перед заказом проверьте тип накопителя, лимит трафика, IPv4-адрес и возможность расширить диск без миграции.

Как оценить объём диска

Начните с измерения, а не с предположений. Пусть приложение создаёт 20 span’ов на запрос, обрабатывает 10 запросов в секунду, а после сериализации и индексов один span в среднем занимает 1–3 KB. Это уже примерно 17–52 GB в сутки до учёта компрессии, служебных данных и sampling. При sampling 10% объём уменьшится примерно в десять раз, поэтому sampling — главный регулятор стоимости хранения.

Для первого месяца выделите минимум в два раза больше места, чем расчётный объём за retention-период. Это оставит резерв для обновлений Docker-образов, архивов бэкапа и внезапного роста трафика. Настройте алерт при заполнении диска на 70%, а не на 95%: Badger и Docker могут резко деградировать, когда свободное место заканчивается.

Когда нужен dedicated, а не VPS

VPS подходит, пока нагрузка предсказуема и latency диска не критична. Dedicated-сервер имеет смысл при постоянно высоком ingestion, больших retention-периодах, необходимости нескольких NVMe в RAID, требованиях к изоляции CPU и IOPS или использовании тяжёлого backend’а вроде OpenSearch. Также dedicated разумен, если tracing-система обслуживает десятки команд и становится внутренней платформой наблюдаемости.

Не переносите Jaeger на dedicated только потому, что в интерфейсе появились десятки тысяч traces. Сначала включите head sampling в SDK, удалите лишние атрибуты, уменьшите retention, проверьте скорость диска и вынесите production и staging в разные сервисы. Обычно это даёт более заметный эффект, чем немедленное увеличение сервера.

Выбор локации

Локация влияет на задержку отправки телеметрии, юридические требования и стоимость трафика. Collector лучше размещать близко к сервисам, которые отправляют трейсы: в том же дата-центре, регионе или хотя бы на том же континенте. Для синхронного OTLP/gRPC высокая RTT увеличивает накладные расходы приложения, особенно при проблемной сети.

Если production-сервисы находятся в разных регионах, не заставляйте каждый сервис писать через интернет в один Collector. Разверните локальные Collector-инстансы рядом с приложениями и отправляйте данные дальше асинхронно или в центральный backend. Для начала достаточно одного VPS, но архитектурно сохраните возможность такого разделения.

Подготовка сервера

Схема: Подготовка сервера
Схема: Подготовка сервера

Ниже используется Ubuntu Server 24.04 LTS. Команды выполняйте от root только на этапе первоначальной подготовки, затем работайте под отдельным пользователем otel. Предполагается, что у вас есть домены traces.example.com для Jaeger UI и, при необходимости, otel.example.com для публичного OTLP endpoint.

До настройки TLS создайте A-запись traces.example.com, указывающую на публичный IPv4 сервера. Если планируете принимать OTLP через отдельный домен, добавьте и запись otel.example.com. Проверить DNS можно с локального компьютера командой dig +short traces.example.com.

Обновление операционной системы

Сначала установите накопившиеся обновления и базовые утилиты. После обновления ядра перезагрузите сервер, прежде чем продолжать настройку Docker.

# Обновляет индексы пакетов и устанавливает обновления безопасности.
sudo apt update && sudo apt upgrade -y

# Устанавливает инструменты диагностики, firewall, редактор и поддержку HTTPS-репозиториев.
sudo apt install -y ca-certificates curl gnupg lsb-release \
  ufw fail2ban unattended-upgrades jq vim htop dnsutils

# Перезагружает сервер, если обновлялось ядро или системные библиотеки.
sudo reboot

Пользователь, SSH-ключи и sudo

Создайте отдельную учётную запись. Не используйте пароль для SSH: ключ Ed25519 проще защитить от brute-force и он удобнее для автоматизации. Перед отключением входа root обязательно откройте вторую SSH-сессию и убедитесь, что новый пользователь может войти и выполнить sudo.

# Создаёт пользователя otel с домашним каталогом и добавляет его в группу sudo.
sudo adduser otel
sudo usermod -aG sudo otel

# Создаёт каталог ключей и копирует текущий authorised_keys для нового пользователя.
sudo install -d -m 700 -o otel -g otel /home/otel/.ssh
sudo cp /root/.ssh/authorized_keys /home/otel/.ssh/authorized_keys
sudo chown otel:otel /home/otel/.ssh/authorized_keys
sudo chmod 600 /home/otel/.ssh/authorized_keys

Отредактируйте SSH-конфигурацию. Если вы подключаетесь не как root или у провайдера ключ лежит в другом месте, не копируйте файл вслепую: добавьте свой публичный ключ через ssh-copy-id otel@SERVER_IP с локальной машины.

# Открывает отдельный SSH-конфиг для усиления настроек доступа.
sudo vim /etc/ssh/sshd_config.d/99-hardening.conf

# Проверяет синтаксис конфигурации и перезапускает SSH только при успехе.
sudo sshd -t && sudo systemctl restart ssh
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
X11Forwarding no
MaxAuthTries 3
AllowUsers otel

Firewall и Fail2ban

Открывайте наружу только SSH, HTTP и HTTPS. Порты 4317, 4318, 16686 и 14268 не нужно публиковать всему интернету. Если приложения находятся на других серверах, позже разрешите OTLP только из их приватной подсети или через WireGuard.

# Разрешает SSH до включения firewall, иначе можно потерять доступ к серверу.
sudo ufw allow OpenSSH

# Разрешает Caddy принимать HTTP для ACME-проверки и HTTPS для Jaeger UI.
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# Включает firewall и показывает активные правила.
sudo ufw enable
sudo ufw status verbose
# Включает Fail2ban для защиты SSH от повторяющихся неудачных попыток входа.
sudo systemctl enable --now fail2ban

# Проверяет, что jail SSH активен.
sudo fail2ban-client status sshd

Важная особенность Docker: опубликованные контейнерные порты могут обходить обычные UFW-правила из-за правил iptables, созданных Docker. В этой инструкции OTLP-порты вообще не публикуются по умолчанию. Если вы позже добавите их в секцию ports, ограничьте доступ отдельными правилами в цепочке DOCKER-USER или используйте private networking/VPN.

Установка ПО — пошагово

Мы используем Docker Engine 27.x или новее, Docker Compose Plugin 2.x, OpenTelemetry Collector Contrib 0.120.x и Jaeger all-in-one 1.67.x как совместимый стабильный набор. Перед production-обновлением сверяйте доступные теги образов с официальными release notes и тестируйте обновление на staging: версия Collector и образ Jaeger могут меняться независимо.

Установка Docker Engine из официального репозитория

Не используйте старый пакет docker.io из стандартного репозитория Ubuntu: он может заметно отставать по версии. Следующие команды добавляют официальный репозиторий Docker и устанавливают engine вместе с Compose plugin.

# Удаляет конфликтующие старые пакеты Docker, если они были установлены.
sudo apt remove -y docker.io docker-doc docker-compose docker-compose-v2 \
  podman-docker containerd runc || true

# Создаёт каталог для ключей сторонних APT-репозиториев.
sudo install -m 0755 -d /etc/apt/keyrings

# Загружает и сохраняет ключ репозитория Docker в формате keyring.
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
  sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
# Добавляет официальный Docker APT-репозиторий для текущего релиза Ubuntu.
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
  https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Устанавливает Docker Engine, CLI, containerd и Compose plugin.
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io \
  docker-buildx-plugin docker-compose-plugin
# Запускает Docker при загрузке и проверяет установленную версию.
sudo systemctl enable --now docker
sudo docker version
sudo docker compose version

# Разрешает пользователю otel управлять Docker без sudo.
sudo usermod -aG docker otel

После добавления в группу docker выйдите из SSH-сессии и подключитесь заново как otel. Членство в группе Docker фактически даёт root-эквивалент на сервере, поэтому добавляйте в неё только доверенных администраторов.

Каталоги проекта и секреты

Храните Compose-файл, конфигурации и данные в предсказуемом каталоге. Каталог /opt/observability обычно удобнее, чем домашняя директория: его легче включить в бэкапы и не удалить случайно при смене пользователя.

# Создаёт структуру каталогов для конфигураций, Caddy и persistent-данных Jaeger.
sudo mkdir -p /opt/observability/{otel-collector,caddy,data/jaeger}
sudo chown -R otel:otel /opt/observability

# Переходит в рабочий каталог проекта.
cd /opt/observability

# Создаёт .env с правами только для владельца.
umask 077
touch .env
chmod 600 .env

Секретом в этой конфигурации будет хэш пароля для Jaeger UI. Caddy не хранит пароль в открытом виде. Сгенерируйте хэш интерактивно: команда попросит пароль и выведет строку, начинающуюся с $2a$ или похожий bcrypt-хэш.

# Генерирует bcrypt-хэш пароля для HTTP Basic Authentication в Caddy.
docker run --rm caddy:2 caddy hash-password --plaintext 'ЗАМЕНИТЕ_НА_ДЛИННЫЙ_ПАРОЛЬ'

# Открывает файл переменных окружения для записи доменов и сгенерированного хэша.
vim /opt/observability/.env

Не вставляйте реальный пароль в docker-compose.yml, не отправляйте .env в публичный Git-репозиторий и не используйте один пароль для SSH и веб-интерфейса. Для команды лучше использовать VPN или внешний identity proxy, а Basic Auth оставить дополнительным уровнем защиты.

Проверка системных лимитов

Jaeger с Badger использует файловую систему интенсивнее, чем простой stateless контейнер. Перед запуском проверьте свободный диск и отключите swap только если у вас есть достаточная RAM и понятная политика OOM. Для небольшого VPS swap полезен как аварийный буфер, но постоянная активность swap означает, что серверу не хватает памяти.

# Показывает объём свободного места, оперативной памяти и наличие swap.
df -h /
free -h
swapon --show

# Создаёт журнал Docker с ограничением размера, чтобы контейнерные логи не заполнили диск.
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json > /dev/null <<'EOF'
{
  "log-driver": "local",
  "log-opts": {
    "max-size": "20m",
    "max-file": "5"
  }
}
EOF

# Перезапускает Docker для применения политики логирования.
sudo systemctl restart docker

Конфигурация OpenTelemetry Collector, Jaeger и HTTPS

Ниже приведена конфигурация, в которой наружу публикуются только Caddy-порты 80 и 443. Collector доступен внутри Docker-сети. Это безопасный вариант, если приложения запущены на том же сервере или в той же Docker-сети. Для удалённых приложений далее будет показано, как осторожно открыть OTLP-порты для доверенной подсети.

Файл переменных окружения

Замените домен, почту и bcrypt-хэш. В значении CADDY_BASIC_AUTH_HASH символ $ Docker Compose может интерпретировать как подстановку переменной, поэтому каждый $ заменяется на двойной $$. Это нормальная особенность Compose.

# /opt/observability/.env
TRACES_DOMAIN=traces.example.com
[email protected]

# Вставьте результат caddy hash-password, заменив каждый $ на $$.
CADDY_BASIC_AUTH_HASH=$$2a$$14$$REPLACE_WITH_YOUR_BCRYPT_HASH

# Среда добавляется Collector ко всем входящим трейсам.
DEPLOYMENT_ENVIRONMENT=production

Конфигурация OpenTelemetry Collector

Collector принимает OTLP по gRPC и HTTP, защищает сервер от переполнения памяти через memory_limiter, группирует spans в пакеты и отправляет их в Jaeger через OTLP/gRPC. Важно: receiver слушает 0.0.0.0 внутри контейнера, но это ещё не делает порт доступным из интернета, пока он не опубликован в Compose.

# /opt/observability/otel-collector/config.yaml
extensions:
  health_check:
    endpoint: 0.0.0.0:13133

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

processors:
  memory_limiter:
    check_interval: 1s
    limit_mib: 512
    spike_limit_mib: 128

  resource:
    attributes:
      - key: deployment.environment
        value: ${env:DEPLOYMENT_ENVIRONMENT}
        action: upsert
      - key: host.name
        value: tracing-vps
        action: upsert

  batch:
    send_batch_size: 8192
    timeout: 5s

exporters:
  otlp/jaeger:
    endpoint: jaeger:4317
    tls:
      insecure: true
    sending_queue:
      enabled: true
      num_consumers: 4
      queue_size: 10000
    retry_on_failure:
      enabled: true
      initial_interval: 5s
      max_interval: 30s
      max_elapsed_time: 300s

service:
  extensions: [health_check]
  pipelines:
    traces:
      receivers: [otlp]
      processors: [memory_limiter, resource, batch]
      exporters: [otlp/jaeger]

  telemetry:
    logs:
      level: info

Лимит 512 MiB рассчитан на сервер с 8 GB RAM. На VPS с 4 GB уменьшите его до 256 MiB; на сервере с 16 GB можно поднять до 1024 MiB, если это оправдано нагрузкой. Collector при достижении лимита начнёт отклонять часть данных вместо того, чтобы вызвать OOM и уронить весь стек.

Docker Compose: Collector, Jaeger и Caddy

Jaeger all-in-one получает трейсы от Collector на внутреннем порту 4317. Badger-каталоги вынесены в bind mount, поэтому удаление контейнера не удалит данные. Переменная BADGER_SPAN_STORE_TTL задаёт retention 168 часов, то есть 7 дней. Настройте срок хранения по доступному диску и необходимости расследований.

# /opt/observability/docker-compose.yml
services:
  jaeger:
    image: jaegertracing/all-in-one:1.67.0
    container_name: jaeger
    restart: unless-stopped
    environment:
      COLLECTOR_OTLP_ENABLED: "true"
      SPAN_STORAGE_TYPE: badger
      BADGER_EPHEMERAL: "false"
      BADGER_DIRECTORY_VALUE: /badger/data
      BADGER_DIRECTORY_KEY: /badger/key
      BADGER_SPAN_STORE_TTL: 168h
      MEMORY_MAX_TRACES: "0"
    volumes:
      - ./data/jaeger:/badger
    networks:
      - tracing

  otel-collector:
    image: otel/opentelemetry-collector-contrib:0.120.0
    container_name: otel-collector
    restart: unless-stopped
    command: ["--config=/etc/otelcol-contrib/config.yaml"]
    environment:
      DEPLOYMENT_ENVIRONMENT: ${DEPLOYMENT_ENVIRONMENT}
    volumes:
      - ./otel-collector/config.yaml:/etc/otelcol-contrib/config.yaml:ro
    depends_on:
      - jaeger
    networks:
      - tracing

  caddy:
    image: caddy:2.8.4
    container_name: caddy
    restart: unless-stopped
    environment:
      TRACES_DOMAIN: ${TRACES_DOMAIN}
      ACME_EMAIL: ${ACME_EMAIL}
      CADDY_BASIC_AUTH_HASH: ${CADDY_BASIC_AUTH_HASH}
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./caddy/Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config
    depends_on:
      - jaeger
    networks:
      - tracing

networks:
  tracing:
    name: tracing
    driver: bridge

volumes:
  caddy_data:
  caddy_config:

Caddy и HTTPS-доступ к Jaeger UI

Caddy автоматически получает и продлевает TLS-сертификат через ACME, если DNS домена уже указывает на сервер, а TCP 80 и 443 доступны извне. Basic Auth защищает интерфейс от случайного просмотра traces. Не считайте Basic Auth полноценной системой управления доступом: для корпоративного использования добавьте VPN, SSO-proxy или сетевой ACL.

# /opt/observability/caddy/Caddyfile
{
    email {$ACME_EMAIL}
    auto_https prefer_wildcard
}

{$TRACES_DOMAIN} {
    encode zstd gzip

    basic_auth {
        observability {$CADDY_BASIC_AUTH_HASH}
    }

    reverse_proxy jaeger:16686

    header {
        -Server
        X-Content-Type-Options "nosniff"
        X-Frame-Options "DENY"
        Referrer-Policy "no-referrer"
        Strict-Transport-Security "max-age=31536000; includeSubDomains"
    }

    log {
        output stdout
        format json
    }
}

Запуск и проверка контейнеров

Перед запуском проверьте синтаксис Compose. Затем скачайте образы и запустите сервисы в фоне. Первый выпуск TLS-сертификата может занять до минуты; не меняйте DNS и не перезапускайте Caddy без необходимости.

# Проверяет подстановку переменных и итоговую конфигурацию Docker Compose.
cd /opt/observability
docker compose config > /tmp/observability-compose-rendered.yml

# Скачивает зафиксированные версии образов и запускает стек в фоне.
docker compose pull
docker compose up -d

# Показывает состояние контейнеров и последние строки журналов.
docker compose ps
docker compose logs --tail=100 otel-collector jaeger caddy
# Проверяет health endpoint Collector из его Docker-сети.
docker exec otel-collector wget -qO- http://127.0.0.1:13133/ || true

# Проверяет, что Jaeger API отвечает внутри контейнера.
docker exec jaeger wget -qO- http://127.0.0.1:16686/api/services

# Проверяет публичный HTTPS endpoint; потребуется пароль Basic Auth.
curl -I -u observability:'ВАШ_ПАРОЛЬ' https://traces.example.com

Откройте https://traces.example.com в браузере, введите пользователя observability и пароль, для которого генерировали хэш. Если Jaeger UI загрузился, но список сервисов пуст, это нормально: сервисы появятся только после отправки первого trace.

Как отправить тестовый trace

Самый простой тест — временно запустить контейнер telemetrygen в той же Docker-сети. Он создаст тестовые spans и отправит их в Collector. После этого в Jaeger выберите сервис telemetrygen, операцию traces и нажмите Find Traces.

# Отправляет 20 тестовых trace в секунду в Collector внутри Docker-сети в течение 10 секунд.
docker run --rm --network tracing \
  ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:0.120.0 \
  traces --otlp-endpoint otel-collector:4317 \
  --otlp-insecure --rate 20 --duration 10s

# Проверяет, что сервис telemetrygen появился в Jaeger API.
docker exec jaeger wget -qO- http://127.0.0.1:16686/api/services | jq .

Подключение удалённых приложений

Самый безопасный подход — соединить серверы через WireGuard, Tailscale или приватную сеть провайдера и опубликовать порты Collector только на приватном интерфейсе. Если это невозможно, добавьте порты 4317 и 4318 в Compose, но разрешите доступ только с известного IP-адреса приложения. Не открывайте их для 0.0.0.0/0: любой сможет присылать мусорные spans и заполнить ваш диск.

# Добавьте в сервис otel-collector только при необходимости внешнего OTLP.
# Лучше заменить 10.20.0.10 на приватный IP интерфейса сервера.
    ports:
      - "10.20.0.10:4317:4317"
      - "10.20.0.10:4318:4318"

Для приложения на Python, Node.js, Go, Java или .NET укажите endpoint через переменные окружения. Инструментирование библиотеками OpenTelemetry SDK выполняется отдельно, но базовые переменные почти одинаковы для всех языков.

# Пример общих переменных окружения приложения для OTLP/gRPC.
export OTEL_SERVICE_NAME=orders-api
export OTEL_RESOURCE_ATTRIBUTES="service.version=2026.03,deployment.environment=production"
export OTEL_EXPORTER_OTLP_ENDPOINT="http://10.20.0.10:4317"
export OTEL_EXPORTER_OTLP_PROTOCOL="grpc"
export OTEL_TRACES_SAMPLER="parentbased_traceidratio"
export OTEL_TRACES_SAMPLER_ARG="0.1"

Sampling 0.1 означает запись примерно 10% новых trace. Родительский sampler сохраняет решение родительского запроса во всех дочерних сервисах, благодаря чему вы не получите обрывки одного trace. Для важных ошибок можно позже внедрить tail sampling в Collector: он позволяет сохранять 100% traces с ошибками и медленные запросы, но требует больше памяти и более тщательной настройки.

Бэкапы и обслуживание

Схема: Бэкапы и обслуживание
Схема: Бэкапы и обслуживание

В этом развёртывании необходимо копировать каталог /opt/observability/data/jaeger, Compose-файл, конфигурацию Collector, Caddyfile, файл .env и данные Caddy volume. Последние содержат TLS-сертификаты и состояние ACME; без них Caddy сможет выпустить сертификат повторно, но восстановление будет медленнее и может упереться в rate limits.

Не считайте Docker image бэкапом. Образ можно скачать заново, но traces в Badger и конфигурация не восстановятся сами. Также не храните единственную копию бэкапа на том же VPS: потеря диска, ошибочное удаление или компрометация сервера уничтожат и данные, и резерв.

Бэкап через restic в S3-совместимое хранилище

Restic шифрует содержимое до отправки и поддерживает дедупликацию, поэтому хорошо подходит для ежедневных копий. Создайте bucket у внешнего S3-провайдера или на отдельном MinIO-сервере. Данные доступа храните в root-доступном файле, а не в командной строке и не в shell history.

# Устанавливает restic из репозитория Ubuntu.
sudo apt install -y restic

# Создаёт каталог для секретов бэкапа с доступом только root.
sudo install -d -m 700 /root/.config/restic

# Создаёт файл переменных S3 и пароль репозитория.
sudo vim /root/.config/restic/observability.env
sudo chmod 600 /root/.config/restic/observability.env
# /root/.config/restic/observability.env
export RESTIC_REPOSITORY="s3:https://s3.example.net/observability-restic"
export RESTIC_PASSWORD="СГЕНЕРИРУЙТЕ_ОТДЕЛЬНЫЙ_ДЛИННЫЙ_ПАРОЛЬ"
export AWS_ACCESS_KEY_ID="REPLACE_ME"
export AWS_SECRET_ACCESS_KEY="REPLACE_ME"
export AWS_DEFAULT_REGION="us-east-1"
# Инициализирует зашифрованный restic-репозиторий один раз.
sudo bash -c 'source /root/.config/restic/observability.env && restic init'

# Создаёт каталог для скриптов администратора.
sudo install -d -m 700 /usr/local/sbin

# Создаёт ежедневный скрипт резервного копирования.
sudo vim /usr/local/sbin/backup-observability.sh
sudo chmod 700 /usr/local/sbin/backup-observability.sh
#!/usr/bin/env bash
set -euo pipefail

source /root/.config/restic/observability.env

cd /opt/observability

# Останавливает запись в Badger на время консистентной копии.
docker compose stop jaeger

# Копирует конфигурации, данные Jaeger и Docker volume Caddy.
restic backup \
  /opt/observability/otel-collector \
  /opt/observability/caddy \
  /opt/observability/data/jaeger \
  /opt/observability/docker-compose.yml \
  /opt/observability/.env \
  /var/lib/docker/volumes/observability_caddy_data \
  --tag observability

# Запускает Jaeger даже при последующей ошибке очистки.
docker compose start jaeger

# Хранит 7 дневных, 4 недельных и 6 месячных наборов.
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune

# Проверяет целостность части данных в репозитории.
restic check --read-data-subset=5%

Для гарантированного запуска Jaeger даже при ошибке можно дополнить скрипт trap 'docker compose start jaeger' EXIT. При небольшом объёме данных краткая остановка Jaeger ночью приемлема. Для больших production-хранилищ используйте нативные snapshot-механизмы backend’а вместо файлового копирования работающей базы.

# Добавляет ежедневный запуск бэкапа в 03:20 и записывает лог в защищённый файл.
sudo crontab -e

# Строка для root crontab:
20 3   * /usr/local/sbin/backup-observability.sh >> /var/log/backup-observability.log 2>&1

Проверка восстановления

Бэкап без теста восстановления — лишь предположение. Раз в месяц восстанавливайте данные на отдельный тестовый VPS или хотя бы во временный каталог. Проверьте, что после запуска Compose открывается Jaeger UI, а старые сервисы и traces доступны в поиске.

# Показывает доступные snapshots в restic-репозитории.
sudo bash -c 'source /root/.config/restic/observability.env && restic snapshots'

# Восстанавливает последний snapshot во временный каталог для проверки.
sudo mkdir -p /tmp/observability-restore
sudo bash -c 'source /root/.config/restic/observability.env && \
  restic restore latest --target /tmp/observability-restore'

Обновления и retention

Для одного VPS обновление выполняйте в maintenance window: сделайте бэкап, прочитайте changelog образов, обновите один компонент, проверьте health и только затем переходите к следующему. Команда docker compose pull && docker compose up -d создаёт короткий перерыв в приёме traces. SDK обычно буферизуют часть данных, но рассчитывать на это без теста нельзя.

Rolling update нужен, когда потеря даже нескольких секунд телеметрии неприемлема. Тогда запускают минимум два Collector-инстанса за балансировщиком, используют очереди и HA backend для Jaeger. Один Jaeger all-in-one с локальным Badger не поддерживает настоящий rolling upgrade без перерыва и не является высокодоступной конфигурацией.

# Проверяет использование диска, контейнеров и размер данных Jaeger.
df -h /
docker stats --no-stream
sudo du -sh /opt/observability/data/jaeger

# Обновляет образы только после создания проверенного бэкапа.
cd /opt/observability
docker compose pull
docker compose up -d
docker compose logs --tail=100

Troubleshooting и FAQ

Почему Jaeger UI открывается, но список сервисов пуст?

Пустой список означает, что Jaeger пока не получил ни одного корректного trace либо Collector не может экспортировать данные. Сначала выполните тест с telemetrygen из раздела конфигурации. Затем проверьте docker compose logs otel-collector: ищите ошибки exporter, connection refused или deadline exceeded. Убедитесь, что endpoint exporter указан как jaeger:4317, а у Jaeger включено COLLECTOR_OTLP_ENABLED=true. Также проверьте, что приложение использует именно traces exporter, а не только metrics или logs.

Почему приложение получает connection refused на порту 4317?

По умолчанию в этой статье порт 4317 не опубликован наружу, поэтому удалённый сервер не может подключиться к Collector. Это сделано намеренно. Для приложений на том же хосте используйте Docker network и адрес otel-collector:4317. Для другого сервера организуйте WireGuard или private network, затем добавьте безопасную публикацию порта на приватный IP. Проверьте также, что приложение не пытается использовать HTTP endpoint 4318 с gRPC-протоколом или наоборот.

Caddy не получает TLS-сертификат: что проверить?

Проверьте, что A-запись домена указывает на публичный IP VPS: dig +short traces.example.com. Затем убедитесь, что порты 80 и 443 разрешены в UFW и в сетевом firewall панели провайдера. Посмотрите docker compose logs caddy; ошибки ACME обычно прямо указывают на DNS, закрытый порт или превышение лимита запросов сертификата. Не используйте CDN proxy в режиме, который мешает HTTP challenge, пока не убедитесь, что Caddy получил первый сертификат.

Collector потребляет много RAM или перезапускается с OOMKilled. Как исправить?

Сначала подтвердите причину командой docker inspect otel-collector и посмотрите поле OOMKilled. Уменьшите limit_mib и spike_limit_mib в processor memory_limiter, сократите queue_size, снизьте sampling в SDK и отключите избыточные атрибуты. Не увеличивайте лимиты памяти бесконечно: если поток trace выше возможностей VPS, Collector лишь накопит больше данных перед падением. Для высокой нагрузки добавьте второй Collector и балансировщик.

Диск быстро заполняется, хотя retention установлен на 7 дней.

Проверьте реальный размер Badger через du -sh /opt/observability/data/jaeger, Docker-логи через docker system df и системные логи через journalctl --disk-usage. Retention не всегда освобождает место мгновенно: compaction хранилища занимает время. Снизьте sampling, сократите BADGER_SPAN_STORE_TTL, очистите неиспользуемые Docker-образы командой docker image prune и не записывайте request/response body в атрибуты spans. Добавлять диск безопаснее до достижения 90% заполнения.

Какой VPS-конфиг минимально подойдёт?

Для тестов, домашней лаборатории или staging достаточно 2 vCPU, 4 GB RAM, 40 GB NVMe и 100 Mbps. Такой сервер подходит для нескольких приложений при sampling 5–20% и коротком retention. Для production лучше взять 4 vCPU, 8 GB RAM и 100–160 GB NVMe: будет запас под очереди Collector, кэш, обновления и рост объёма данных. Если диск медленный HDD, tracing backend может тормозить даже при достаточном количестве CPU.

Что выбрать — VPS или dedicated для этой задачи?

Начинайте с VPS, если это один проект, несколько сервисов и retention до нескольких недель. VPS проще масштабировать по CPU, RAM и диску, а OpenTelemetry Collector хорошо работает в такой среде. Dedicated нужен при устойчиво высокой записи spans, жёстких требованиях к IOPS, нескольких NVMe в RAID, длинном хранении и тяжёлом backend’е вместо локального Badger. Переход оправдан измерениями: загрузкой CPU, memory pressure, latency диска и скоростью заполнения хранилища.

Можно ли отправлять в Collector персональные данные, JWT или SQL-параметры?

Нет, по умолчанию не стоит. Атрибуты trace часто доступны разработчикам, попадают в бэкапы и могут храниться неделями. Исключите заголовки Authorization, cookies, пароли, токены, полные тела запросов, адреса электронной почты и платёжные данные. В SDK используйте allowlist атрибутов, а в Collector добавьте processors для удаления чувствительных ключей. Для SQL лучше записывать тип операции и имя таблицы, а не полный запрос с параметрами пользователя.

Почему trace состоит из одного span, хотя запрос проходит через несколько сервисов?

Обычно причина в потере контекста между сервисами. Проверьте, что HTTP-клиент автоматически передаёт заголовки W3C Trace Context: traceparent и tracestate. Для очередей убедитесь, что producer записывает context в message headers, а consumer извлекает его до создания дочернего span. Все сервисы должны использовать совместимый propagator, обычно tracecontext. Также проверьте, что разные сервисы не генерируют новый root trace из-за отключенного auto-instrumentation.

Выводы и следующие шаги

Схема: Выводы и следующие шаги
Схема: Выводы и следующие шаги

Теперь у вас есть self-hosted стек distributed tracing: приложения отправляют OTLP-трейсы в OpenTelemetry Collector, Collector передаёт их в Jaeger, а Jaeger UI доступен по HTTPS с базовой защитой. Конфигурация подходит для первого production-использования при умеренной нагрузке и даёт контроль над данными, retention и инфраструктурой.

  1. Инструментируйте основные HTTP-входы, исходящие HTTP-вызовы, SQL, Redis и очереди; начните с одного критического пользовательского сценария.
  2. Добавьте sampling по правилам: сохраняйте все ошибки и медленные запросы, а успешные типовые запросы записывайте с пониженной вероятностью.
  3. При росте нагрузки разделите Collector и Jaeger по разным серверам, вынесите storage в production backend и настройте мониторинг CPU, RAM, диска, queue size и скорости ingestion.

Регулярно проверяйте восстановление бэкапов, свободное место и актуальность образов. Трейсинг приносит пользу только тогда, когда нужный trace можно быстро найти именно в момент инцидента.

Был ли этот гайд полезен?

Ваш отзыв помогает нам улучшать гайды.

Поделиться записью:

Отправьте гайд тому, кому он может пригодиться.

Telegram VKVK WhatsApp Facebook LinkedIn XX

развёртывание opentelemetry collector и jaeger для distributed tracing на vps
support_agent
Valebyte Support
Usually replies within minutes
Hi there!
Send us a message and we'll reply as soon as possible.