Обо мне

Внедрял SIEM-системы и системы комплексного мониторинга. Подключал и парсил источники, нормализировал события различных доменов данных. Сейчас занимаюсь анализом данных, разработкой контентных модулей для решения задач мониторинга и информационной безопасности в компании VolgaBlob.

Вводная информация

Ознакомившись с опытом использования трассировок для мониторинга микросервисных архитектур от сообщества и крупных игроков в области Observability (DataDog):

  1. Как мы делаем трейсинг в условиях тысяч сервисов и миллионов спанов в секунду

  2. Как мы в Авито анализируем 5 миллионов трейсов и проводим архитектурный надзор

  3. APM Terms and Concepts

Мы решили реализовать инструменты анализа трассировок на нашем продукте Smart Monitor, чтобы оценить правильность выбранного направления в разработке функциональных модулей и возможности системы.

Трассировки и стандарт OpenTelemetry

Стандарт хорошо задокументирован, включая основные компоненты и способы интеграции. Поэтому подробно останавливаться на этом не буду. Вот полезные ссылки:

  1. Observability primer

  2. Мониторинг бизнес-процессов с помощью OpenTelemetry

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

Что такое Smart Monitor

Немного про сам продукт. Smart Monitor является платформой сбора и анализа данных. Ключевые возможности:

  1. Движок pipeline like запросов

  2. Набор компонентов и модулей для IT-мониторинга (РСМ, инвентаризация) и SIEM систем (UBA, Менеджер инцидентов)

  3. Дашборды, задачи по расписанию

Подробнее описано на сайте. Под капотом хранилище OpenSearch, плюс возможность использовать ClickHouse (нативно), либо подключать иные базы данных через jdbc.

Какие задачи мы хотели решить

Вдохновившись докладом Как мы в Авито анализируем 5 миллионов трейсов и проводим архитектурный надзор, было принято решение на базе платформы Smart Monitor решить следующие задачи:

  1. Сделать удобные визуальные инструменты анализа трассировок и сервисов — решается дашбордами и поисковым движком.

  2. Построить карту инфраструктуры (провести инвентаризацию сервисов и операций, построить модель зависимостей) — решается модулем инвентаризации.

  3. Сформировать инструмент поиска первопричин проблем — решается формированием ресурсно-сервисной модели микросервисов.

  4. Предоставить возможность создания и управления жизненным циклом инцидентов — решается поисковым движком и модулем Менеджер инцидентов.

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

Как выглядит архитектура сбора

Здесь ничего нового не было изобретено. Использовался базовый механизм сбора данных в OpenSearch, предполагающий использование Otel Collector в связке с Data Prepper для формирования документов трассировок и их отправки в кластер OpenSearch. В дальнейшем мы планируем отказаться от Data Prepper в пользу собственного сервиса трансформации данных. Однако для первой версии модуля трассировок этого компонента достаточно.

Схема транспорта данных
Схема транспорта данных

Откуда данные

Для быстрого старта нам нужен был продукт, построенный на микросервисной архитектуре. Мы взяли демо-приложение, предоставляемое OpenTelemetry. Сервис является магазином телескопического оборудования, который состоит из 19 сервисов. Сервисы разворачиваются в докере.

Архитектура сервисов
Архитектура сервисов
Интерфейс магазина
Интерфейс магазина

Как выглядят данные

В модуле использовалась два домена данных:

  1. спаны трассировок

  2. записи связей сервисов

Вот так выглядят события трассировки
{
  "_index": "otel-v1-apm-span-000001",
  "_type": "internal:otel-v1-apm-span:",
  "_id": "efcf79b98104f430",
  "_score": 0,
  "_source": {
    "droppedLinksCount": 0,
    "traceId": "3371e97cd5b83f8e0619c919e39cda6a",
    "instrumentationScope": {
      "name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
      "version": "0.60.0"
    },
    "resource": {
      "attributes": {
        "process@runtime@name": "go",
        "host@name": "1ad5865b3c7d",
        "os@description": "Alpine Linux 3.21.3 (Linux 1ad5865b3c7d 6.10.14-linuxkit #1 SMP Sat May 17 08:28:57 UTC 2025 aarch64)",
        "service@name": "product-catalog",
        "telemetry@sdk@version": "1.35.0",
        "process@executable@name": "product-catalog",
        "telemetry@sdk@name": "opentelemetry",
        "docker@cli@cobra@command_path": "docker compose",
        "process@command_args": "[\"./product-catalog\"]",
        "process@pid": 1,
        "process@executable@path": "/usr/src/app/product-catalog",
        "process@runtime@description": "go version go1.22.12 linux/arm64",
        "os@type": "linux",
        "telemetry@sdk@language": "go",
        "process@owner": "root",
        "process@runtime@version": "go1.22.12"
      }
    },
    "kind": "SPAN_KIND_SERVER",
    "traceGroupFields": {
      "endTime": "2025-07-22T10:49:41.200Z",
      "durationInNanos": 24000000,
      "statusCode": 0
    },
    "droppedEventsCount": 0,
    "traceGroup": "HTTP GET",
    "serviceName": "product-catalog",
    "parentSpanId": "3a803bed798334f4",
    "spanId": "efcf79b98104f430",
    "traceState": "",
    "name": "oteldemo.ProductCatalogService/GetProduct",
    "startTime": "2025-07-22T10:49:41.197253796Z",
    "links": [],
    "droppedAttributesCount": 0,
    "durationInNanos": 57583,
    "endTime": "2025-07-22T10:49:41.197311379Z",
    "events": [
      {
        "name": "feature_flag",
        "attributes": {
          "feature_flag@variant": "on",
          "feature_flag@key": "productCatalogFailure",
          "feature_flag@provider_name": "flagd"
        },
        "time": "2025-07-22T10:49:41.197286712Z",
        "droppedAttributesCount": 0
      },
      {
        "name": "Error: Product Catalog Fail Feature Flag Enabled",
        "attributes": {},
        "time": "2025-07-22T10:49:41.197289962Z",
        "droppedAttributesCount": 0
      }
    ],
    "status": {
      "code": 2,
      "message": "Error: Product Catalog Fail Feature Flag Enabled"
    },
    "span": {
      "attributes": {
        "rpc@method": "GetProduct",
        "rpc@service": "oteldemo.ProductCatalogService",
        "app@product@id": "OLJCESPC7Z",
        "rpc@grpc@status_code": 13,
        "rpc@system": "grpc"
      }
    }
  }
}
А так связи между сервисами через операции
{
  "_index": "otel-v1-apm-service-map",
  "_type": "internal:otel-v1-apm-service-map:",
  "_id": "KXDQMdubpQ73lmXPjpx3vA==",
  "_score": 1,
  "_source": {
    "kind": "SPAN_KIND_CLIENT",
    "traceGroupName": "HTTP GET",
    "destination": {
      "resource": "Currency/Convert",
      "domain": "currency"
    },
    "serviceName": "frontend",
    "hashId": "KXDQMdubpQ73lmXPjpx3vA==",
    "target": null
  }
}

События карты сервисов формируются компонентом Data Prepper и являются результатом ETL-процесса трансформации данных трассировок. Сущность связи интерпретируется вот так:

сервис frontend вызывает ресурс Currency/Convert у сервиса currency.

Дашборды для анализа трассировок

Первый этап — формирование дашбордов мониторинга и анализа трассировок и сервисов. Структура дашбордов и переходов между ними построена по логике от общего к частному.

Переходы между дашбордами
Переходы между дашбордами

Результаты разработки дашбордов на скриншотах, я не буду останавливаться подробно на каждом из них, только подсвечу интересные моменты.

Общий дашборд по трассировкам
Общий дашборд по трассировкам
Детализация трассировки
Детализация трассировки

На дашборде детализации трассировки есть пара интересных моментов:

  1. Диаграмму вызовов мы позаимствовали у OpenSearch Dashboards, реализацию собственной диаграммы отложили на следующий релиз модуля.

    Диаграмма вызовов
    Диаграмма вызовов
  2. Граф вызовов очень приятный (написали сами) и визуализирует маршруты с ошибками в контексте трассировки.

    Карта маршрутов трассировки
    Карта маршрутов трассировки

Остальные дашборды.

Дашборд по сервисам
Дашборд по сервисам
Детализация сервиса
Детализация сервиса
Детализация ресурса
Детализация ресурса

Визуализации строятся на движке поиска, который использует pipeline like язык.

Пример запроса для диаграмма временного ряда операций
Пример запроса для диаграмма временного ряда операций

$service$ и $resource$ — это токены, значения для которых формируются из фильтров на дашборде.

Инвентаризация сервисов

Механизм инвентаризации простой:

  1. Из otel-v1-apm-service-map формируем измерения (сервисы, ресурсы) и связи.

  2. Используем измерения и связи в модуле инвентаризации.

В результате имеем два типа актива, связанные между собой.

Активы сервисов и ресурсов
Активы сервисов и ресурсов
Связи между сервисами и ресурсами
Связи между сервисами и ресурсами

Это позволяет построить маршруты между сервисами через ресурсы.

И получить полную карту маршрутов.

Модель здоровья микросервисов

Модель здоровья строиться на трех основных показателях сервисов:

  1. Количество операций в единицу времени — деградирует при отсутствии операций.

  2. Количество ошибок операций — деградирует при росте ошибок.

  3. Продолжительность операций — деградирует при увеличении задержек.

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

  1. Шаблоны метрик (для каждого сервиса всегда доступны три метрики).

  2. Сформированные связи между сервисами в рамках инвентаризации.

Вот так выглядит шаблон метрики - используется jinja template
{
  "title": "Ошибки операций",
  "description": "Количество спанов, выполненных с ошибкой",
  "source": {
    "search": {
      "query": "source otel-v1-apm-span\n| search serviceName=\"{{ service }}\"\n| peval is_error_span = if(status.code > 1, 1, 0)\n| aggs sum(is_error_span) as errors by name",
      "time_field": "startTime",
      "earliest": "now-4m",
      "latest": "now"
    },
    "schedule": {
      "cron": {
        "expression": "* * * * *",
        "timezone": "Europe/Moscow",
        "schedule_delay": 0
      }
    },
    "lock_duration_seconds": 10,
    "calculation_field_name": "errors"
  },
  "type": "NUMBER",
  "units": "ед",
  "drilldown": null,
  "thresholds": {
    "static": {
      "base_severity": "NORMAL",
      "ranges": [
        {
          "above": 0,
          "severity": "NORMAL"
        },
        {
          "above": 2,
          "severity": "HIGH"
        }
      ]
    }
  },
  "split_by_entity_params": {
    "enabled": true,
    "entity_key_field_name": "name",
    "aggregation_function": "MAX",
    "entity_thresholds": [
      {
        "entity_pattern": "*",
        "thresholds": {
          "static": {
            "base_severity": "MEDIUM",
            "ranges": [
              {
                "above": 0,
                "severity": "NORMAL"
              },
              {
                "above": 2,
                "severity": "HIGH"
              }
            ]
          }
        }
      }
    ]
  }
}

Скрипт запускается по расписанию и не является частью системы. Он использует API для создания сервисов и метрик в нотации РСМ и запускается отдельной службой.

В результате, мы получаем модель здоровья.

Граф ресурсно-сервисной модели
Граф ресурсно-сервисной модели
Слой модели микросервисов
Слой модели микросервисов

Помимо прямой зависимости от собственных метрик, на состояние сервиса влияет состояние дочерних сервисов.

Регистрация инцидентов

Завершает упаковку модуля анализа трассировок правило для регистрации инцидентов. Мы сформировали правило, срабатывающее при наличии ошибок в распределённой трассировке. Оно выявляет ошибки в трассировке относительного родительского сервиса — того, с которого началась обработка запроса.

Настройка правила
Настройка правила

В рамках данного правила формируется следующий инцидент.

Выводы

Итак, мы решили задачу прикладного применения современных стандартов мониторинга в нашей платформе и упаковали объекты знаний и конфигурации в контентный модуль. Компоненты продукта решают задачи построения системы мониторинга состояния микросервисов на базе трассировок OpenTelementry. Но сделана только база, адаптировать решение придется на каждой инсталляции. Помимо этого остается ряд вопросов, которые необходимо решить:

  1. Операции агрегации спанов трассировок на OpenSearch выполняются медленно при больших объемах данных. Возможности для оптимизации такого рода появились только в 3.0. В частности — Star-tree index. Но, кажется, вариант использовать ClickHouse для процессинга трассировок выглядит более удачным. Такие задачи уже решались и не раз. Поэтому смотрим в эту сторону.

  2. Ввиду количества микросервисов в продуктах и значимой разнице в профилях нагрузки (количество запросов, длительность обработки, сложность операций), метрики должны рассчитываться адаптивно. Использования заданных порогов для метрик приведет только к большому количеству false-positive состояний. Доверия к такой системе не будет.

  3. Веса между сервисами в рамках ресурсно-сервисной модели должны рассчитываться динамически, используя коэффициент GB (Graceful Degradation).

  4. От использования Data Prepper и визуализации OpenSearch Dashboards нужно отказываться. Качество этих open-source решений не соответствует качеству продукта.

  5. Большие инсталляции содержат очень много сервисов (тысячи), строить модели и инвентаризировать такие инсталляции нужно с применением дополнительных логических фильтров. Делать множество моделей по доменам продукта, критичности для бизнеса и пользователей.

На этом все, если у вас есть опыт работы с трассировками и альтернативными решениями для их анализа — буду рад обменяться опытом в комментариях.

Комментарии (0)