Наблюдаемость “по-взрослому”: опыт внедрения OpenTelemetry

от автора

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

Перед любым внедрением нового решения я начинаю с фундаментальной базы — книги экономят часы гугли­нга и спасают от архитектурных ошибок. В итоге мой выбор упал на стек OpenTelemetry в связке с привычными open-source бэкендами: Prometheus, Loki, Tempo и Grafana.

С чего стоить начать

С чего стоить начать

Также настоятельно рекомендую обратиться к официальный документации, так как на русскоязычных форумах на удивление мало актуальной информации по этому продукту.

Ключевая идея решения

  1. Максимальная изоляция: всё запускаю в контейнерах с заранее настроенными конфигурационными файлами и дашбордами.

  2. Collector-ы на узлах: универсальный агент для хоста и специализированный агент для приложения собирают метрики, логи и трассировки. Управляем через Ansible.

  3. Единый шлюз (Gateway): маршрутизация по типу данных.

  4. Привычные хранилища: Prometheus, Loki, Tempo.

  5. Grafana: единые дашборды и алерты.


Мой авторский канал — сообщество, где делятся опытом

https://t.me/IT_Chuyana


Архитектура мониторинга

Схема системы мониторинга

Схема системы мониторинга

В ходе проектирования я решил сделать двух агентов:

  • Универсальный коллектор-агент устанавливается на всех узлах через Ansible, собирает метрики и логи и позволяет наблюдать за их состоянием хостов. Он устанавливается и на хосты с приложениями и на поддерживающую инфраструктуру.

Лично я люблю оценивать всю картину одним взглядом

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

    Для теста разрешил отслеживать трейсы и логи, формируемые при съеме метрик

    Для теста разрешил отслеживать трейсы и логи, формируемые при съеме метрик

Уровень

Элемент

Что происходит

1.1

Collector Agent

Снимает метрики системы через node-exporter, читает системные логи.

1.2

Collector Agent app

Снимает метрики /metrics от SDK приложения, читает логи контейнеров, пересылает трейсы.

2

Collector Gateway

Принимает всё по gRPC, добавляет лейблы, раскидывает по бэкендам.

3

Хранилища

Prometheus → метрики, Loki → логи, Tempo → трейсы.

4

Grafana

Дашборды, алерты, корреляция трёх видов данных.


1.1 Клиентские агенты хоста (Collector Agent)

Collector Agent отвечает за сбор системных метрик и логов авторизации на каждом хосте. Для этих целей используется контейнер с Node Exporter (метрики), а также парсятся логи /var/log/fail2ban.log и /var/log/auth.log для событий блокировки и аутентификации. Перед отправкой все метрики и логи обогащаются нужными лейблами/атрибутами, такими как имя хоста, имя сервиса и др.

Необходимую среду мы поднимаем с помощью Docker-Compose:

Скрытый текст
 x-logging:  &default-logging   driver: "json-file"   options:     max-size: "10m"     max-file: "1"     tag: "{{.Name}}"  x-common-labels: &default-labels   logging: "promtail"   logging_jobname: "containerlogs"   stackname: "docker-monitoring-stack-gpnc"  services:  # --- monitoring   otel-collector-agent:     image: otel/opentelemetry-collector-contrib:0.128.0     container_name: otel-collector-agent     restart: always     user: "0:4"     cpus: 0.15     mem_limit: 256m     ports:       - 4317:4317     volumes:       - "./configs/otel-collector-agent/config.yaml:/etc/otel-collector/config.yaml:ro"       - "/var/log/fail2ban.log:/var/log/fail2ban.log:ro"       - "/var/log/auth.log:/var/log/auth.log:ro"     command: ["--config=/etc/otel-collector/config.yaml"]     environment:       - GATEWAY_ENDPOINT=${MONITOR_HOST}       - HOST_HOSTNAME=${HOST_HOSTNAME}     networks:       - monitor-net     depends_on:       - node-exporter     labels:       <<: *default-labels       component: "otel-collector"     logging: *default-logging    node-exporter:     image: prom/node-exporter:v1.9.1     container_name: node-exporter     restart: always     cpus: 0.15     mem_limit: 256m     pid: "host"     volumes:       - /proc:/host/proc:ro       - /sys:/host/sys:ro       - /:/rootfs:ro     command:       - '--path.procfs=/host/proc'       - '--path.rootfs=/rootfs'       - '--path.sysfs=/host/sys'       - '--collector.filesystem.ignored-mount-points=^/(sys|proc|dev|host|etc)($$|/)'     networks:       - monitor-net     labels:       <<: *default-labels       component: "node-exporter"     logging: *default-logging   networks:   monitor-net:     driver: bridge 

Конфигурация коллектора:

Скрытый текст
# configs/otel-collector-agent/config.yaml service:   pipelines:     metrics:       receivers: [prometheus]       processors: [batch]       exporters:          - otlp/gateway         # - debug      logs:       receivers:          - filelog/fail2ban         - filelog/auth       processors:          - attributes/custom         - attributes/add_loki_label         - batch       exporters:          - otlp/gateway         # - debug    # --- точки экспорта метрик exporters:   otlp/gateway:     endpoint: "${GATEWAY_ENDPOINT}:4317"     tls:       insecure: true     timeout: 15s     sending_queue:       enabled: true       num_consumers: 10       queue_size: 5000     retry_on_failure:       enabled: true       initial_interval: 10s       max_interval: 30s       max_elapsed_time: 2m   # уровни дебага detailed, normal, basic   debug:     verbosity: detailed  receivers:   otlp:     protocols:       grpc:         endpoint: 0.0.0.0:4317       http:         endpoint: 0.0.0.0:4318    # логирование   filelog/fail2ban:     include:       - /var/log/fail2ban.log     poll_interval: 5s     start_at: end     include_file_path: true     include_file_name: true     operators:       # кастомный атрибут       - type: add         field: attributes.service_name         value: fail2ban        - type: regex_parser         # ! Основной парсер. Разбирает fail2ban-строку на части:         regex: '(?P<timestamp>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d{3}).*?(?P<detected_level>NOTICE|INFO)\s+\[(?P<target>[^\]]+)\] (?P<msg>.*)'         parse_to: attributes     filelog/auth:     include:       - /var/log/auth.log     poll_interval: 5s     start_at: end     include_file_path: true     include_file_name: true     operators:       # кастомный атрибут       - type: add         field: attributes.service_name         value: auth        - type: regex_parser         regex: '.*New session\s+(?P<login_session>\d+)\s+of user\s+(?P<login_user>\S+)'         parse_to: attributes    # --- prometheus node_exporter   prometheus:     config:       scrape_configs:         - job_name: node-exporter           scrape_interval: 30s           scrape_timeout: 20s           metrics_path: /metrics           static_configs:             - targets: ["node-exporter:9100"]               labels:                 container: 'node-exporter'                 node: ${HOST_HOSTNAME}   processors:   batch:     timeout: 10s             # ← отправлять не реже, чем раз в 10 секунд     send_batch_size: 200     # ← при достижении 200 метрик/логов отправить батч сразу  # --- атрибуты   # --- кастомные   attributes/custom:     actions:       - action: insert         key: node         value: ${HOST_HOSTNAME}  # --- лейблы   attributes/add_loki_label:     actions:       - action: insert         key: loki.attribute.labels         value: service_name, node, detected_level, login_session, login_user 

В коллекторе реализованы следующие ключевые функции:
Сбор метрик

  • через ресивер prometheus с Node Exporter, опрашивается каждые 30 секунд для получения данных о состоянии системы.

Сбор логов:

  • из файлов /var/log/fail2ban.log и /var/log/auth.log через ресиверы filelog/fail2ban и filelog/auth.

  • Для каждого лога настраиваются парсеры regex_parser, которые вытаскивают полезные поля (например, для fail2ban — timestamp, detected_level, target, msg; для auth — login_session, login_user).

  • Добавляются кастомные атрибуты (service_name и другие).

Процессоры:

  • batch: группирует метрики и логи, чтобы отправлять их пакетами (batch), что повышает производительность передачи.

  • attributes/custom: добавляет дополнительный атрибут node с именем текущего хоста.

  • attributes/add_loki_label: формирует набор лейблов (service_name, node, detected_level, login_session, login_user) для дальнейшей фильтрации в системах логирования вроде Loki.

Экспортеры:

Все метрики и логи передаются в указанный otlp/gateway . Поддерживаются очереди, ретраи на случай ошибок сети.

  • Есть отладочный экспортер debug для локальной разработки.

  • Вся конфигурация легко масштабируется и кастомизируется, переменные окружения используются для гибкости.


1.2 Клиентские агенты приложения (Collector Agent app)

Collector Agent приложения собирает метрики приложения (например, с помощью prometheus_fastapi_instrumentator), читает и парсит логи, а также собирает трейсы из кода приложения (opentelemetry.instrumentation.fastapi).

Рекомендация: Старайтесь, чтобы логи приложения были в формате ключ-значение, например JSON. Это значительно упрощает их дальнейший разбор на атрибуты.

Конфигурация коллектора:

Скрытый текст
# configs/otel-collector-agent/config.yaml service:   pipelines:     traces:       receivers: [otlp]       processors: [batch]       exporters:          - otlp/gateway         # - debug      metrics:       receivers: [prometheus]       processors: [batch]       exporters: [otlp/gateway]      logs:       receivers: [filelog/docker]       processors:          - filter/drop_unavailable         # - filter/drop_service         # - filter/drop_info         - attributes/custom         - attributes/add_loki_label          - attributes/delete_attributes         - batch       exporters:          - otlp/gateway         # - debug    # --- точки экспорта метрик exporters:   otlp/gateway:     endpoint: "${GATEWAY_ENDPOINT}:4317"     tls:       insecure: true     timeout: 15s     sending_queue:       enabled: true       num_consumers: 10       queue_size: 5000     retry_on_failure:       enabled: true       initial_interval: 10s       max_interval: 30s       max_elapsed_time: 2m   # уровни дебага detailed, normal, basic   debug:     verbosity: detailed  # --- точки импорта метрик receivers:   otlp:     protocols:       grpc:         endpoint: 0.0.0.0:4317       http:         endpoint: 0.0.0.0:4318   # --- docker logs via filelog   filelog/docker:     include:       - /var/lib/docker/containers/*/*-json.log     poll_interval: 5s     start_at: end     include_file_path: true     include_file_name: true     operators:       - type: json_parser         id: parser-docker         parse_from: body         parse_to: attributes         on_error: drop       - type: json_parser         parse_from: attributes.log         parse_to: attributes         on_error: drop         if: 'attributes.log != nil and attributes.log matches "^\\{"'       # кастомный атрибут       - type: add         field: attributes.app         value: chuyan   # --- prometheus для метрик   prometheus:     config:       scrape_configs:         - job_name: cadvisor           scrape_interval: 30s           scrape_timeout: 20s           static_configs:             - targets: [ 'cadvisor:8080' ]               labels:                 container: 'cadvisor'                 node: ${HOST_HOSTNAME}         - job_name: app           scrape_interval: 30s           scrape_timeout: 20s           metrics_path: /metrics           static_configs:             - targets: ["front0ui:8010"]               labels:                 container: 'front0ui'                 node: ${HOST_HOSTNAME}   # --- обработка данных processors:   batch:     timeout: 10s             # отправлять не реже, чем раз в 10 секунд     send_batch_size: 200     # при достижении 200 метрик/логов отправить батч сразу  # --- атрибуты   # --- кастомные   attributes/custom:     actions:       - action: insert         key: node         value: ${HOST_HOSTNAME}   # --- удаляем лишние    attributes/delete_attributes:     actions:       - action: delete         key:  log.file.path       # - action: delete       #   key: log.file.path   # --- лейблы   attributes/add_loki_label:     actions:       - action: insert         key: loki.attribute.labels         value: app, node, container_name, trace_id, span_id  # --- фильтрация   # --- Ошибка  UNAVAILABLE   filter/drop_unavailable:     error_mode: ignore     logs:       log_record:         - 'IsMatch(body, "(?i)StatusCode\\.UNAVAILABLE") or IsMatch(attributes["log"], "(?i)StatusCode\\.UNAVAILABLE")'   # --- сервисов   filter/drop_service:     error_mode: ignore     logs:       log_record:         - 'IsMatch(attributes["log"], "(?i)(/health|/metrics)")'   # --- логов по info   filter/drop_info:     error_mode: ignore     logs:       log_record:         - 'IsMatch(attributes["level"], "(?i)(info)")'   

В это коллекторе реализуется функции:
Прием трассировок:

  • через ресивер otlp, принимаются span-данные от приложений (например, от микросервисов, использующих OpenTelemetry SDK). Данные поступают в режиме реального времени, каждое входящее соединение валидируется.

Прием логов:

  • filelog/docker: читает docker-логи из каждого контейнера, путь: /var/lib/docker/containers//-json.log.

  • Для парсинга применяется каскад json_parser (разбор тела лога и атрибута log) — таким образом, все заметки переводятся в формат атрибутов OpenTelemetry.

  • Добавляется кастомный аттрибут app: chuyan.

Процессоры:

  • batch: собирает трассировки и логи в пакеты для эффективной отправки , что минимизирует сетевые накладные расходы.

  • filter/drop_unavailable — убирает логи с ошибками статуса UNAVAILABLE.

  • attributes/loki_labels: определяет и присваивает специальные лейблы (webapp_name, environment, response_code, request_method, error_level) для фильтрации и поиска в log-стореджах типа Loki или Elasticsearch.

  • Удаляется технический аттрибут log.file.path.

Экспортеры:

  • Все метрики и логи передаются в указанный otlp/gateway. При отладке можно переключить на дебаг в консоль.


2 Коллектор-шлюз (Collector Gateway)

  • Получает всё по gRPC от агентов.

  • Фильтрует и добавляет атрибуты (лейблы хоста, сервиса).

  • Маршрутирует данные в нужный бэкенд.

Конфигурация коллектора-шлюза:

Скрытый текст
# configs/otelcol/otelcol.yaml # --- точки экспорта метрик exporters:   prometheus:     endpoint: "0.0.0.0:9464"   loki:     endpoint: "http://loki:3100/loki/api/v1/push"   otlp/tempo:      endpoint: tempo:4417     tls:       insecure: true   debug:     verbosity: detailed    # уровни дебага detailed, normal, basic  # --- точки импорта метрик receivers:   otlp:     protocols:       grpc: { endpoint: 0.0.0.0:4317 }       http: { endpoint: 0.0.0.0:4318 }  processors:   batch: {}  service:   pipelines:     traces:       receivers: [otlp]       processors: [batch]       exporters:          - otlp/tempo         # - debug     metrics:       receivers: [otlp]       processors: [batch]       exporters: [prometheus]     logs:       receivers: [otlp]       processors: [batch]       exporters: [loki]  

На практике всё просто: при необходимости можно централизованно добавлять лейблы и атрибуты к телеметрии, а также осуществлять фильтрацию данных.


3 Хранилища + 4 Grafana

Наш сервер мониторинга состоит из следующих компонентов:

  • grafana — веб-интерфейс для визуализации метрик, логов и трассировок. Работает как дашборд для наблюдения за всей системой.

  • prometheus — система сбора и хранения метрик. Опрашивает экспортёры (например, node-exporter, cadvisor) и другие источники, хранит и отдает метрики для анализа.

  • loki — cистема сбора и хранения логов от контейнеров и других сервисов, тесно интегрирована с Grafana.

  • tempo — распределённое хранилище трассировок запросов (tracing), чтобы отслеживать путь запросов через сервисы (Distributed Tracing).

  • cadvisor — сборщик метрик использования ресурсов контейнерами Docker (CPU, память, диски и др.). Источник данных для мониторинга контейнеров для Prometheus.

  • node-exporter — экспортирует метрики состояния хоста (нагрузка CPU, память, диски, файловые системы и др.) для Prometheus.

  • alertmanager — обработчик алертов Prometheus, отправляет уведомления при срабатывании правил (в Slack, email и т.д.).

  • uncomplicated-alert-receiver — простой веб-приемник алертов из alertmanager, далее опционально может пересылать их или обрабатывать.

  • promtail — агент, собирает логи контейнеров и отправляет их в Loki. Можно использовать только opel-коллектор, но я решил его оставить, чтобы навык работы с ним не потерять.


Трассировки и корреляция

Трейсы собираются через OTEL SDK и отправляются в агент. Главная сложность — не в сборе, а в связывании данных:

  • В логах оставляем атрибут trace_id.

  • В Grafana создаем связи в разделе Data Sources для Loki и Tempo:

для Loki

для Loki
для Tempo

для Tempo

Теперь, открывая нужный лог в Grafana, можно перейти к соответствующему трейсy.

переход на трейс лога

переход на трейс лога
изучаем трейс

изучаем трейс

Аналогично — от трейса можно перейти к логам.

Это существенно ускоряет диагностику и анализ работы системы.


Итоги и выводы

  1. Унификация. OpenTelemetry Collector стал единым шлюзом для любой телеметрии.

  2. Гибкая маршрутизация. Любые изменения поведения бэкендов не требуют доработки клиентов.

  3. Простое масштабирование. Все сервисы упакованы в контейнеры, обновляются через Ansible или CI/CD.

  4. Широкие возможности. Grafana объединяет метрики, логи и трейсы в единой визуальной среде с алертами.

В результате я получил полнофункциональную observability-платформу: теперь вижу, где возникают задержки, когда появляются ошибки и как запросы «путешествуют» между сервисами. Переход на OpenTelemetry и знакомые бэкенды — это не только про технологический стек, но и про формирование DevOps-культуры наблюдаемости в компании.

Внедрял всё это не «ради моды», а чтобы упростить управление системой, заложить возможности масштабирования и, главное, раньше пользователей обнаруживать и устранять инциденты.

Спасибо за внимание!


ссылка на оригинал статьи https://habr.com/ru/articles/919214/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *