Zabbix — замечательный продукт для администраторов крупных программно-аппаратных комплексов. Он настолько хорош, что может использоваться не только крупным бизнесом, но и средне-малым бизнесом, и даже в pet
-проекте. В общем, у меня есть небольшой опыт работы с Zabbix'ом и я смело могу рекомендовать его к использованию.
Правда я не могу сказать, что понимаю "философию Zabbix'а". Несмотря на обширную подробную документацию на русском языке, мне было сложно погружаться в мир Zabbix'а — создавалось ощущение, что мы с разработчиками одни и те же вещи называем разными именами. Возможно потому, что Zabbix создавался админами для админов, а я всё-таки больше разработчик и пользователь.
Тем не менее, для запуска Zabbix'а и для мониторинга основных параметров компьютерных систем (процессор, память и т.п.) навыков обычного linux-пользователя хватает. Есть большое количество плагинов от сторонних разработчиков, расширяющих возможности Zabbix'а. Для моих нужд мне потребовалось настроить мониторинг Redis-сервера. Я немного покопался в коде имеющихся плагинов и на их примере выяснил, что архитектура Zabbix'а позволяет достаточно просто подключать к мониторингу любые параметры информационных систем, которые могут быть выражены в числовом виде.
Под катом — пример Zabbix-плагина с моим пояснением по терминологии Zabbix'а. Кому-то этот пример покажется наивным, ну а кому-то поможет проще освоиться с понятиями. В любом случае, Zabbix достаточно велик для того, чтобы ощупать его с разных сторон.
Кратко о некоторых понятиях, которые используются в Zabbix'е: agents, items, triggers, actions, notifications, templates.
С точки зрения пользователя Zabbix делится на две большие части: сервер и агенты. Сервер располагается на одной машине, которая собирает и хранит статистические данные, а агенты — на тех машинах, данные с которых собираются:
Любая величина, которая может выражена в числовом или строковом виде, называется в терминологии Zabbix'а — элементом данных (item). Каждый элемент связывается с уникальным ключом (именем). Вот примеры элементов данных:
Значения этих элементов данных (параметров мониторинга) привязываются ко времени, история значений параметров сохраняется в базе сервера.
При наступлении некоторого события в Zabbix'е срабатывает триггер. Например,
По сути, триггеры — это формулы, в которых переменными выступают параметры мониторинга (текущие и сохранённые), и которые на выходе дают true
/false
.
В случае наступления события (срабатывания тригера) сервер может выполнить действие. Например, отправить оповещение по email'у на заданный адрес ("Problem: host is unreachable for 5 minutes"). Также действие может быть выполнено в случае возвращения триггера в исходное состояние ("Resolved: host is unreachable for 5 minutes"). Все события (переключения триггера) логируются на стороне сервера.
Zabbix даёт возможность как настроить правила мониторинга для отдельного хоста, так и создать шаблон правил (template), который можно применять к различным хостам:
На примере видно, что шаблон "Template App SSH Service" описывает одно приложение (Applications), один параметр мониторинга (Items), один триггер (Triggers). Также доступны описания для графиков, экранов, правил обнаружения и web-сценариев.
Сам Zabbix предлагает свой собственный плагин для мониторинга состояния Redis'а, но на моей версии сервера (4.2.8) мне не удалось его задействовать (плагин для версии 4.4 и выше). Также предлагаются решения от третьих лиц (около десятка вариантов под различные версии Zabbix'а, на картинке только первых три):
Каждый из них обладал своими плюсами-минусами, пришлось заглянуть внутрь, чтобы выбрать. Лучшим, на мой взгляд, оказался плагин Shakeeljaveed/zabbix-redis-userparamaters, состоявший из двух файлов:
Немножко пришлось поработать "ручками", но зато на его примере стало чуть понятнее, как данные от агента попадают на сервер. По предложению автора Javeed Shakeel состояние Redis'а каждые 2 минуты сбрасывалось кроном в файл /tmp/redismetric
:
*/2 * * * * /usr/bin/redis-cli info > /tmp/redismetric
А затем каждый параметр мониторинга извлекался агентом из файла /tmp/redismetric
при помощи средств самой операционной системы. Инструкции для этого размещались в конфигурации Zabbix-агента /etc/zabbix/zabbix_agent.conf.d/userparameter_redis.conf
. Например, вот так выглядят инструкция для извлечения параметра used_memory
(использование памяти Redis-сервером):
UserParameter=used_memory,grep -w 'used_memory' /tmp/redismetric | cut -d: -f2
То есть, в файле /tmp/redismetric
с выводом redis-cli INFO
по ключу used_memory
ищется строка (grep -w ...
)
used_memory:7153216
которая затем разбивается на столбцы по разделителю ":" (cut -d: -f2
). На выходе агент получает число 7153216
и присваивает его параметру used_memory
.
Остаётся через web-интерфейс настроить сервер, чтобы он периодически отправлял запросы агенту на получение данных по параметру used_memory
, после чего данные начинают литься на сервер, сохраняться в базе, по ним можно строить графики и создавать триггера, реагирующие на изменения этого параметра.
Задачей мониторинга состояния любой системы явлется не только сбор статистики, но и предупреждение о возникновении ситуаций, требующих вмешательства человека. Так как с Redis'ом я работаю на уровне очень начинающего пользователя, то пришлось поискать информацию, на какие параметры "здоровья" обращать внимание и что они значат. Наиболее достойной показалась статья "6 Crucial Redis Monitoring Metrics You Need To Watch". Проанализировав её, я пришёл к выводу, что "для полного счастья" мне нужно собирать данные для обнаружения следующих событий:
Также я хотел собирать статистику по дополнительным параметрам (версия Redis'а, uptime и т.п.). В общем, имея общее представление о том, каким образом данные собираются агентом и передаются на сервер, "хотелки" можно сильно не ограничивать. В итоге получился список параметров для мониторинга из 12 позиций.
Плагин, который я анализировал, предполагал выполнение отдельной команды для получения отдельного параметра (элемента данных, item'а):
grep -w 'used_memory' /tmp/redismetric | cut -d: -f2
Т.е., для получения данных по 12 параметрам агент должен будет 12 раз выполнить различные наборы команд. А если мне нужно мониторить параметры, которые сложно извлечь цепочкой команд и нужно будет писать отдельный shell-скрипт или полноценную программу? Для таких "хотелок" Zabbix предлагает вариант с зависимыми элементами данных. Суть его в том, что на стороне агента скриптом формируется набор данных (например, в формате JSON), который передаётся на сервер в виде строкового параметра. Затем на стороне сервера происходит разбор полученных данных и вычленение из них отдельных элементарных параметров.
Я описал основной элемент данных redis.info
строкового типа с периодом обновления в 1 мин., без сохранения истории изменений:
Предположительно, на стороне агента должен генерироваться такой JSON:
{
"version": "4.0.9",
"uptime": 1897336,
"used_memory": 1054943416,
"used_memory_rss": 1138159616,
"keyspace_hits": 75810274,
"keyspace_misses": 13545949,
"connected_clients": 15,
"rdb_last_save_time": 1580126954,
"total_connections_received": 1258614,
"rejected_connections": 0,
"expired_keys": 60270,
"evicted_keys": 0
}
после чего этот текст должен попадать на сервер в виде элемента данных redis.info
, но не сохраняться, а служить базой для других элементов данных (параметров мониторинга).
Тестовый параметр redis.info.version
зависит от redis.info
и сохраняет свои значения в базе в течение 90 дней. Периодичность мониторинга параметра зависит от базового элемента (redis.info
):
Значение параметра redis.info.version
извлекается из значения redis.info
при помощи инструкций JSONPath:
По аналогичной схеме описываются остальные зависимые элементы данных (параметры мониторинга), которые передаются в виде JSON'а. Вот пример описания числового параметра redis.info.used_memory
:
Всё достаточно прозрачно, за исключением Units
и Trend storage period
. Со вторым пунктом я не разбирался, оставил по-умолчанию, а единицы измерения объяснены в документации. В данном случае значение redis.info.used_memory
измеряется в байтах и в web-интерфейсе сворачивается до кило/мега/гига/...-байт.
Формула для извлечения значения из JSON'а: JSONPath = $.used_memory
Для вычисления фрагментации памяти используется отношение used_memory_rss
/ used_memory
и на его базе определяется триггер, срабатывающий при превышении отношением значения 1.5. В Zabbix'е есть вычисляемый тип элементов данных:
Значение для параметра redis.info.used_memory_ratio
вычисляется каждую минуту на основании последних значений двух других параметров (redis.info.used_memory_rss
и redis.info.used_memory
), сохраняется в базе в течение 90 дней и т.д.
Вот пример триггера, срабатывающего при излишней фрагментации памяти:
Ничего необычного, за исключением формата выражений, используемого в формуле изменения состояния триггера. В Zabbix'е есть конструктор форм, можно воспользоваться им или обратиться к документации/примерам (список триггеров доступен через web-интерфейс по адресу "Configuration / Templates / ${TemplateName} / Triggers").
Триггер может базироваться на любых элементах данных (item'ах) вне зависимости от их типа (основной, зависимый, вычисляемый).
Для получения значений параметров мониторинга и формирования JSON'а я использую вот такой shell-скрипт:
#!/bin/bash
## ===========================================================================
# Script to generate Redis monitoring data in JSON format
# for 'zabbix-agent'.
## ===========================================================================
# collect working variables/data
BIN_REDIS=$(whereis -b redis-cli | cut -d" " -f2) # /usr/bin/redis-cli
DATA_INFO=$(${BIN_REDIS} INFO | tr -d '\r') # get info and remove trailing '\r' from output
##
# Extract stats and save it into env. vars.
##
# find lines with 'grep', cut second field after ":" and put it into env. variable:
ITEM_VERSION=$(grep "^redis_version:" <<<"${DATA_INFO}" | cut -d: -f2)
ITEM_UPTIME=$(grep "^uptime_in_seconds:" <<<"${DATA_INFO}" | cut -d: -f2)
ITEM_USED_MEMORY=$(grep "^used_memory:" <<<"${DATA_INFO}" | cut -d: -f2)
ITEM_USED_MEMORY_RSS=$(grep "^used_memory_rss:" <<<"${DATA_INFO}" | cut -d: -f2)
ITEM_KEYSPACE_HITS=$(grep "^keyspace_hits:" <<<"${DATA_INFO}" | cut -d: -f2)
ITEM_KEYSPACE_MISSES=$(grep "^keyspace_misses:" <<<"${DATA_INFO}" | cut -d: -f2)
ITEM_CONNECTED_CLIENTS=$(grep "^connected_clients:" <<<"${DATA_INFO}" | cut -d: -f2)
ITEM_RDB_LAST_SAVE_TIME=$(grep "^rdb_last_save_time:" <<<"${DATA_INFO}" | cut -d: -f2)
ITEM_TOTAL_CONNECTIONS_RECEIVED=$(grep "^total_connections_received:" <<<"${DATA_INFO}" | cut -d: -f2)
ITEM_REJECTED_CONNECTIONS=$(grep "^rejected_connections:" <<<"${DATA_INFO}" | cut -d: -f2)
ITEM_EXPIRED_KEYS=$(grep "^expired_keys:" <<<"${DATA_INFO}" | cut -d: -f2)
ITEM_EVICTED_KEYS=$(grep "^evicted_keys:" <<<"${DATA_INFO}" | cut -d: -f2)
# compose output JSON for Zabbix server:
echo -n "{"
echo -n "\"version\": \"${ITEM_VERSION}\","
echo -n "\"uptime\": ${ITEM_UPTIME},"
echo -n "\"used_memory\": ${ITEM_USED_MEMORY},"
echo -n "\"used_memory_rss\": ${ITEM_USED_MEMORY_RSS},"
echo -n "\"keyspace_hits\": ${ITEM_KEYSPACE_HITS},"
echo -n "\"keyspace_misses\": ${ITEM_KEYSPACE_MISSES},"
echo -n "\"connected_clients\": ${ITEM_CONNECTED_CLIENTS},"
echo -n "\"rdb_last_save_time\": ${ITEM_RDB_LAST_SAVE_TIME},"
echo -n "\"total_connections_received\": ${ITEM_TOTAL_CONNECTIONS_RECEIVED},"
echo -n "\"rejected_connections\": ${ITEM_REJECTED_CONNECTIONS},"
echo -n "\"expired_keys\": ${ITEM_EXPIRED_KEYS},"
echo -n "\"evicted_keys\": ${ITEM_EVICTED_KEYS}"
echo -n "}"
Этот скрипт я поместил в файл /var/lib/zabbix/user_parameter/redis/get_info.sh
на сервере с Redis'ом, на котором уже установлен агент Zabbix'а. Пользователь, под которым запускается Zabbix-агент (обычно zabbix
) должен иметь права на выполнение файла get_info.sh
.
userparameter_XXX.conf
На стороне агента дополнительные параметры мониторинга прописываются в файлах userparameter_*.conf
в каталоге /etc/zabbix/zabbix_agentd.d
. Поэтому для того, чтобы агент узнал о том, каким образом ему нужно собирать данные по параметру redis.info
, я создал файл /etc/zabbix/zabbix_agentd.d/userparameter_redis.conf
с таким содержимым:
UserParameter=redis.info,/var/lib/zabbix/user_parameter/redis/get_info.sh
Т.е., для получения данных по параметру redis.info
агент должен запустить скрипт /var/lib/zabbix/user_parameter/redis/get_info.sh
и передать на сервер результат выполнения.
После рестарта Zabbix-агента (sudo service zabbix-agent restart
) у него появляется возможность собирать данные для параметра redis.info
и отправлять их на сервер.
Понимание Zabbix'а ко мне приходило (и всё ещё приходит) достаточно тяжело. Тем не менее я считаю его прекрасным инструментом, особенно после того, как для меня открылась простота добавления собственных параметров мониторинга (элементов данных). По большому счёту, достаточно добавить один файл на сервер с агентом (userparameter_XXX.conf
) с shell-командой для сбора данных и настроить Zabbix-сервер на получение этих данных через web-интерфейс. И всё — можно накапливать данные, строить графики, анализировать изменения и создавать триггера, реагирующие на эти изменения.
Код шаблона, файла userparameter_redis.conf
и скрипта get_info.sh
можно посмотреть в проекте flancer32/zabbix_plugin_redis.
Спасибо всем, кто дочитал до конца, а особенно тем, кто нашёл в публикации что-то полезное для себя.
К сожалению, не доступен сервер mySQL