[recovery mode] Poor man's monitoring или мониторим сервера из консоли
Всех приветствую уважаемые читатели. В данной статье я расскажу Вам о моём «велосипеде», на котором я делаю мониторинг разных вещей не выходя из консоли.
Столкнулся я как-то раз с ситуацией, когда расплодилось довольно много разных проектов и серверов, а настроить нормальный мониторинг руки не доходили.
Да и в современном мире «правильный» мониторинг подразумевает развертывание целой кучи софта, настройки всего этого дела. Ну вы знаете там… докер, эластик стек и пошло поехало. Для меня это был сильный оверхед. Хотелось чтобы раз-два и в продакшн.
Смотрел я в сторону Simple monitor на питоне, он был ближе всего мне по духу, но у него не хватало довольно многих фич. А мне заодно хотелось подучить Go… ну вообщем Вы сами знаете как обычно это все начинается.
Поэтому я взял сварку Go, и сколотил этот Велосипед.
Cli Monitoring написан на Go и представляет собой набор бинарников, каждый из которых получает данные из stdin, выполняет какую-то свою конкретную задачу и выводит результат в stdout.
Всего есть четыре типа бинарников: metrics, processors, filters, и outputs.
Метрики, как понятно из названия, собирают какие-либо данные и обычно идут первыми в цепочке.
Процессоры находятся посредине и как-либо меняют данные или выполняют другие утилитные функции.
Фильтры почти как процессоры, но в отличии от них пропускают либо не пропускают данные в зависимости от условия.
Выходы находятся на выходе из цепочки и служат для отправки уведомлений в различные сервисы.
Вся цепочка команд обычно имеет вид:
some_metric | processor_1 | processor_2 ... | cm_p_message | output_1 | output_2 ...
Любой кусок этой цепочки может быть любой линуксовой командой, лишь бы она получала данные в stdin и отдавала их в stdout без буферизации. Есть только одно небольшое НО, связанное с переносом строк, но об этом чуть позже.
Название бинарников формируется как cm_{type}_{name}, где type одно из трех: m, p, f или o, а name название команды.
К примеру cm_m_cpu — это метрика, которая выводит в stdout статистику по процессору в формате json.
А файл cm_p_debounce — это процессор, который пропускает на выход только одно сообщение раз в заданный интервал.
Есть один особый процессор cm_p_message, который должен стоять перед первым output. Он создает сообщение нужного формата для последующей обработки его Выходами.
Для обработки json в консоли и различных условий я применил утилиту jq. Это что-то вроде sed, только для json.
Вот так, к примеру, выглядит в итоге мониторинг загрузки процессора.
cm_m_cpu | cm_p_eot2nl | jq -cM --unbuffered 'if .LoadAvg1 > 1 then .LoadAvg1 else false end' | cm_p_nl2eot | cm_f_regex -e '\d+' | cm_p_debounce -i 60 | cm_p_message -m 'Load average is {stdin}' | cm_o_telegram
А так мониторинг к-ва сообщений в очереди RabbitMQ
while true; do rabbitmqctl list_queues -p queue_name | grep -Po --line-buffered '\d+'; sleep 60; done | jq -cM '. > 10000' --unbuffered | cm_p_nl2eot | cm_f_true | cm_p_message -m 'There are more than 10000 tasks in rabbit queue' | cm_o_opsgenie
Так можно мониторить что в файл ничего не писалось уже как 10 секунд
tail -f out.log | cm_p_nl2eot | cm_p_watchdog -i 10 | cm_p_debounce -i 3600 | cm_p_message -m 'No write to out.log for 10 seconds' -s 'alert' | cm_o_telegram
Не спешите закрывать экран, сейчас разберем что тут происходит на первом примере.
1) Метрика cm_m_cpu выводит раз в секунду (задается параметром -i, по-умолчанию секунда) строки в формате json. К примеру {«LoadAvg1»:2.0332031, «LoadAvg2»:1.9018555, «LoadAvg3»:1.8623047}
2) cm_p_nl2eot одна из утилитных команд, которая преобразует символ EOT в символ LF. Дело в том что во избежании проблем с переносом строки, я решил сделать так, что все мои бинарники читают данные вплоть до ascii символа EOT (End of Transmission). Это позволяет безопасно передавать многострочные данные между командами.
Поэтому когда происходит вызов любых других команд, они должны быть окружены в виде:
cm_p_eot2nl | любая другая команда | cm_p_nl2eot.
3) Далее следует вызов утилиты jq, которая проверяет поле LoadAvg1 и если оно больше 1, то выводит его далее, если меньше, выводит false
4) Далее нам надо выкинуть из цепочки все сообщение false. Для этого применяем фильтр cm_f_regex, который принимает на вход строку, сопоставляет её с регулярным выражением, и в случае с совпадением выводит дальше. В противном случае строка просто отбрасывается
Можно было бы использовать и обычный grep, но во-первых он буферизирует вывод, и полный синтаксис становится чуть длиннее (grep --line-buffered), во вторых cm_f_regex позволяет очень просто вывести совпадения групп. К примеру:
cm_f_regex -e '(\d+)-(\d+)' -o '{1}/{2}'
Преобразует строку 123–345 в строку 123/345
5) Процессор cm_p_debounce, в данном случае принимает наше значение LoadAvg1 и выводит его дальше по цепочке только раз в 60 секунд. Это нужно для того чтобы не заспамить самого себя. Можно выставить любой другой интервал.
6) Почти все готово. Осталось только сформировать сообщение и отправить его в телеграм. Сообщение формируется специальной командой cm_p_message. Она просто принимает на вход строку, создает json с полями Severity, Message и другими и выводит далее для обработки output’ами. Если бы мы не передали ей параметр -m, то в качестве сообщения был бы stdin, т.е. просо число — наш LoadAvg1. Это не очень информативно.
7) Комада cm_o_telegram просто отправляет полученное на вход сообщение в телеграм. Настройки телеграма хранятся в ini файле.
Конфигурация
Все параметры которые принимают бинарники, можно указать в ini файле. Параметры заданные аргументом командной строки имеют приоритет перед ini файлом.
Формат init файла имеет вид:
[global]
host_name=override host name for this machine
[telegram]
cid=…
token=…
[opsgenie]
apiToken=…
apiEndpoint=…
…
[debounce]
i=3600
Сам ini файл выбирается в следующем порядке:
1) Файл cm.config.ini в текущей рабочей директории
2) Файл /etc/cm/config.ini если файл из п.1 не найден
Продакшн
На реальном сервере я создаю файл к примеру cpu.sh, в котором записана вся нужная цепочка команд. Далее в кроне прописываю что-то вроде этого:
*/5 * * * * flock -n /etc/cm/cpu.lock /etc/cm/cpu.sh > /dev/null
Если что-от упало flock переподнимет команду. И все! Простота которой мне не так не хватало.
Вот такой получился инструмент, возможно кто-то посчитает его удобным. Для меня удобство в том что не надо городить много лишнего чтобы просто мониторить необходимые вещи. Да и настраивается это все довольно удобно: склонировал репозиторий, добавил в $PATH путь к бинарникам и все.
Прошу не судить строго. Инструмент писался для себя, набор команд пока не большой. Но я буду рад любым отзывам и пожеланиям. Всем спасибо за внимание.