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

В сложных интеграционных системах мониторинг является неотъемлемым инструментом как для инженера, так и для бизнеса. Zabbix показал себя надёжной и гибкой системой, которая позволяет строить понятные дашборды. Но я, как тимлид команды поддержки, столкнулся с тем, что шину Red Hat JBoss Fuse так просто к Zabbix не подключить. Шина работает на JVM, а значит, нужен мониторинг по JMX.

1. Что происходит на шине

Первое, что я сделал — включил в параметры запуска шины (в setenv) работу JMX:

-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=8921
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false

Таким способом мы давно и надёжно наблюдаем стандартные JMX-метрики на всех поддерживаемых шинах. Отмечу, что в продакшн-средах я рекомендую включить аутентификацию (-Dcom.sun.management.jmxremote.authenticate=true) и TLS, используя внутренние CA.

Итак, ура! Через JConsole мы видим нашу шину и запущенные в ней модули (бандлы/бины). Теперь черёд за Zabbix'ом. Создаём кастомную метрику для мониторинга состояния бандла. Она отлично работает... пока модуль жив. Но если его остановить или перезапустить всю шину, Zabbix перестаёт получать данные и сыплет ошибками. Поэтому пришлось мониторить не состояние, а существование бандла, добавив предобработку.

Пример ключа:

jmx.get["beans","org.apache.camel:context=adapter.nav-camel.context-ru.rsins.esb.adapter.nav,type=components,name="properties""]

и предобработки:

return value.search(/properties/) > 0 ? 1 : 0

К сожалению, все модули уникальны, и применить единый шаблон не получилось. Около 15 кастомных метрик пришлось создать вручную. Зато теперь мы видим, если вдруг какой-то модуль «захотел передохнуть». По правде сказать, такого никогда не случалось. Зато когда инженер производит вынос или перезапуск шины, алерты приходят мгновенно — и это бальзам на душу заказчика.

2. Что ты делал 6 недель назад в этот самый час?

Маршрутизация к сервисам шины происходит через единую точку входа — Nginx. Удобно для пользователей. И тут заказчик ставит задачу: он хочет видеть количество обращений к каждому сервису, но не просто так, а в сравнении со средним значением. И это ещё не всё: нужно, чтобы каждый день и час недели сопоставлялся с таким же днём и часом за прошедшие 6 недель. Чего?!! Жизнь к такому не готовила. Или да?

Задача декомпозируется на две:

  • Получение количества обращений к каждому location в Nginx.

  • Получение недельной статистики.

Для первой задачи используем классическое решение — создаём кастомный лог Nginx:

log_format  conn  'realip_remote_port $uri';
access_log  /var/log/nginx/conn.log  conn;

Пишем bash-скрипт, который парсит лог и сопоставляет запросы к location с текущими открытыми соединениями:

#!/bin/bash
if [[ ($1) ]]; then
    a_conn=`ss -npt | grep nginx | awk '{print $5}' | sort`
    for u_conn in ${a_conn[@]}; do
        t_conn+=(`printf "%s\n" | grep $u_conn /var/log/nginx/conn.log | tail -n1 | awk '{print $2}' | sort`)
    done
fi
s=0
for item in ${t_conn[*]}
do
    if [ "$item" == "$1" ]; then
        let s++
    fi
done
echo $s
unset s
exit 0

Даём Zabbix'у право запускать этот скрипт по расписанию. Вуаля! Красивые графики, радующие глаз заказчика, готовы. 

Конечно, такое решение имеет ограничения по производительности и создаёт нагрузку на диск, но как быстрый win для малонагруженной системы — годится.

Для второй задачи вспоминаем школьную математику и создаём кастомные вычисляемые метрики. Штатных таких нет.

Вот пример формулы для усреднённого исторического значения:

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

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

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

Заключение

  1. Работающее решение лучше идеального. Не скажу, что это изящная реализация, но иногда приходится отходить от красивых архитектур в пользу работающих. Главное — изолировать такую сложность, чтобы она не усложняла поддержку системы в будущем. Кстати, дашборд выглядит вполне приятно:

  2. «Пилим слона». Задачи от заказчика иногда звучат пугающе. Важно с холодной головой разбивать даже самые немыслимые формулировки на простые технические шаги.

  3. Взгляд в будущее. Идеальным следующим шагом было бы вынесение логики парсинга и агрегации в отдельный лёгкий микросервис, который отдавал бы метрики в Zabbix по API.

P.S. Ещё один интересный кейс интеграции

Пока писал статью, вспомнил ещё одну классическую, но не самую примитивную связку, которая у нас работает: Zabbix → PowerShell → MSSQL → шина.

Есть у нас один сервис отправки чеков в кассу. И отправляются они через очередь в БД. Размер данной очереди — это важная бизнес-метрика, которую необходимо всегда иметь на виду. База у заказчика MSSQL, а свежий Zabbix подружился пока только с PosgreSQL. Поэтому для заглядывания в базы других производителей рекомендуются скрипты. Что мы и реализовали на PowerShell, т. к. именно у этого заказчика шина вертится на MS Windows server.

Данный скрипт работает предельно просто:

1. Выполняет SQL-запросы к базе
2. Парсит результат и отдаёт в Zabbix только цифру — количество чеков.

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

Пример PowerShell-скрипта, который Zabbix запускает по расписанию:

# Мониторинг очереди платежей в ESB через MSSQL
# Zabbix вызывает этот скрипт для проверки количества "зависших" платежей

# Параметры подключения
$dataSource = "sql-server-host,1433"
$database = "ESB_Monitoring_DB"
$sql = @"
SELECT COUNT(*) as pending_count 
FROM esb_event_log WITH (NOLOCK) 
WHERE object_type = 'PAYMENT' 
  AND stream IS NOT NULL 
  AND status IN ('PROCESSED', 'PENDING')
"@

# Подключение через OLEDB
$connectionString = "Provider=sqloledb; Data Source=$dataSource; Initial Catalog=$database; User Id=monitoring_user; Password=********;"
$connection = New-Object System.Data.OleDb.OleDbConnection $connectionString
$command = New-Object System.Data.OleDb.OleDbCommand $sql, $connection

try {
    $connection.Open()
    $result = $command.ExecuteScalar()  # Получаем одно значение COUNT
    Write-Output $result
} finally {
    if ($connection.State -eq 'Open') { $connection.Close() }
}

В Zabbix это реализовано, как внешняя проверка - (system.run[]) вызывает этот скрипт.

Плюшки для бизнеса: теперь видно не только «жива ли шина», но и «здорова ли она» с точки зрения бизнес-процессов. Если в очереди накапливается 100+ чеков без движения в течении 15 минут — это повод поинженерить до того, как пользователи начнут жаловаться.

А вам приходилось решать подобные задачи? Какие подходы вы использовали для мониторинга нестандартных систем или сложной аналитики нагрузки? Буду рад обсудить интересные кейсы в комментариях.



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