Десятки, а иногда и сотни тысяч событий в день. Каждое — потенциальная авария, а может, просто шум. L1-инженеру нужно решить: добавить событие к инциденту? Создать новый? А может, это часть уже закрытого? Или всё серьёзнее — и перед нами экосистемный сбой, затрагивающий десятки сервисов?

Раньше мы в МТС всё классифицировали вручную. Но при таком объёме и разнообразии инфраструктуры быстро поняли, что нужна автоматизация. Слишком велик риск пропустить важное, не найти корень проблемы, потратить драгоценные минуты в критический момент.

В поисках решения придумали использовать то, что есть: богатую разметку от дежурных инженеров, накопленную за годы наблюдений. Так начался наш путь к инструменту, который с помощью ML группирует события в осмысленные цепочки, распознаёт инциденты и помогает дежурным работать точнее, быстрее и спокойнее. 

В этой публикации мы — Михаил Копытин, руководитель команды разработки, и Евгений Лачугин, руководитель экосистемной команды поддержки в МТС Web Services — расскажем, как построили решение, какие архитектурные решения приняли, какие грабли собрали и как достигли точности выше 80%.

Масштаб задачи 

В портфеле МТС — больше 500 продуктов. Мы взаимодействуем с тысячами информационных систем, и в консоль мониторинга ежемесячно поступает более 200 тысяч критических событий. Управлять таким объёмом аварийных сигналов сложно.

У нас есть Mission Control Center (МСС) — команда дежурных инженеров, отвечающих за задачи консолидации, агрегации и корреляции событий на базе мониторинга. Нагрузка колоссальная — не каждый справится с таким объёмом и уровнем стресса.

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

Ситуация осложняется спецификой продуктов. У каждого — свой технологический стек, разные варианты мониторинга и уровень автоматизации. А ещё — десятки систем мониторинга, которые интегрированы под общим зонтичным решением на базе Zabbix, Victoria Metrics, Prometheus и так далее.

Немного терминологии:

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

  • Дочерние события — это события, симптомы, которые следуют за корневым. По сути это его следствие. Например, у нас возникла проблема в ЦОДе. Корневое событие здесь — алерт о недоступности сетевого оборудования. Дочерними станут события по сервисам и серверам, которые наступят позже.

  • Синглеты — частные события. Вызванные, например, ошибкой в коде или разовой активностью пользователя в маркетинговой рассылке. Они самостоятельные и «дочек» у них нет.

Представьте себе «вечер пятницы», когда в ЦОДе происходит крупный инцидент. На экране — шквал однотипных критических событий: серверы недоступны, сеть перегружена, сервисы не отвечают.

Это похоже на последствия одной глобальной проблемы, только без первоначального root cause и понимания связи между событиями, поэтому крайне сложно определить, где именно лежит первопричина. Это может быть и сбой в системе электропитания, отказ кондиционирования или что угодно ещё.

Можно предположить, что причиной стал DDoS, или авария в ЦОДе, или проблемы сети, или даже группы в Active Directory. Но на самом деле без контекста по событиям правильно определить первопричину сбоя нельзя. Вредно даже пытаться.

Сейчас эту работу делает дежурный инженер — классифицирует события вручную, в режиме реального времени. А если речь о серьёзной аварии — счёт идёт на секунды, и время начинает стоить очень дорого.

Автоматизация на основе алгоритмов и ML

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

  • Корреляция на основе географического расположения — ЦОД / серверная.

  • Корреляция событий бизнес-логики и инфраструктуры.

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

Если есть ЦОД, расположенный в конкретной локации, с определённым количеством железа, и мы видим, что сразу несколько серверов начинают сбоить в течение короткого промежутка времени — кажется очевидным, что речь об одной и той же проблеме.

Но постоянно возникали нюансы, которые ломали эти схемы. Например:

  • Сбой происходил сразу в двух локациях, в короткий промежуток времени, и автоматизация не справлялась.

  • Или, наоборот, случается локальный сбой, а последствия — очень серьёзные.

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

И мы подумали: дежурные MCC, обрабатывающие критические события, это люди с колоссальной насмотренностью. Они уже предобработали огромный массив данных, у них есть накопленные знания, и всё это — бесценный ресурс.

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

Как работает MCC Contexter

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

Базовые определения

Причинно-следственные связи образуют древовидную структуру, поэтому на поиске корневой причины (top cause) сложности не заканчиваются.

Дочерние события могут сами становиться самостоятельными узлами — так с ними оказываются связаны последующие зависимости. Поэтому, когда мы говорим о корневом событии, важно различать:

  • top cause — то самое изначальное событие, с которого всё началось,

  • просто событие в контексте, к которому можно что-то привязать.

Чтобы лучше объяснить, как работает MCC Contexter — инструмент, который как раз помещает события в контекст, разберём несколько статистических графиков. Наша цель — понять, есть ли в событии контекст, насколько он глубокий и можно ли вообще это осмыслить без изрядной доли безумия и отваги.

Исследование связи событий во времени

Начнём с довольно насыщенной гистограммы корреляции событий по времени.

Здесь ось Y показывает количество событий — сколько раз встретилось определённое значение. А по оси X отложена дельта по времени между событием и его ближайшей причиной.

Мы наложили на гистограмму сразу два распределения:

  • от 0 до 600 секунд (красное),

  • и от 0 до 600 минут (серое).

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

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

Анализ статистики связей

Переходя к анализу статистики связей, будем различать два вида семейств:

  • Непосредственное семейство (Immediate family) события и ближайшие события, отделённые от первого одним ребром графа. 

  • Полное семейство (family) — всё, что удалось привязать к общей корневой причине.

Посмотрим, как семейства распределены по количеству членов в непосредственном окружении. Так выглядит полное:

Корневая причина — N следствий
Корневая причина — N следствий

Глядя на гистограмму, можно сказать, что это такой «город контрастов». Подавляющее большинство — это единичные события, просто синглеты. А дальше идёт длинный затухающий (не экспоненциально, а по степенному закону) хвост. То есть случиться может примерно всё, что угодно, вплоть до тысячи событий в семействе.

Теперь посмотрим, как распределяются непосредственные семейства. Здесь похожая картина — 89% не имеют потомков (дочерних событий).

Причина — N «прямых» следствий
Причина — N «прямых» следствий

Может быть, дежурным было бы проще, если бы система работала более равномерно, но — увы — равновесия здесь нет. Это режим перманентного стресса и перепадов.

Что знает MCC Contexter

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

  • MonitoringEvent — то, что дали дежурные. Эталон релевантности.

  • MonitoringEventAttributes — более сложные источники данных, которые структурируются по принципу ключ-значение. В роли ключа может выступать что угодно.

  • Inventory Management System — справочная информация о системе. Достаточно медленно обновляющийся источник.

Сейчас версия MCC Contexter работает только с «красной таблицей» —  MonitoringEvent.

Методы кодирования признаков

Атрибуты кодируются по принципу вхождения или отсутствия признака. Это так называемый one-hot encoding, который игнорирует порядок слов. Например, если приходит сообщение, вроде: «Warning: disk space is exceeded», нам понятно, что речь о проблемах с дисковым пространством. И даже если слова переставить — «Warning: space disk is exceeded» — смысл всё равно ясен. Мы исходим из того, что контекст достаточно узкий, поэтому пока допускаем, что порядок слов не играет решающей роли.

Для отбора признаков используем сортировку по TF-IDF — стандартный подход из информационного поиска. Он позволяет оценивать, насколько важен термин в конкретном сообщении по сравнению с другими. Выбираем атрибуты с самым большим TF-IDF, а атрибуты с низким — игнорируем, и многие из них мы больше не встретим.

Выбор решения

Мы выбирали архитектуру, отталкиваясь от реальных условий задачи. Вот что было важно:

  • KPI по точности: система должна работать с точностью не ниже 0,75.

  • Наличие богатой обучающей выборки из данных, которые собирают и размечают сами дежурные. Качественно размеченные события отлично вписываются в ML-парадигму.

  • Учёт специфики данных: язык логов сильно отличается от обычной человеческой речи.

Система из трёх моделей

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

Каждая модель отвечает за свою часть классификации, а вместе они работают как слаженная команда:

S — модель синглетов

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

R — модель корневого события (Root)

Как и S, работает на векторе одного события и принимает бинарное решение: является ли событие корнем инцидента или нет. Именно с этой модели начинается построение дерева и решается, какую роль событие играет в модели C.

C — модель продолжения (Continue)

Самая сложная и нагруженная модель, потому что взаимодействует со всеми событиями, которые предклассифицированы R-моделью, как достойные следующих привязок. Здесь работа с комбинаторикой событий. Модель получает вектор, описывающий оба события («кандидат-родитель» и «кандидат-дочь»), и также принимает бинарное решение: продолжать цепочку или нет.

Как выглядит пространство исходов

На верхнем уровне — S-модель, которая отсекает синглеты. Дальше все возможные исходы раскладываются по четырём классам бинарной классификации: False-Negative, True-Negative, False-Positive, True-Positive.

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

На втором уровне R сразу выпадают False-Positive и True-Positive из модели синглетов, потому что выходят из игры. Дальше в работу вступают R- и C-модели и работают с тем, что осталось.

Алгоритм обработки события

В довершение ещё пара ударов по тому же самому гвоздю — диаграмма того, как это работает на уровне логики кода:

Всё происходит примерно так же:

  1. Сначала событие проверяется моделью S. Если оно синглет — отбрасываем.

  2. Если нет — передаём в R-модель, которая решает, может ли это событие быть корнем (родителем).

  3. Если может, оно остаётся в системе как потенциальный родитель (Continue).

Модель C работает с комбинаторикой — значит, в ней крутится цикл проверки всех кандидатов-родителей по факту появления каждого потенциально дочернего события. Если подходящая пара нашлась — создаётся связь. Если ничего не нашлось, событие отбрасывается. Но если дочернее событие является также корневым, оно остаётся и дальше служит как кандидат на корень привязки.

Валидация моделей

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

На метрике бинарной классификации не будем акцентировать внимание. Понятно, что для такой неравновесной задачи с точки зрения населенности классов важны:

  • PPV (Positive Predictive Value) — метрика того, насколько можно доверять положительным прогнозам модели;

  • NPV (Negative Predictive Value) — насколько надёжны отрицательные прогнозы.

Всё начинается с матрицы 2×2 (желто-сине-голубой), которая представляет из себя пространство исходов и его плотность. Мы стремимся к тому, чтобы оно было заселено диагонально.

Ещё интересное здесь — центральная пирамида в верхней части. Это процесс валидации, где мы используем заранее отложенную часть выборки, для которой известны правильные ответы. Картинка показывает, как среднеквадратичное отклонение между реальностью и ответом модели зависит от уверенности, которую модель сама в себе декларирует.

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

Архитектура MCC Contexter и его потребление с точки зрения хардварных ресурсов

Здесь ничего необычного и сложного, никаких GPU. Схема такая:

  • Первый компонент — Airflow, он просто засасывает данные.

  • Дальше две инстанции KeyDB и два сервиса реализуют логику — прохождение через модели S, R и C.

  • В итоге всё это стекает в размеченные события, которые Sender передаёт в API, экспонируемый экосистемной командой поддержки.

В развитии системы от MVP к зрелому продукту важную роль играет этап гипотез, доработок и offline-тестов. Система уже крутится на проде и дежурные MCC могут смотреть, что делает Contexter и опционально выключать его по какой-то причине и с нужной регулярностью.

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

Результаты обратной связи: трубопроводная диаграмма

На верхнем уровне диаграммы — входной поток событий (INPUT). Дальше — исходы, закодированные цветом и буквенными метками:

  • S — синглет-модель,

  • R — корневая модель,

  • C — модель продолжения.

На диаграмме хорошо видно тот самый компромисс: мы вырезаем события с низкой уверенностью классификации по S-модели — и сознательно отправляем около 30% в зону UNDEFINED. Это делается  в пользу заказчика: поток событий хоть и становится урезанным, но зато обладает более надёжной классификацией.

Получается, что всё крутится вокруг потока из S → R → C, который на схеме непросто оценить количественно. Поэтому следующая таблица показывает результаты обратной связи в цифрах.

Главный показатель среди этих цифр — frontend-точность — процент правильно распознанных событий. Её непосредственно и видит заказчик. Точность нужно обязательно сопровождать цифрой охвата, потому что между этими двумя метриками всегда существует компромисс. На текущий момент:

  • Frontend-точность: 88.3% ± 0.2

  • Охват: 66%

Важно и можно проследить дальше, как точность меняется на каждом уровне — от S к R и далее к C. Ошибки на ранней стадии могут «отравить» всё дерево классификации:

  • если S-модель неправильно пометила событие,

  • R-модель получает неправильную вводную от S-модели,

  • C-модель получает неправильную вводную от R-модели, за которой, в том числе, может стоять правильная или неправильная вводная от S-модели.

Ещё мы внимательно следим за PPV (точностью положительных ответов) и NPV (точностью отрицательных). С этими метриками у нас были сложности, о которых поговорим дальше.

Какими были подводные камни и как мы их обходили

  • Непостоянство всех величин → Распределения, оценки неопределённостей.

Как вы уже поняли, чтобы заниматься такими задачами, нужно быть со статистикой хотя бы на «ты». Любое решение — это по сути A/B-тест, и оно должно быть статистически обосновано: величины сопровождаются оценками неопределённости, доверительными интервалами и пр.

  • Большая часть событий — синглеты → Отдельная модель, первая в потоке.

Это был приятный сюрприз, потому что мы легко достигли метрик успеха, которые нам ставились на «лёгком старте», просто за счёт классификации и запуска синглетной модели.

  • Вложенность связей → Не притягивать всё к top cause.

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

  • Разный вес классов → Не пытаться выровнять их в ML.

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

  • Низкие PPV (редкий класс) «из коробки» → «Каскадный» принцип создания обучающих выборок.

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

S-модель обучаем, концептуально размечая события: это синглет, это нет. Но для всего остального, что идёт ниже по течению, поскольку питается продуктами работы предыдущих, мы стали каскадно формировать обучающие выборки. Сначала запустили реальную S-модель, посмотрели, что она поставляет в R-модель, и размечали именно это, а не искусственно созданные выборки. Аналогично для C-модели. Это оказалось решением, которое нас сильно толкнуло вверх именно по показателю PPV.

  • Потенциально неограниченный рост числа событий-кандидатов для C-модели → Решение через TTL и параллелизацию сервиса.

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

Выводы и планы на будущее

Какие выводы мы сделали:

  • Корреляция и группировка больших объёмов аварийных событий — это сложно.

Интеграция MCC Contexter в нашу систему мониторинга позволила обрабатывать большие объёмы аварийных событий, с которыми раньше вручную работали дежурные. Было действительно сложно, а в каких-то критических ситуациях даже больно. Теперь мы делаем эту работу быстрее и эффективнее.

  • MCC Contexter повысил результативность обработки событий.

Это не замена дежурным — это инструмент, который помогает фокусироваться на действительно важных событиях и быстрее находить корень проблемы.

  • Машинное обучение — это не магия.

ML настраивается человеком. Да, модель обучается, но цели, структура, логика — всё задаёт автор. И если вы изначально поставите неверную метрику, модель просто будет хорошо выполнять не ту задачу.

Изначально, когда мы выдвинули гипотезу о внедрении ML в корреляцию, мы поставили цель — достичь 75% охвата всех событий. Но, как стало ясно, этого недостаточно. С точки зрения результативности нужно, как минимум, два показателя: охват и точность. На практике между ними всегда приходится искать баланс и идти на компромиссы, потому что точность важнее.

Что дальше

  1. Интеграция MCC Contexter в нашу ITSM-систему

Мы поняли, как это работает, а следующий шаг — переход от event-менеджмента к incident-менеджменту. Это значит:

  • работа не только с алертами, но и с инцидентами,

  • анализ связей между инцидентами,

  • подсветка критичных кейсов команде поддержки,

  • подсказки и гипотезы по решению (в том числе на основе прошлых решений коллег).

  1. Применение MCC Contexter в постмортемах

Мы хотим подключать Contexter при разборе инцидентов: искать root cause, оценивать достаточность мониторинга, принимать меры по нивелированию таких ситуаций в будущем, чтобы клиенты по возможности не замечали сбоев.

  1. Оптимизация мониторинга на основе инсайтов Contexter

Планируем использовать полученные данные для динамической настройки порогов, выявления «дыр» в мониторинге и рекомендаций командам по усилению точек контроля.

Главный вывод: машинное обучение — не волшебная палочка. Оно требует постоянной настройки, развития и участия экспертов. Только в тандеме искусственного интеллекта с естественным, можно достичь результатов, которые получились. Отдельное спасибо всей команде, кто так или иначе участвовал в разработке продукта.

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