Всем привет! Я Артём Седых, работаю ведущим разработчиком команды банковского сопровождения. Проекту уже девятый год, накопилась большая функциональная база и много легаси-кода с набором узких мест для возможной оптимизации. В данном наборе статей рассказываю про наш опыт разработки и внедрения собственной системы мониторинга бэкенда PHP с возможностью просмотреть, как именно проседают экшены, и даже спрогнозировать их поведение. Если не читали предыдущие части, ссылки выше. Сегодня, в третьей и заключительной части, рассмотрим мониторинг со стороны devops на дашбордах SLI/Apdex, поколдуем над статистическими методами для прогноза снижения производительности (рубрика «эксперименты»), поговорим об автоматических уведомлениях Grafana. Оценим перспективы развития, сравнительный анализ выбранного подхода и выводы по нашему опыту. Заваривайте любимый напиток, приятного прочтения!
Расследование проблемных параметров
В прошлой части было много дашбордов для изучения и оценки больше количественных показателей по проекту и экшенам с самой разнообразной агрегацией. Теперь попробуем получить информацию из метрики параметров. Дашборд покажет, какие именно параметры загружают систему дольше всего вместе с их количеством и количеством ошибок. Выполняется группировка по parameters.
Отследить, какие именно параметры имеют больше влияния на время работы системы
Для лучшей четкости картинки можно открыть в новой вкладке.

Тут уже вступает анализ конкретных данных, стоит обратить внимание: а всё ли нормально с этими параметрами, корректно ли обрабатываются и почему обработка так долго занимает. Добавлен фильтр по пользователю: если есть тяжелый пользователь с дашборда ТОПов, здесь увидим, какие параметры он запрашивает. На второй таблице можно точечно отследить конкретные запросы конкретного юзера с конкретными параметрами и получить по ним статистику. Часто помогает и наоборот: если на других дашбордах видно аномалию, этот дашборд покажет, при каком наборе параметров она возникает.
Дашборд SLI и Apdex
Со стороны бизнеса и devops первое, на что стоит обратить внимание, – конкретные показатели производительности по стандартам SRE (Site Reliability Engineering) вроде SLI и Apdex по четко выставленным критериям. Это вполне реализуемо на текущих инструментах в разрезе каждого тега и экшена. До сих пор в ClickHouse использовали только одну таблицу – таблицу логов. Для удобства аналитики укажем целевые показатели SLI и Apdex по времени отклика прямо в CH в виде отдельной таблицы – это позволит приджойниться и динамически отследить соответствие метрик поставленным целям. Мы выбрали разное время для разных тегов исходя из природы операции. Например, время на загрузку списка сущностей в таблицу фронтенда должно быть довольно мало, т. к. это наиболее востребованное действие в системе. При этом на тяжелую операцию импорта или интеграции можно заложить побольше, особенно если пользователь не участвует в операции и не ждет немедленной реакции.
Перечень колонок таблицы с целевыми показателями по времени SLI и Apdex:
log_target(action_tag, slo_percent, sli_time, apdex_time)
При вычислении по формулам целевое время выбирается из этой таблицы по конкретному тегу в логе операций.
Отследить показатели производительности сервиса по стандартам Google Site Reliability Engineering
Пример SLI


Видно как глобальный индекс, так и с разбивкой по тегам и экшенам. В таблице видно тяжелые экшены по показателю SLI с сортировкой по низкому %. В первую очередь стоит заняться оптимизацией красной зоны. Дополнительно планируем вычислять характеристику импакта в целом по системе: к примеру, экшен А вызвали всего 2 раза за день, он нарушил сроки и находится в красной зоне, а экшен Б вызвали 10 000 раз, но он обитает в желтой зоне – всё же оптимизировать Б до зеленого важнее, чем помочь единичным вызовам экшена А. Эта характеристика даст больше информации и понимания при выборе приоритетов для оптимизации.
Пример Apdex


Apdex покажет комфорт пользователей при работе в системе относительно отдельных тегов и экшенов. Целевое время зададим ниже отметки для SLI как рекомендованное время для удобства пользователя. Покажет, где пользователи ждут больше обычного в наглядных областях «хороший», «медленный» и «плохой». Разобравшись с проблемами по SLI, можно будет приняться за улучшение показателя Apdex.
Дашборд Business Apdex
Отследить показатели производительности сервиса метрикой Apdex с группировкой по бизнес-понятиям. Вместо специфичных для разработки понятий (тег, экшен, контроллер) отобразить информацию в виде удобных и понятных панелей-карточек. Каждая панель отображает одну из частей системы с человеческим названием и может в себе группировать один или несколько экшенов.
Для возможности группировки и мониторинга по бизнес-понятиям заведем таблицу в ClickHouse с колонками:
log_business_target(action_tag, controller, action, business_target, apdex_time, priority)
Конфигурация поможет гибко настроить маппинг tag-controller-action => понятие + целевое время для Apdex. Выполняется группировка по понятиям и сортировка по приоритету. Список понятий формируется эмпирически, что именно необходимо отслеживать по системе. В одну карточку могут попасть как один конкретный экшен, так и целая группа, покрывающая часть логики сервиса. В Grafana используется элемент Canvas с настройкой Repeat by variable (business_target).
Пример дашборда на тестовом наборе данных, где части системы были под нагрузкой (чтобы показать разные цвета):

● Отображает для каждого бизнес-понятия панель с состоянием Apdex за указанный период Grafana человеческими словами.
● Отображает динамику по сравнению с предыдущим периодом (+-0.xx).
● Отображает справочную информацию о целевом времени и о количестве запросов.
● Позволяет отфильтровать только нужные бизнес-понятия либо вывести все сразу.
Каждая карточка нацелена на свое время Apdex, и помимо самого индекса отображает дополнительную информацию. Дашборд использовать как средство мониторинга состояния системы для менеджеров, бизнес-аналитиков, заказчиков и других заинтересованных лиц.
Дашборд оценки и прогнозирования производительности экшенов
Однажды при наблюдении за статистикой по экшенам появилась мысль: «если у нас такой богатый объем данных, есть статистика производительности экшена и есть числовые показатели, на каких масштабах данных выполнялся экшен, почему бы не зайти дальше и не попытаться оценить, как себя поведет экшен в будущем при росте этих показателей?». Сказано – сделано: кастомный инструмент очередной раз показал, насколько быстро и гибко получается накидывать дашборды, ограничиваясь лишь фантазией и здравым смыслом.
Используя статистические методы, оценить зависимость используемых ресурсов от объема данных и спрогнозировать поведение экшена.
Оценка и прогнозы основаны на базовых методах математической статистики и подвержены ухудшению при аномалиях или недостаточном количестве данных. В основе стоит гипотеза о том, что потребление экшеном ресурса (время и RAM) зависит от объема данных, который представлен количеством SQL-запросов либо длительностью их выполнения. Если же экшен по природе не меняет количество или общую длительность SQL-запросов, корреляцию этим дашбордом обнаружить не получится.

Текущий алгоритм, по которому выполняется оценка сложности O(n^x): анализ сложности на основе регрессии логарифмов или log-log regression analysis / plot в английской терминологии (ссылка 1, ссылка 2, больше про сложность и линейную регрессию при оценке алгоритмов – ссылка 3). Стоит отметить, это ни в коем случае не аналитическая сложность алгоритма (экшена), а сложность в бытовом понимании – оценить, насколько увеличивается время работы от объема данных, и попытаться предсказать, сколько времени займет при увеличении этого объема.
Ключевые моменты (под спойлером)
Два предположительно взаимосвязанных массива данных в отношении a => b.
Используется натуральный логарифм. Логарифм превращает сложную степенную зависимость в линейную, чтобы легко найти показатель сложности slope через линейную регрессию.
Расчет ковариации и дисперсии. Ковариация – это статистическая мера того, насколько две переменные изменяются вместе. В нашем случае это мера того, как ln(total_sql_queries) и ln(duration_s) изменяются относительно друг друга. Дисперсия – это мера разброса значений переменной. Здесь мы используем дисперсию ln(total_sql_queries).
Расчет наклона (slope). Наклон линии регрессии в log-log пространстве рассчитывается как отношение ковариации к дисперсии. Этот наклон и есть показатель сложности алгоритма.
Интерпретация наклона. Если наклон близок к 1, это указывает на линейную сложность O(N). Наклон между 1 и 2 может указывать на сложность O(N*log(N)). Наклон около 2 указывает на квадратичную сложность O(N^2). Другие значения наклона интерпретируются как O(N^slope).
Экстраполяция. Используя полученный наклон (slope), мы можем предсказать время выполнения для больших объемов данных.
Дашборд в альфа-версии. Список улучшений на будущее: улучшить качество оценки (другие алгоритмы прогноза; нейронная сеть лучше отследит закономерности), убрать недостоверную оценку (отрицательную сложность), снизить влияние аномалий и шумов на оценку, дать возможность повысить приоритет точек на экстремумах.
Примеры – оценка и прогноз времени отклика экшена в зависимости от объема данных
Пример импорта позиций спецификации из XLSX. Значение prediction_multipler задает, насколько далеко мы желаем увеличить объемы данных для прогноза, обычно ×1 хватает для просмотра кривой сложности.

Попробуем оценить результат:
● Время импорта нелинейно зависит от количества SQL-запросов. Степенная сложность O(n^3) может говорить о том, что используется алгоритм вида for-for-for с тяжелой операцией во внутреннем цикле, стоит пересмотреть алгоритм импорта.
● Время импорта линейно зависит от длительности SQL-запросов, здесь мало пользы.
Пример экспорта списка позиций реестра платежей в XLSX

● Время экспорта не зависит от количества SQL-запросов (количество запросов не меняется, а время растет) – оценка сложности невозможна.
● Время экспорта почти линейно зависит от длительности SQL-запросов. Сейчас проблемы нет, однако можем спрогнозировать, при каком росте объема данных наступит лимит по времени выполнения для пользователя.
Оценка и прогноз времени CPU в зависимости от объема данных
В предыдущем прогнозе время отклика содержит в себе и собственно время обработки SQL-запросов (а по нему мы и пытаемся оценить зависимость). Как тогда найти сложные вычислительные алгоритмы и операции самого кода без учета запросов к БД? При оценке сложности берем общее время отклика и минус время из метрик типа Duration. Останется т. н. время CPU, не учтенное ни в каких других метриках Duration. Повышенный slope времени CPU, скорее всего, и будет означать медленный код: недостаточно эффективные алгоритмы; медленные библиотеки.
Рассмотрим на примере экспорта списка компаний заявки. Оценка времени отклика показала O(n^0.36) и O(n^0.41). Однако если оценивать время без БД и внешних запросов:

Время CPU более чем линейно зависит от количества SQL-запросов. Это говорит о неэффективном использовании алгоритмов (действительно, используется медленная библиотека для генерации файла экспорта – phpspreadsheet). После перехода на собственную разработку, которая распаковывает xlsx и работает с xml, как со строками, получилось снизить сложность.

Оценка и прогноз использования RAM в зависимости от объема данных
Пример экспорта списка позиций в XLSX

Максимальный объем задействованной оперативной памяти более чем линейно зависит от длительности SQL-запросов. Это говорит о неэффективном использовании RAM (действительно, используется библиотека phpspreadsheet, которая в базовой реализации требует многократного объема оперативной памяти).
Дашбор ТОПов экшенов по оценке сложности

Аналогично разделу ТОПов дашборд составлен для быстрого поиска экшенов с высокой оценкой сложности по перечисленным критериям. Это позволит обнаружить экшены, которые в перспективе могут стать тяжелыми при увеличении объемов данных. Как и намекает JSON Statham с картинки, не стоит верить статистике: необязательно высокая оценка сложности приведет к медленному экшену в перспективе. Каждый экшен лучше дополнительно анализировать и изучать текущую статистику: может ли существенно увеличиться объем данных; как часто экшен вызывается; насколько важно заниматься оптимизацией. Оценка также может быть рассчитана неточно: повлияют аномалии, шумы или недостаток данных. С опытом и анализом по другим дашбордам придет понимание, на какие экшены действительно стоит обратить внимание при закладывании оптимизации.
Автоматические оповещения
Аналитики с других дашбордов уже хватает на месяцы работ по оптимизации. В перспективе понадобятся автоматические рассылки при резком снижении производительности экшена. Возможности Grafana alerts позволяют гибко настроить правила рассылок, а составление запросов с помощью SQL развязывает руки в плане вариантов событий.


Тут и пригодилась таблица с настройками по целевым показателям SLI: например, можно создать алерт со списком экшенов, которые нарушили SLO за выбранный период.
SELECT l.controller_name || '.' || l.action_name AS action,
lt.slo_percent, ROUND((countIf(l.duration_s <= lt.sli_time) / COUNT(*)) * 100, 2) AS sli_current
FROM logs AS l
JOIN log_target AS lt ON l.action_group_tag = lt.action_tag
WHERE $__timeFilter(l.start_datetime) AND l.success
GROUP BY l.controller_name, l.action_name, lt.slo_percent
HAVING ROUND((countIf(l.duration_s <= lt.sli_time) / COUNT(*)) * 100, 2) < lt.slo_percent;

Можно строить любые запросы и выдавать в уведомлении всё, что выдаст этот запрос. Можно запросом получать некоторую метрику и в автоматическом режиме задать правила и значения, при которых оповещение будет рассылаться. Поддерживаются самые разнообразные экспортеры: почта, смс, телеграм и другие мессенджеры.
Примеры условий для алертов по экшенам:
● Тяжелый запрос БД – % БД по времени дольше остальных %.
● Запросы к БД – больше 5 000 запросов в одном вызове экшена.
● Много итераций в интеграции – интеграция в цикле.
● Неоптимизированный код – долгое время вычисления CPU.
● Большое потребление памяти – медиана по потреблению за полчаса от 100 МБ.
● Триггеры на изменения – метрика по экшену выросла более 50% относительно обычного в данный период.
● На вырост – прогнать нейронную сеть через базу кейсов: когда потребовалась критичная оптимизация, получим рекомендации SQL-запросов по неочевидным взаимосвязям метрик.
Дальнейшее развитие инструмента
По мере накопления опыта и подключения других проектов появляется всё больше идей для развития и усовершенствования инструмента:
● Больше метрик (FS, cache, Request/Response size, AMQP, TOP SQL, near/far external requests …)
● Автоматическая конфигурация метрик (заведение любых метрик типов IDENT/Single/Count/Duration) с автоинициализацией БД и дашбордов
● Автоматический сбор метрик в популярных фреймворках
● Оптимизация времени отправки (например, с демоном и posix shared memory/UDP/TCP)
● Адаптация под другие языки, поддержка микросервисов
● Больше дашбордов (P95/P99…), возможность сократить дашборды для уменьшения дублирования информации, улучшение алгоритмов прогноза (нейронка)
● Автоматические отчеты на почту
● Расширение списка триггеров для Grafana alerts (+нейронка)
● Мониторинг статистики для отслеживания тенденций использования сервиса (тренды развития проекта для аналитиков)
● Связать с мониторингом железа (USE+)
● Добавить показатели по фронтенду
● Подключить отказы и прерывания – тем самым добавим метрики недоступности по экшенам
● Генерация аналитических запросов с помощью LLM, которая находится в контексте проекта
● Опциональное включение профайлинга а-ля pinba для конкретных экшенов (must have для поиска конкретных мест в коде при оптимизации)
Путей для развития много, но кажется лишним двигаться в сторону Sentry и агрегировать вообще всё отовсюду и всегда. Вместо разработки универсального инструмента хочется сконцентрироваться на конкретной проблеме контроля производительности, помочь разработчикам быстро и плавно обнаружить и разобраться с проблемами на продакшене. Добавить уверенности в стабильной и качественной работе сервиса.
Плюсы и минусы используемого подхода
Преимущества используемых инструментов – большая гибкость как по метрикам, так и по методу отправки и отображаемому анализу, почти нет влияния на время отклика (чуть задерживаем fpm), малый объем для хранения, легко разрабатывать новые дашборды. SQL в качестве анализа – очень удобный язык. Текущие дашборды позволяют со всех сторон взглянуть на бэкенд и прозрачно видеть все аспекты производительности.
Теперь по недостаткам по сравнению с аналогичными решениями:
● Ручное подключение статических вызовов в pipeline экшена. Вручную придется заводить БД и выполнять инициализирующие запросы. Хоть и настройка дашбордов решается импортом JSON файла в графану. Проблема ручного подключения решается разработкой версий библиотеки по сбору метрик для конкретных фреймворков.
● Аналоги в виде Uptrace и Sentry подключить быстрее, и они возможно дают более глубокую аналитику (например, указывают место в коде, но для этого приходится эту информацию хранить и занимать трафик/место). Мы делаем чуть по-другому: концентрация на мониторинге небольшого набора метрик, агрегация показателей на разных аспектах производительности вкупе с аналитикой с точки зрения вызовов конкретных экшенов с помощью SQL.
● Пока среди языков поддерживается только PHP. При этом сама библиотека для сбора и отправки укладывается в 200 строк кода. При необходимости можно взять пример и написать реализацию для своего языка с подходящим методом сбора и отправки.
● Микросервисы – сложнее отследить, но можно добавить единую метрику trace_id. С группировкой по trace_id отслеживать целиком процесс затрагивающий несколько микросервисов.
● В текущей схеме сложно увидеть недоступность сервиса для показателей SLI по доступности (отказы регистрируются на уровне железа), тут можем поставить дополнительный сканер и отвалившиеся запросы логировать на входе балансера/nginx и записывать в ту же БД кликхауза.
● Prometheus больше оптимизирован под хранение и выборку одиночных метрик в виде временных рядов, данные могут занимать меньше места, но дольше вставка, и, если не заложились на определенную структуру, возможно, будет дольше выборка данных особенно на сложных агрегациях по нескольким метрикам сразу. Вместо SQL используется свой язык PromQL, может чуть неудобен для сложных аналитических запросов и нужно отдельно знакомиться с синтаксисом.
Итоги

● В больших проектах важно отслеживать производительность с прицелом на конкретные экшены – мониторинг и анализ.
● Можно не ограничиваться стандартами RED/USE, SLI/Apdex – добавлять метрики и аналитику, полезные именно вашему проекту.
● Не просто живые графики, но и статистика вроде таблиц ТОПов, желательно с автоматическими оповещениями + прогнозы.
● Своевременная оптимизация снизит тревогу пользователей и улучшит отзывы о работе системы.
Инструмент назвали VESNA.Insight – система детального мониторинга и анализа производительности сервисов с прицелом на конкретные действия пользователей. Позволяет с легкостью мониторить аспекты производительности более тысячи экшенов: время выполнения, нагрузка на БД, CPU, ожидание HTTP-запросов, RAM, ошибки. Реализована возможность группировки действий по типам, задание критичных точек по различным аспектам и мониторинг всей группы с отслеживанием выполнения целевых показателей. Довольно быстро добавляются новые метрики, расширяются панели мониторинга и аналитики.
Благодаря дашборду ТОПов создаем несколько задач оптимизации для разработчиков еженедельно, сокращает время на поиск узких мест. Каждой задаче помимо описания действия дашборды сообщают ценную информацию о критичности правки, какие аспекты требуют оптимизации, оценку сложности алгоритмов и предсказание производительности, получить рекомендации по оптимизации из предыдущих кейсов работы с подобными задачами.
ClickHouse отрабатывает очень шустро, индекса по времени хватает на то, что все перечисленные дашборды загружаются менее чем за 1 секунду. Агрегации моментальные: при отрисовке глобальной статистики – даже без фильтра по времени – десятки миллионов строк логов обрабатываются за пару секунд.
Благодаря Grafana с плагином кликхауз можем отправлять автоматические уведомления при превышении настроенных целевых показателей по всем отслеживаемым аспектам производительности сервиса. Оценка и прогнозирование производительности дают возможность получать уведомления вида «Импорт данных превысит лимит времени/RAM при росте объема на 200%» и заложить ресурсы на оптимизацию заранее.
Инструмент в первую очередь разрабатывался для внутренних нужд и уже преобразовал просто ручные точечные применения улучшений на управление целым комбайном производительности сервиса. Для коллег из интеграционных систем это гарантия бесперебойности работы с самыми важными контрактами. Для компании и менеджмента – снижение рисков, улучшение показателей работы команды и впечатлений по работе с сервисами. Для разработчиков, QA и аналитиков по проекту – уменьшение неудобств при попытках исследовать проблему с продакшена, меньше отвлечений на оперативные правки, больше уверенность при доработке тяжелых экшенов. Ну и наконец, для пользователей – отсутствие подтормаживающих экшенов и увеличение комфорта позволяет спокойно работать в системе.
Я же с удовольствием провел время, разбираясь в тонкостях мониторинга, аспектах производительности и, аки детектив, выслеживал проблемные места в крупном легаси-проекте с помощью построения нужных дашбордов. После долгих лет ручного разбора логов наконец появилась возможность видеть, как методы оживают в продакшене и весь бэкенд становится видно насквозь. Чувствуется осуществление моей мечты, как бэкенд-разработчика, болеющего за свой проект. Несколько раз в месяц во время работы над задачами возникают идеи всё новых метрик и дашбордов и что с их помощью можно обнаружить – такой инструмент позволяет быстро и просто за считанные дни накидывать в графану всё необходимое. Весьма интересно, куда эта дорожка приведет в дальнейшем, какие нюансы производительности получится вскрыть и какие рубежи преодолеть.
Послесловие
Огромное спасибо всем, кто дочитал до конца и выжил! В комментариях предлагаю обсудить не только предложенный инструмент, но и ваш опыт внедрения или использования подобного мониторинга. Особенно если работали с Sentry/Uptrace/.. или, может, даже разработали свой аналог, прошу рассказать о впечатлениях использования этих инструментов и какой аспект больше всего пригодился. Разумеется, задавайте вопросы и пишите рекомендации по улучшению. Спасибо за поддержку и ценный фидбэк! Ещё хочется послушать, как бы пригодился подобный инструмент в вашем языке (или, может, ваш язык уже имеет мощный встроенный инструмент мониторинга и анализа производительности экшенов и подключать что-то ещё не требуется?), какие метрики 100% нужно добавить, а какие будут бесполезны.
А так – хочется пожелать всем быстрых сервисов и безотказных экшенов, при всем этом – спокойной разработки и размеренной оптимизации. До встречи!