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

Давайте вообразим себе следующее: вы работаете в организации (она может быть и вашей), в которой есть бизнес-процесс, поддерживаемый одной или несколькими ИТ-системами. Я точно знаю, что есть система мониторинга. Дальше видение немного расплывается, и непонятно: промышленная это система или бесплатная Open Source. Однако у вас случаются ситуации, когда все датчики на ней зеленые, но сам бизнес-процесс дает необъяснимые сбои, демонстрируя снижение ключевых показателей. Как вспышка электрошокера представителя правопорядка на несанкционированном митинге, в вашей голове проскакивает мысль о том, что ситуация вышла из-под контроля и нужно немедленно действовать. Только вот непонятно как. Скажу вам, это достаточно распространенный кейс, от проявлений которого желательно поскорее отделаться. Системный подход к решению данной проблемы обеспечит составление модели сервиса.
Том Вуджек, который оказывает услуги по визуализации процессов, происходящих в компаниях (не только ИТ-), провел одно любопытное исследование. Он попросил разных людей нарисовать процесс приготовления тостов. Ниже приведены некоторые результаты этой работы.
Что же мы видим на многих картинках? Правильно! Объекты и связи, присутствующие в любой системе. Чем их больше, тем более системным будет подход. Правильная степень гранулярности позволит более точно отслеживать «здоровье» бизнес-системы. Для построения схемы вы можете попробовать использовать Visio, но гораздо интереснее взять маркер для отрисовки связей, клейкие листочки для объектов и изобразить вашу систему на маркерной доске. Чем большее количество желтых листочков, тем больше связей, тем выше шанс определить максимальное количество точек мониторинга для точного определения источника проблемы.
А теперь пришло время рассказать о наших наработках в области расширения стандартного функционала Zabbix и применения системного подхода, описанного выше. Их ровно две.
Первая – составление карты систем и создание тепловой карты сервисов. Учитывая наш серьезный опыт в части мониторинга банковских бизнес-процессов, пример мы приведем именно из этой области. Рассмотрим три самые типичные банковские системы. Если в вашем банке есть более типичные системы, прошу меня извинить – их мы рассматривать тут не будем.
Система дистанционного банковского обслуживания (ДБО):

Корпоративная шина передачи данных (ESB):

Система принятия решений (СПР):

В нашем приложении это будет выглядеть следующим образом (да, структура несколько нарушается, но при этом наглядность остается):

В случае необходимости можно перейти на уровень ниже. И, что не менее важно, при наведении на объект всплывает pop-up окно с описанием события и ссылкой на график в Zabbix:

Благодаря такому подходу и заданной степени детализации, на выходе мы получаем удобный дашборд, а заодно простой инструмент локализации проблемы. Несколько слов о функциональных возможностях нашей системы:
— визуализация зависимостей между корпоративными системами;
— настройка степени влияния компонентов друг на друга (вес связи);
— интеграция с Zabbix (объекты на тепловой карте связаны с триггерами);
— всплывающие окна с текстом события при наведении на объект;
— визуальный интерфейс настройки связей объектов;
— визуальный интерфейс настройки связи объектов с триггерами Zabbix.
В качестве примера приведу несколько уже разработанных интерфейсов.
Добавление объектов на тепловую карту:

Добавление интеграций с Zabbix:

Подключение триггеров Zabbix к объектам на тепловой карте:

Система работает на базе Google Charts и Bootstrap. Пока это альфа-версия, мы планируем ее развивать и дальше, добавляя полезные фишки из промышленных систем, с которыми успешно работаем уже много лет. Постараюсь держать вас в курсе и публиковать посты по мере накопления вороха новых возможностей.
Вторая наработка – это интеграция с Zabbix и тепловой картой функционала синтетических транзакций. Фактически это продолжение тепловой карты, но взгляд с другой стороны. Однозначно, контролируя систему только со стороны самого приложения и инфраструктуры, вы не будете обладать необходимой полнотой информации. Синтетические транзакции позволят посмотреть на это дело со стороны пользователя и локализовать проблему еще до первых обращений пользователей в Help Desk.
Синтетические транзакции построены на базе фреймворка phantom.js (но вам ничего не мешает перейти на casper.js, на чистый selenium или на что-то другое на ваш вкус). В нашей тестовой лаборатории выполнение тестового сценария настроено через cron и далее полученные данные передаются в Zabbix посредством zabbix_trapper. В качестве примера тестового сценария взят логин в личный кабинет МТС и получение остатка денег на счету и трафика в интернет-пакете. Ниже – листинг скрипта. В банковской среде наиболее вероятным применением этого инструмента может быть, например, ДБО. Никто же вам не мешает производить логин в систему и перекидывать 1 рубль со счета на счет.
Собираемые items выглядят следующим образом:

Каждому соответствует свой график.
Ни в коем случае не хочу сказать, что применение в мониторинге Open Source решений является таблеткой от всех неприятностей. Открою секрет Полишинеля: как и в физике, тут действует закон сохранения денег и трудозатрат. Чем больше денег вы вольете в готовый продукт, тем меньше трудозатрат на доработку, и наоборот. Всегда стоит руководствоваться здравым смыслом, имеющимся бюджетом и человеческим фактором: готова ли будет ваша команда броситься на амбразуру бизнес-мониторинга по первому зову?
Особо интересующимся технологиями мониторинга предлагаю ознакомиться с нашей предыдущей статьей по этой теме «Принципы мониторинга бизнес-приложений».
Автор статьи: Антон Касимов

Давайте вообразим себе следующее: вы работаете в организации (она может быть и вашей), в которой есть бизнес-процесс, поддерживаемый одной или несколькими ИТ-системами. Я точно знаю, что есть система мониторинга. Дальше видение немного расплывается, и непонятно: промышленная это система или бесплатная Open Source. Однако у вас случаются ситуации, когда все датчики на ней зеленые, но сам бизнес-процесс дает необъяснимые сбои, демонстрируя снижение ключевых показателей. Как вспышка электрошокера представителя правопорядка на несанкционированном митинге, в вашей голове проскакивает мысль о том, что ситуация вышла из-под контроля и нужно немедленно действовать. Только вот непонятно как. Скажу вам, это достаточно распространенный кейс, от проявлений которого желательно поскорее отделаться. Системный подход к решению данной проблемы обеспечит составление модели сервиса.
Том Вуджек, который оказывает услуги по визуализации процессов, происходящих в компаниях (не только ИТ-), провел одно любопытное исследование. Он попросил разных людей нарисовать процесс приготовления тостов. Ниже приведены некоторые результаты этой работы.
![]() |
![]() |
![]() |
Что же мы видим на многих картинках? Правильно! Объекты и связи, присутствующие в любой системе. Чем их больше, тем более системным будет подход. Правильная степень гранулярности позволит более точно отслеживать «здоровье» бизнес-системы. Для построения схемы вы можете попробовать использовать Visio, но гораздо интереснее взять маркер для отрисовки связей, клейкие листочки для объектов и изобразить вашу систему на маркерной доске. Чем большее количество желтых листочков, тем больше связей, тем выше шанс определить максимальное количество точек мониторинга для точного определения источника проблемы.
А теперь пришло время рассказать о наших наработках в области расширения стандартного функционала Zabbix и применения системного подхода, описанного выше. Их ровно две.
Первая – составление карты систем и создание тепловой карты сервисов. Учитывая наш серьезный опыт в части мониторинга банковских бизнес-процессов, пример мы приведем именно из этой области. Рассмотрим три самые типичные банковские системы. Если в вашем банке есть более типичные системы, прошу меня извинить – их мы рассматривать тут не будем.
Система дистанционного банковского обслуживания (ДБО):

Корпоративная шина передачи данных (ESB):

Система принятия решений (СПР):

В нашем приложении это будет выглядеть следующим образом (да, структура несколько нарушается, но при этом наглядность остается):

В случае необходимости можно перейти на уровень ниже. И, что не менее важно, при наведении на объект всплывает pop-up окно с описанием события и ссылкой на график в Zabbix:

Благодаря такому подходу и заданной степени детализации, на выходе мы получаем удобный дашборд, а заодно простой инструмент локализации проблемы. Несколько слов о функциональных возможностях нашей системы:
— визуализация зависимостей между корпоративными системами;
— настройка степени влияния компонентов друг на друга (вес связи);
— интеграция с Zabbix (объекты на тепловой карте связаны с триггерами);
— всплывающие окна с текстом события при наведении на объект;
— визуальный интерфейс настройки связей объектов;
— визуальный интерфейс настройки связи объектов с триггерами Zabbix.
В качестве примера приведу несколько уже разработанных интерфейсов.
Добавление объектов на тепловую карту:

Добавление интеграций с Zabbix:

Подключение триггеров Zabbix к объектам на тепловой карте:

Система работает на базе Google Charts и Bootstrap. Пока это альфа-версия, мы планируем ее развивать и дальше, добавляя полезные фишки из промышленных систем, с которыми успешно работаем уже много лет. Постараюсь держать вас в курсе и публиковать посты по мере накопления вороха новых возможностей.
Вторая наработка – это интеграция с Zabbix и тепловой картой функционала синтетических транзакций. Фактически это продолжение тепловой карты, но взгляд с другой стороны. Однозначно, контролируя систему только со стороны самого приложения и инфраструктуры, вы не будете обладать необходимой полнотой информации. Синтетические транзакции позволят посмотреть на это дело со стороны пользователя и локализовать проблему еще до первых обращений пользователей в Help Desk.
Синтетические транзакции построены на базе фреймворка phantom.js (но вам ничего не мешает перейти на casper.js, на чистый selenium или на что-то другое на ваш вкус). В нашей тестовой лаборатории выполнение тестового сценария настроено через cron и далее полученные данные передаются в Zabbix посредством zabbix_trapper. В качестве примера тестового сценария взят логин в личный кабинет МТС и получение остатка денег на счету и трафика в интернет-пакете. Ниже – листинг скрипта. В банковской среде наиболее вероятным применением этого инструмента может быть, например, ДБО. Никто же вам не мешает производить логин в систему и перекидывать 1 рубль со счета на счет.
Тестовый сценарий проверки баланса и остатка трафика (Javascript)
/** * Скрипт получения метрик по балансу абонента МТС * 1.0 * * Параметры запуска: * phantomjs --web-security=no getMtsBalance.js "<папка для вывода результатов>" "телефон в формате (XXX) XXX-XX-XX" "<пароль>" * Пример: * phantomjs --web-security=no getMtsBalance.js "/tmp/getMtsBalance" "(916) 123-45-67" "P@ssw0rd" * * (c) Jet/ДСУ 2016 */ // PhantomJs stuff var fs = require('fs'); var system = require('system'); var webpage = require('webpage'); var args = system.args; var TRAFFIC_REGEX = /Доступно.{1,100}?([0-9.]+).{0,50}?(ГБ|МБ).{0,50}?на (\d+) дн/i; var DATE_REGEX = /Дата обновления интернет-пакета ([0-9]{1,2})\.([0-9]{1,2})\.([0-9]{2}) ([0-2][0-9]):([0-5][0-9])/; var SCRIPT_TIMEOUT = 40000; var config = { lkUrl : 'https://lk.ssl.mts.ru/', lkLogin: null, lkPass : null, outDir : null, debugDir: null, formattedStartTime: null }; var timer = { lastActionStartTime: null, lastActionTimeMs : null, currentTimeMillis: function() { return Date.now(); }, startAction: function() { this.lastActionStartTime = this.currentTimeMillis(); this.lastActionTimeMs = null; }, stopAction: function() { lastActionTimeMs = this.currentTimeMillis() - this.lastActionStartTime; }, getLastActionTimeMs: function() { return lastActionTimeMs; }, getLastActionTimeSec: function() { return lastActionTimeMs === null ? null : lastActionTimeMs/1000; } } var metrics = { trafLeftMb: null, daysLeft : null, balance : null, pages: { login: { availability : null, responseTimeSec: null }, lk: { availability : null, responseTimeSec: null } } }; ////////// HELPER FUNCTIONS ////////// var func = { log: function(s) { console.log(this.formatDateTimeForLog(new Date()) + " " + s); }, roundToTwo: function(num) { return +(Math.round(num + "e+2") + "e-2"); }, zero: function(i) { return i < 10 ? '0' + i : i; }, formatDateTimeForLog: function(date) { var dd = date.getDate(); var mm = date.getMonth() + 1; var yy = date.getFullYear(); var hh = date.getHours(); var min = date.getMinutes(); var ss = date.getSeconds(); var ms = date.getMilliseconds(); ms = ('00' + ms).slice(-3); return yy + '-' + this.zero(mm) + '-' + this.zero(dd) + ' ' + this.zero(hh) + ':' + this.zero(min) + ':' + this.zero(ss) + '.' + ms; }, formatDateTimeForFileName: function(date) { return date.getFullYear() + this.zero(date.getMonth() + 1) + this.zero(date.getDate()) + '-' + this.zero(date.getHours()) + this.zero(date.getMinutes()) ; }, writeMetricToFileAndLog: function(filePrefix, metricName, metricValue) { if ( metricValue == null ) { metricValue = 0; } fs.write(config.outDir + filePrefix + config.formattedStartTime + '.log', this.roundToTwo(metricValue), 'w'); this.log(' ' + metricName + ' = ' + metricValue); } } // Разбираем аргументы запуска скрипта config.outDir = args[1] + '/'; config.lkLogin = args[2]; config.lkPass = args[3]; config.debugDir = config.outDir + 'debug/'; fs.makeDirectory(config.debugDir); func.log("Папка с результатами работы: " + config.outDir); // Таймаут - чтобы процесс навечно не завис setTimeout(function() { func.log("Сработал таймаут после " + SCRIPT_TIMEOUT + " мс"); if ( metrics.pages.login.availability == null ) { metrics.pages.login.availability = 0; metrics.pages.login.responseTimeSec = 0; } if ( metrics.pages.lk.availability == null ) { metrics.pages.lk.availability = 0; metrics.pages.lk.responseTimeSec = 0; } outMetricsAndExit(); }, SCRIPT_TIMEOUT); // Настраиваем наш "браузер" var page = webpage.create(); page.settings.userAgent = 'Mozilla/4.0'; // Отмечаем время начала процесса для логов config.formattedStartTime = func.formatDateTimeForFileName(new Date()); // Открываем страницу личного кабинета func.log("Загружаем " + config.lkUrl); timer.startAction(); page.open(config.lkUrl, function (status) { timer.stopAction(); metrics.pages.login.responseTimeSec = timer.getLastActionTimeSec(); if (status !== "success" ) { func.log("Страница " + config.lkUrl + " недоступна"); metrics.pages.login.availability = 0; outMetricsAndExit(); } else { func.log("Страница " + config.lkUrl + " успешно получена"); metrics.pages.login.availability = 1; page.render(config.debugDir + 'login.png'); // Страница будет подгружать iframe'ы, будем их обрабатывать var contentN = 0; page.onLoadFinished = function(status) { // Останавливаем таймер, чтобы замерять время получения страницы личного кабинета // Если мы будем получать несколько страниц (iframe'ов), // то время таймера просто будет расти, т.к. мы его стартанули только один раз, // перед отправкой логина-пароля. Личный кабинет открывается не сразу - // сначала он открывает страницу "Подождите", и только через некоторое время // показывает контент. Соотв. парсер корректно замеряет время от отправки логина до // получения страницы с реальными данными timer.stopAction(); contentN++; func.log('Загружен контент N' + contentN + ':' + status); page.render(config.debugDir + contentN + '.png'); fs.write(config.debugDir + contentN + '.html', page.content, 'w'); if ( status === 'success') { getMtsMetrics(page, contentN); } }; func.log("Заполняем поля формы, логин: " + config.lkLogin); timer.startAction(); page.evaluate(function(config) { var form = document.forms[0]; form.phone.value = config.lkLogin; form.password.value = config.lkPass; form.elements[2].click(); }, config); } }); function getMtsMetrics(page, contentN) { if ( page.content.match('подозрительную активность') ) { func.log("Ответ страницы МТС: замечена подозрительная активность. Слишком частое обращение к странице личного кабинета"); metrics.pages.lk.availability = 0; metrics.pages.lk.responseTimeSec = 0; outMetricsAndExit(); } // Ищем информацию о балансе внутри iFrame'ов findBalanceInPage(page, contentN); // Ищем остаток трафика и кол-во дней до оплаты findTrafficInfoInPage(page); // Если собрали все метрики, заканчиваем скрипт if ( checkGotMetricsAlready() ) { outMetricsAndExit(); } } /** * Ищет информацию о балансе на полученной странице */ function findBalanceInPage(page, contentN) { // Информация о балансе находится в iframe'ах if ( page.framesCount == 0 ) { return; } func.log("Анализируем полученные iframe'ы"); var balanceResult = page.evaluate(function() { var result = { iframes: [], balance: null }; $("iframe").each(function(i, iframe) { var iframeBody = $(iframe).contents().find('body'); if ( iframeBody.size() > 0 ) { result.iframes.push( iframeBody.html() ); // Вариант поиска баланса 1 - через DOM if ( result.balance === null ) { iframeBody.find(".b-header_balance").each(function() { var m = $(this).text().match(/([0-9.]+) руб/i); if ( m ) { result.balance = m[1]; } }); } // Вариант поиска баланса 2 - через regex if ( result.balance === null ) { var m = iframeBody.text().match(/баланс\s*:\s*-?([0-9.]+)\s*руб/i); if ( m ) { result.balance = m[1]; } } } }); return result; }); var iframesAnalyzed = balanceResult.iframes.length; func.log("Проанализировано iframe'ов:" + iframesAnalyzed); if ( iframesAnalyzed > 0 ) { // Сохраняем iFrame'ы на диск for (var i = 0; i < iframesAnalyzed; i++) { var iframeContent = balanceResult.iframes[i]; func.log(" Сохраняем iframe " + config.debugDir + contentN + '_iframe' + i + '.html'); fs.write(config.debugDir + contentN + '_iframe' + i + '.html', iframeContent, 'w'); } // Проверяем, была ли найдена информация о балансе if ( balanceResult.balance !== null ) { if ( metrics.pages.lk.availability === null ) { // Если мы получили баланс, то страница личного кабинета корректно загрузилась metrics.pages.lk.availability = 1; metrics.pages.lk.responseTimeSec = timer.getLastActionTimeSec(); } func.log("Найдена информация о балансе: " + balanceResult.balance); metrics.balance = balanceResult.balance; } } } /** * Ищет информацию об интернет-трафике на странице */ function findTrafficInfoInPage(page) { var traf = page.content.match(TRAFFIC_REGEX); if ( traf ) { func.log("Остаток трафика - строка найдена: " + traf); metrics.trafLeftMb = traf[1]; var trafUnits = traf[2]; if ( trafUnits.toLowerCase() == 'гб' ) { metrics.trafLeftMb *= 1024; } metrics.daysLeft = traf[3]; } else if (page.content.match("ревышена квота трафика") ) { func.log("Найдена строка: превышена квота трафика"); metrics.trafLeftMb = 0; if ( page.injectJs("jquery.min.js") ) { metrics.daysLeft = page.evaluate(function() { var p = $("p:contains('Интернет-пакет будет обновлен')"); var pText = p.find("b").text(); console.log( "Найден текст: " + pText); return pText.replace(/\D/g, ''); }); } } } /** * Проверяет, собраны ли уже все метрики */ function checkGotMetricsAlready() { if ( metrics.pages.login.availability == 0 || metrics.pages.lk.availability == 0 ) { // Мы определили недоступность одной из страниц (логин или страница ЛК) // В любом из этих случаев метрики собрать не удастся, инициируем окончание скрипта return true; } if ( metrics.balance != null && metrics.daysLeft != null && metrics.trafLeftMb != null ) { // Все метрики собраны, инициируем окончание скрипта return true; } return false; } /** * Выводит значения всех метрик на консоль и в файлы, * инициирует завершение скрипта */ function outMetricsAndExit() { func.log("Метрики:"); func.writeMetricToFileAndLog('traffic', 'metrics.trafLeftMb', metrics.trafLeftMb); func.writeMetricToFileAndLog('money', 'metrics.balance', metrics.balance); func.writeMetricToFileAndLog('daysLeft', 'metrics.daysLeft', metrics.daysLeft); func.writeMetricToFileAndLog('status-initialpageload', 'metrics.pages.login.availability', metrics.pages.login.availability); func.writeMetricToFileAndLog('time-initialpageload', 'metrics.pages.login.responseTimeSec', metrics.pages.login.responseTimeSec); func.writeMetricToFileAndLog('status-lkpageload', 'metrics.pages.lk.availability', metrics.pages.lk.availability); func.writeMetricToFileAndLog('time-lkpageload', 'metrics.pages.lk.responseTimeSec', metrics.pages.lk.responseTimeSec); phantom.exit(); }
Собираемые items выглядят следующим образом:

Каждому соответствует свой график.
Ни в коем случае не хочу сказать, что применение в мониторинге Open Source решений является таблеткой от всех неприятностей. Открою секрет Полишинеля: как и в физике, тут действует закон сохранения денег и трудозатрат. Чем больше денег вы вольете в готовый продукт, тем меньше трудозатрат на доработку, и наоборот. Всегда стоит руководствоваться здравым смыслом, имеющимся бюджетом и человеческим фактором: готова ли будет ваша команда броситься на амбразуру бизнес-мониторинга по первому зову?
Особо интересующимся технологиями мониторинга предлагаю ознакомиться с нашей предыдущей статьей по этой теме «Принципы мониторинга бизнес-приложений».
Автор статьи: Антон Касимов



