В этом посте мы хотим рассказать, как перешли от проприетарного решения в области мониторинга электросетевого оборудования на Open Source систему и с какими проблемами нам пришлось при этом столкнуться. Реальный кейс по мониторингу промышленного оборудования с помощью Zabbix.
Энергетики подкинули задачу
Ранее мы уже имели опыт в настройке мониторинга различного оборудования для энергообеспечения ЦОД, поэтому очередная задача от энергетиков казалась легко выполнимой. Однако что-то пошло не так…
Нам позвонил коллега-энергетик и спросил: «А вы сможете настроить мониторинг состояния электрощитов?»
«Ну, разумеется, мы можем всё, но нужно конкретное ТЗ», — ответили мы.
В ходе обсуждений выяснилось, что коллеги уже пользуются системой мониторинга, однако ее поддержка платная, гибкость в настройке отсутствует, и она полностью проприетарная. Система мониторинга представляла собой приложение, написанное на C#, которое отображает поверх изображения плана серверной состояние щитов, может отправлять уведомления об отклонениях с помощью SMS/Email и даже хранить историю за пару дней. Всё это выглядело в лучших традициях подобных АСУ ТП и напоминало панель управления космолетом.
Итак, что же изначально у нас было:
n-ое количество преобразователей RS-232/422/485 в Ethernet «MOXA NPort 5150»;
подключенные к ним по RS-485 измерительные приборы Satec;
несколько измерителей Satec, подключенных напрямую по Ethernet;
уже настроенная система SCADA;
огромное желание заполучить эти данные в Zabbix.
Приступаем к изучению
Энергетики предоставили планы размещения устройств по серверным, адреса преобразователей MOXA, доступ к существующему мониторингу, и мы приступили к изучению работы системы. Разумеется, наши «исследования» не должны были влиять на работу текущей инсталляции, поэтому мы были весьма ограничены в действиях.
Изучив планы, выделили две схемы подключения оборудования: с использованием преобразователей MOXA и прямое подключение к приборам учета Satec (рис. 1). В обоих случаях транспортом выступал TCP, необходимости напрямую работать с последовательными портами не было.
Поскольку информации о работе системы не было, а все данные были зашиты в один бинарный файл, для начала мы решили проанализировать трафик и посмотреть, как ПО «общается» с оборудованием. Изучая трафик, мы обнаружили, что для общения с приборами учета используется протокол Modbus — очень распространенный протокол, который применяется в сетях промышленной автоматизации. Привычных протоколов SNMP и IPMI у данных приборов нет.
Система мониторинга Zabbix, начиная с версии 5.2 умеет получать данные через агента по этому протоколу. Для этого используется ключ modbus.get, который может применяться для подключения протоколов TCP/RTU/ASCII. Подробнее с параметрами и синтаксисом можно ознакомиться тут.
Ну вот вроде бы и все, ставь агента, собирай данные — дело на пять минут. Мы испробовали этот метод на счетчиках, подключенных напрямую через Ethernet, и получили очень странные показатели частоты и напряжения, но с этим мы разберемся чуть позже. А вот в случае использования MOXA в качестве преобразователя, на выходе в Zabbix мы благополучно получали ошибку:
Сначала мы проверили настройки преобразователя и убедились, что используем для коммуникации правильные порты, они соотносятся с необходимыми нам выходами на электрооборудовании, машина с нашим агентом находится в white-листе MOXA и доступ со стороны агента есть. Всё было корректно, и мы отбросили мысль о неправильно настроенном преобразователе.
Далее, мы подробнее ознакомились с документацией MOXA и проанализировали трафик, сгенерированный уже настроенной SCADA-системой в сторону MOXA и обратно, с трафиком, генерируемым Zabbix. И обнаружили, что есть разница в структуре TCP пакета.
Для получения метрик Zabbix использует протокол обмена Modbus TCP, у которого структура пакетов отличается от Modbus RTU наличием дополнительного заголовка и отсутствием CRC-суммы (рис. 2).
Получается, что Zabbix отправлял передатчику (MOXA) пакет Modbus TCP, тот его обрабатывал и передавал нашему щиту пакет без CRC и с дополнительным заголовком. Конкретно у этих измерительных приборов на борту был только порт RS-485, и они вполне справедливо «посылали нас» при попытке «накормить их каким-то мусором». Причем посылали с использованием протокола Modbus RTU, который, в свою очередь, не понимал уже Zabbix. Стало понятно, что стандартными средствами мониторинга нам не обойтись.
Разбираемся дальше
Раз Zabbix не может напрямую общаться с нашими щитами, необходимо разработать прослойку для получения и возможной предобработки данных. Ознакомившись с популярными библиотеками для работы с протоколом Modbus, мы решили написать прототип на Python с использованием модуля PyModbus.
Вот как это все выглядело на практике.
Для начала нужно сформировать пакет формата Modbus RTU:
SlaveID — 1 байт | Код функции — 1 байт | Адрес первого регистра — 2 байта | Количество регистров — 2 байта | CRC — 2 байта |
где:
SlaveID — идентификатор устройства Satec, находящегося за MOXA;
код функции — идентификатор функции, выполняющей действия над регистрами (табл. 1);
адрес первого регистра — адрес первого регистра с данными устройства;
количество регистров — то количество регистров, идущих подряд от указанного ранее «первого» регистра, которое нам необходимо прочитать;
CRC — контрольная сумма сформированного пакета.
Таблица 1. Коды и описание функций, используемых в протоколе Modbus (Источник: http://www.simplymodbus.ca/TCP.htm)
Код функции | Что делает функция | Тип значения | Тип доступа | |
01 (0x01) | Чтение DO | Read Coil Status | Дискретное | Чтение |
02 (0x02) | Чтение DI | Read Input Status | Дискретное | Чтение |
03 (0x03) | Чтение AO | Read Holding Registers | 16-битное | Чтение |
04 (0x04) | Чтение AI | Read Input Registers | 16-битное | Чтение |
05 (0x05) | Запись одного DO | Force Single Coil | Дискретное | Запись |
06 (0x06) | Запись одного AO | Preset Single Register | 16-битное | Запись |
15 (0x0F) | Запись нескольких DO | Force Multiple Coil | Дискретное | Запись |
16 (0x10) | Запись нескольких AO | Preset Multiple Registers | 16-битное | Запись |
А теперь — ловкость рук и никакого мошенничества. Нас интересует функция чтения «Read Input Registers» с кодом 04. Для того, чтобы понять, какой адрес первого регистра нам необходим, обратимся к технической документации конкретного измерительного прибора Satec и увидим, что нужные нам данные по напряжению и частоте начинаются с 256 и заканчиваются 272 регистром (нужно прочитать 16 регистров подряд). Считаем для всего этого «великолепия» CRC и получаем итоговый пакет:
0c | 04 | 01 00 | 00 16 | 71 25 |
Теперь можно отправлять эти данные с помощью TCP нашей MOXA и ждать ответа, который, в свою очередь, имеет следующий формат:
Адрес устройства | Код функции | Количество байтов | Значения регистров | CRC |
Если все сделано верно, то мы получим в ответ массив с «сырыми» данными, которые еще нужно будет обработать.
Что делать с ответом
Вот мы и достали заветные циферки из наших щитов, но почему частота равна 2501 Гц, а входное напряжение 5000 В? Все дело в том, что Satec может отдавать данные в трех различных форматах, для которых требуется дополнительная обработка:
16-bit integer,
32-bit integer/ mod 10000,
32-bit integer (два 16-bit регистра, которые хранят по половине числа).
При этом в 16-битном формате числа могут отдаваться или в «сыром» виде (так как они уже были получены от отслеживаемого прибора), или будут требовать еще дополнительной обработки. Это правило справедливо для чтения и записи данных в регистры.
Используемый формат описан в техдокументации для каждого из регистров, и в нашем случае необходимо было дополнительно преобразовывать значения.
«LIN3» — это функция, выполняющая преобразование необработанных данных в диапазоне от 0 до 9999 в определяемый пользователем диапазон Low/HIgh. Сама функция выглядит следующим образом:
где:
X — «сырое» значение от 0 до 9999;
HIGH и LOW — верхняя и нижняя границы.
Граничные значения мы выбираем из той же таблицы с описанием регистров, основываясь на характеристиках оборудования. Тут нам существенно помогли коллеги-энергетики и реверс-инжиниринг существующей системы. Выдыхаем — мы разобрались аж с целой формулой, дальше будет проще.
Итоговая схема реализации:
Передаем IP-адрес MOXA и набор unitID конечных устройств Satec в качестве аргументов скрипту.
Скрипт опрашивает счетчики и возвращает данные в виде массива значений.
Zabbix обрабатывает полученный массив и с помощью автоматического обнаружения создает набор метрик для хранения данных.
Каждая метрика содержит правило предобработки, которые вычисляет итоговое значение по формуле.
Что же касается самого скрипта, это — внешняя проверка Zabbix-сервера, поэтому располагаем скрипт в директории /usr/lib/zabbix/externalscripts. В зависимости от входных параметров выполняется либо автообнаружение устройств, либо получение данных с конкретного устройства. В качестве конфигуратора используется xls-файл, в котором описываем всю необходимую информацию: ip-moxa, unitID, название щита расположения прибора Satec, а также все переменные для расчета по формулам напряжения, силы тока, мощности и т. д. (рис. 4). Нет, современные технологии не обошли нас стороной, и YAML-файлами мы тоже «балуемся», просто в данном случае было проще сделать так.
В Zabbix, используя ключ moxa_app[discovery,{HOST.CONN}], передаем параметры для скрипта:
moxa_app — название самого скрипта;
discovery — параметр, запускающий автообнаружение;
{HOST.CONN} — адрес MOXA.
Скрипт на входе, видя параметр discovery, парсит содержимое xls-файла и получает по IP-адресу хоста данные по щиту, которые возвращает в виде json-объекта. Далее Zabbix из этого объекта создает набор метрик c правилами предобработки для расчета значений (рис. 5).
Для получения данных мы используем один элемент на каждый unitID, в котором собирается информация из всех регистров за один раз. Затем эта информация применяется в «зависимых» элементах данных. Для этого используется тот же скрипт, но в качестве аргументов передает ключ get, unitID из конфига (мы его получили на этапе выполнения правила дискаверинга) и {HOST.CONN}. На рисунке 6 показано, как выглядит подключение и запрос данных с помощью PyModbus.
Как можно увидеть из листинга скрипта, используется порт 502, в нашем случае — ModbusRtuFramer. Запрашиваем данные от MOXA 192.168.0.5 начиная с 256 регистра, 50 регистров, с UNIT id: 1
А вот так выглядит правило JS предобработки, о котором мы говорили ранее, для каждого элемента данных:
В итоге мы автоматически получаем по каждому прибору учета, подключенному к опрашиваемому преобразователю, набор элементов данных и триггеров.
По собранным данным можно строить графики и создавать информативные дашборды в системах ИТ-мониторинга Zabbix и Grafana. Можно гибко настроить оповещения и эскалации с учетом времени возникновения проблемы или ее критичности, чего не было ранее. Также мы можем задействовать такой механизм Zabbix, как «карты сети» и, используя план здания, отображать агрегированное состояние щитов и выводить список проблем. На рисунке 7 дан пример такой небольшой карты.
Заключение
Zabbix «из коробки» умеет собирать данные с разных источников, поддерживает push/pull модели опроса и большое число протоколов связи и передачи данных, среди которых SNMP, WMI, JMX, HTTP/HTTPS, XML Soap, SSH, Telnet, ICMP, агенты, трапы и др. Открытый исходный код и возможность писать модули для расширения функционала позволяют модифицировать систему мониторинга и добавлять необходимый функционал. Однако если у вас в штате нет людей, хорошо знающих C, или вам необходимо проверить еще не подтвержденную концепцию, то в этом случае мы рекомендуем использовать внешние скрипты либо агенты/трапы для реализации задуманного. В нашем варианте мы остановились на реализации модуля на Python, поскольку его проще поддерживать, и этот подход позволил нам безболезненно обновлять Zabbix-сервер.
Jet Service Team