Система мониторинга: зачем?
Многие из вас, кому довелось отвечать за небольшую подсеть, сталкивались с проблемой учёта работоспособности пары десятков машин. Либо Вам просто захотелось иметь возможность в любой момент времени из любой точки планеты узнать как себя чувствует ваша торрент-качалка, оставленная включенной дома.
Лично я разрабатывал эту систему для решения проблемы скрытого удаленного наблюдения за вверенными мне компами. На этапе реализации мне предложили получить некоторый профит с этого проекта и сделать все на BASH как проект для некоторой конференции.
… и вот, разгребая хлам, я нашел исходники. Время работы веб-программистом не прошло даром, было решено полностью переверстать и расширить функционал. Собственно, начнем…
Описание платформы, зависимости
Не так давно я обновился до 12.10й версии всеми любимой Kubuntu (Ubuntu с KDE в качестве WM, GNOME не переношу). Система девственно чиста, никаких манипуляций с ней не производилось, так что в ней не будет пакетов, которых нет у Вас.
Ядро 3.5.0-21, KDE. Для работы самой системы нам понадобятся дополнительные пакеты, которые можно найти в стандартном репозитории. Набираем следующее:
sudo -s
apt-get install perl
apt-get install libnotify-bin
apt-get install lm-sensors
apt-get install sysstat
apt-get install apache2
exit
Перл нам будет нужен для выполнения скрипта-обработчика строк, libnotify — для вывода уведомления, lm-sensors — для снятия температуры, sysstat — для сбора статистики по I/O, apache2 — вебсервер для отображения. Ну, само-собой нужен любой текстовый редактор с подсветкой синтаксиса.
Реализация
Сначала я сверстал шаблон на дивах, в ряде случаев получился тизер фильма «Начало», но в целом структура дерева DOM корректна. Дальше спроектируем систему, которая будет за��олнять этот шаблон. Для этого напишем скрипт на BASH. Для этого следует знать, что для нашего (Ubuntu) дистрибутива дефолтной директорией для вебсервера апач будет /var/www. Следовательно, если кроме нашей системы ничего не будет в открытом доступе, вывод реализуем сразу в index.html.
Сам скрипт предельно простой: команда echo выводит в stdout входной параметр, мы этот выхлоп перенаправляем в файл. Так мы сможем передать в html из BASH шаблон. С точки зрения теории алгоритмов, мы получаем уже специализированный код.
#!/bin/bash
export DISPLAY=:0.0;
echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<HTML>
<!-- Coded and designed by 19N4T0V -->
<head>
<meta http-equiv="Content-Type" content="text/html; charset=KOI8-R">
<script type="text/javascript" src="jquery.js"></script>
<title>Kansatsu</title>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div class="leftline">
<a href=""><div class="header"></div></a>
<ul class="leftmenu">
<a href="#summary"><li>SUMMARY</li></a>
<a href="#IO"><li>I/O STATS</li></a>
<a href="#temperature"><li>TEMPERATURE</li></a>
<a href="#HDD"><li>HDD</li></a>
<a href="#RAM"><li>RAM</li></a>
<a href="#net"><li>NETWORK</li></a>
<a href="#PROC"><li>PROC</li></a>
</ul>
</div>
<div class="content">
<!--ABOUT BEGINS HERE-->
<div class="block">
<div class="bl_head">
ABOUT
</div>
<div class="bl_info">
KANSATSU monitoring system is an opensource project. License is <a href="http://www.gnu.org/copyleft/"><font color="red">GNU copyleft</font></a>. This is the simple scripts written in BASH and Perl, running as a cron job . Everything you can see there is just responding to console comands. Author - <a href="http://19N4T0V.PNZ.RU/"><font color="red">19N4T0V</font></a>. If you want to receive the code to use or improve it - write me. <br>
Thanks to <a href="http://linux.org.ru/"><font color="red">LOR</font></a> users for help and advices.<br>
DEPENDS: lm-sensors, libnotify-bin, perl, sysstat.
</div>
</div>
<!--SUMMARY BEGINS HERE-->
<div class="block" id = "summary">
<div class="bl_head">
SUMMARY
</div>
<div class="bl_info">
<div class="line">
<div class="tit">
HOSTNAME
</div>
<div class="tty">' > /var/www/index.html
hostname >> /var/www/index.html;
echo ' </div>
</div>
<div class="line">
<div class="tit">
UPTIME
</div>
<div class="tty">' >> /var/www/index.html;
uptime | sed 's/,.*//' | sed 's/.*up //' >> /var/www/index.html;
echo ' </div>
</div>
<div class="line">
<div class="tit">
TIMESTAMP
</div>
<div class="tty">' >>/var/www/index.html;
date >> /var/www/index.html;
echo ' </div>
</div>
<div class="line">
<div class="tit">
KERNEL
</div>
<div class="tty">'>> /var/www/index.html;
uname -s >> /var/www/index.html;
echo ' </div>
</div>
<div class="line">
<div class="tit">
VERSION
</div>
<div class="tty">' >> /var/www/index.html;
uname -r >> /var/www/index.html;
echo ' </div>
</div>
<div class="line">
<div class="tit">
HARDWARE
</div>
<div class="tty">' >> /var/www/index.html;
uname -m >> /var/www/index.html;
echo ' </div>
</div>
<div class="line">
<div class="tit">
PROCESSOR
</div>
<div class="tty">' >> /var/www/index.html;
uname -p >> /var/www/index.html;
echo ' </div>
</div>
<div class="line">
<div class="tit">
PLATFORM
</div>
<div class="tty">' >> /var/www/index.html;
uname -i >> /var/www/index.html;
echo ' </div>
</div>
<div class="line">
<div class="tit">
OS
</div>
<div class="tty">' >> /var/www/index.html;
uname -o >> /var/www/index.html;
echo ' </div>
</div>
</div>
</div>
<!--IO BEGINS HERE--> ' >> /var/www/index.html;
echo '
<div class="block" id ="IO">
<div class="bl_head">
I/O STATS
</div>
<div class="bl_info"> ' >> /var/www/index.html;
iostat -h -x > /var/www/tmp;
perl /var/www/scr.pl;
echo ' </div>
</div>
</div>
</div>
<!--TEMPERATURE BEGINS HERE-->
<div class="block" id ="temperature">
<div class="bl_head">
TEMPERATURE
</div>
<div class="bl_info">
<div class="line">
<div class="tit">
CHIPSET CURRENT
</div>
<div class="tty">' >> /var/www/index.html;
sensors -u | grep "temp1_input" | sed 's/temp1_input://' | sed 's/ *//g' | sed 's/\.000//' >> /var/www/index.html;
echo ' </div>
</div>
<div class="line">
<div class="tit">
CHIPSET CRITICAL
</div>
<div class="tty">' >>/var/www/index.html;
sensors -u | grep "temp1_crit" | sed 's/temp1_crit://' | sed 's/ *//g' | sed 's/\.000//' >> /var/www/index.html;
echo ' </div>
</div>
<div class="line">
<div class="tit">
CORE1 CURRENT
</div>
<div class="tty">' >> /var/www/index.html;
sensors -u | grep "temp2_input" | sed 's/.*: //' | sed 's/.000//' >> /var/www/index.html;
echo ' </div>
</div>
<div class="line">
<div class="tit">
CORE1 CRITICAL
</div>
<div class="tty">' >> /var/www/index.html;
sensors -u | grep "temp2_crit:" | sed 's/.*: //' | sed 's/.000//' >> /var/www/index.html;
echo ' </div>
</div>
<div class="line">
<div class="tit">
CORE2 CURRENT
</div>
<div class="tty">' >> /var/www/index.html;
sensors -u | grep "temp3_input" | sed 's/.*: //' | sed 's/.000//' >> /var/www/index.html;
echo ' </div>
</div>
<div class="line">
<div class="tit">
CORE2 CRITICAL
</div>
<div class="tty">' >> /var/www/index.html;
sensors -u | grep "temp3_crit:" | sed 's/.*: //' | sed 's/.000//' >> /var/www/index.html;
echo ' </div>
</div>
</div>
</div>
<!--HDD BEGINS HERE-->
<div class="block" id="HDD">
<div class="bl_head">
HDD
</div>
<div class="bl_info">' >> /var/www/index.html;
df -a -h > /var/www/tmp;
perl /var/www/scr.pl;
echo ' </div>
</div>
<!--RAM BEGINS HERE-->
<div class="block" id="RAM">
<div class="bl_head">
RAM
</div>
<div class="bl_info">' >> /var/www/index.html;
free -l -h > /var/www/tmp;
perl /var/www/scr.pl;
echo '</div></div>
<!--NETWORK BEGINS HERE-->
<div class="block" id="net">
<div class="bl_head">
NETWORK
</div>
<div class="bl_info">' >> /var/www/index.html;
ifconfig > /var/www/tmp;
perl /var/www/scr.pl;
echo ' </div>
</div>
<!--PROC BEGINS HERE-->
<div class="block" id="PROC">
<div class="bl_head">
PROC
</div>
<div class="bl_info">' >> /var/www/index.html;
top -n1 -b > /var/www/tmp;
perl /var/www/scr.pl;
echo ' </div>
</div>' >> /var/www/index.html;
#footer, closing "content" div and finishing DOM tree
echo '</div>
</body>
</html>' >> /var/www/index.html;
notify-send "KANSATSU" "Report has been updated";
Мы видим перенаправление потока вывода echo в изобилии. Так получается шаблон. Для заполнения шаблона используем ту же систему перенаправления потока вывода для обычных команд. Если рассмотреть подробнее, увидим следующее:
hostname >> /var/www/index.html;
uptime | sed 's/,.*//' | sed 's/.*up //' >> /var/www/index.html;
date >> /var/www/index.html;
uname -s >> /var/www/index.html;
uname -r >> /var/www/index.html;
uname -m >> /var/www/index.html;
uname -p >> /var/www/index.html;
uname -i >> /var/www/index.html;
uname -o >> /var/www/index.html;
iostat -h -x > /var/www/tmp;
perl /var/www/scr.pl;
sensors -u | grep "temp1_input" | sed 's/temp1_input://' | sed 's/ *//g' | sed 's/\.000//' >> /var/www/index.html;
sensors -u | grep "temp1_crit" | sed 's/temp1_crit://' | sed 's/ *//g' | sed 's/\.000//' >> /var/www/index.html;
sensors -u | grep "temp2_input" | sed 's/.*: //' | sed 's/.000//' >> /var/www/index.html;
sensors -u | grep "temp2_crit:" | sed 's/.*: //' | sed 's/.000//' >> /var/www/index.html;
sensors -u | grep "temp3_input" | sed 's/.*: //' | sed 's/.000//' >> /var/www/index.html;
sensors -u | grep "temp3_crit:" | sed 's/.*: //' | sed 's/.000//' >> /var/www/index.html;
df -a -h > /var/www/tmp;
perl /var/www/scr.pl;
free -l -h > /var/www/tmp;
perl /var/www/scr.pl;
ifconfig > /var/www/tmp;
perl /var/www/scr.pl;
top -n1 -b > /var/www/tmp;
perl /var/www/scr.pl;
notify-send "KANSATSU" "Report has been updated";
Это скелет нашей системы. Можно сказать, что в таком виде — это консольная версия для liks2, например. Рассмотрим команды и дадим комментарии:
hostname возвращает имя хоста, для которого собирается информация.
uptime возвращает аптайм системы. В нашем случае он уходит по конвееру в sed, строковый редактор, где регэкспом замены из выхлопа этой команды мы выделяем лишь цифры: часы и минуты.
uname с различными параметрами — общие сведения о машине: архитектура железа, процессора, ядро, ОС…
iostat с ключом -x дает расширенное описание по работе ввода/вывода. Ключ -h обещает сделать вывод читабельным.
sensors возвращает температуру двух моих ядер и температуру матери. Ключ -u дает расширенный вывод, далее по конвееру выхлоп идет в греп, что позволяет из всего вывода выбрать одну строку с введенной подстрокой, потом в sed, где мы снова убираем лишние символы регэкспом. У меня всего три датчика, посему я вывожу 6 строк: текущие и критические температуры.
df расскажет нам о жестком диске, с флагом -a — о всех разделах, с флагом -h позволит разобрать выхлоп человеку.
free — отчетик о использовании памяти. Здесь с флагами для удобного вывода и полного отчета.
ifconfig — полный отчет о всех сетевых интерфесах. Я взял его as is.
top выводит в бесконечном цикле самые прожорливые процессы. Я использовал флаг -b для вывода в файл, что убирает спецсимволы и с флагом -n1 — указываю количество снятий показаний. Нужно отметить, что с таким набором флагов я получил список всех процессов. Если учесть, что порой надо посмотреть кто чем занят на удаленной машине — легко отслеживается по этому списку.
И в заключение notify-send дает системное сообщение об удачном обновлении отчета.
Perl-скрипт
А никто ни о чем не умолчал! Просто для этого нужна отдельная тема. Я верстал всё на дивах и не хотел использовать ни одной таблицы. В случаях, когда я получал единственное значение из утилиты, я его описание и само значение упаковывал в два дива в одну строку. Все по столбикам, все красиво. Но теперь на выходе я получаю целые таблицы. Я бы мог их сунуть в html как есть, использовав тег pre, сохранив тем самым исходное форматирование. Но в таком случае длинные таблицы будут нечитабельны, ибо для события hover дива line, в который заключается каждая строка, у меня установлена подсветка всей строки, чтобы не приходилось прик��адывать линейку к монитору или вести курсором вдоль строки до нужного столбца. Вполне лочично, что такая тенденция должна сохраняться. Для этого, собственно, я и сделал следующий ход конем: если присмотреться, везде, где используется perl, строкой выше выхлоп направлен не в html, а в /var/www/tmp. Это текстовой файл. Перл-скрипт должен построчно читать этот файл и «облачать» каждую строку в див, после чего выплёвывать это в html. Что ж, сказано — сделано:
#! perl -w
open (STREAM_IN, '/var/www/tmp');# || die "Can't open STREAM_IN\n";
open (STREAM_OUT, '>> /var/www/index.html');# || die "Can't open STREAM_OUT\n";
$s1 = '<div class="line">';
$s2 = '</div>';
while ($curr = <STREAM_IN>)
{
chomp($curr);
$curr=~s/\s/\ /g;
$out = $s1.$curr.$s2;
print (STREAM_OUT $out);
};
close STREAM_IN;
close STREAM_OUT;
Из кода видно, что мы берем строку, отсекаем символ перевода строки, меняем пробелы на коды (консолью таблица формируется именно пробелами, если дать их в html как есть — будет каша). Статически задаем вспомогательные строки и конкатенацией получаем одну строку, которая дописывается в конец html-отчета. Из кода скрипта на BASH видно, что следом за подобными конструкциями следует echo, которое спешит закрыть дивы.
Такой скрипт работает с любым содержимым одного и того же файла и дозаписывает его в конец другого файла. Я всегда отправляю вывод таблицы во временный файл на ПЕРЕ запись, он сам очищается, потом вызывается скрипт, который этот вывод приводит к общему шаблону и ДОзаписывает к html. Один скрипт на все времена).
Дополнительные файлы, последние штрихи
Совершенно очевидно, что нужно написать таблицу стилей. Я преследовал принцип KISS, хотел максимум функциональности и минимум загруженности. Выбрал следующее решение: слева фиксированно будут ссылки на каждый блок отчета, а сам отчет будет листаться справа.
html
{
height:100%;
width:100%
}
body
{
height:100%;
width:100%;
padding:0px;
margin:0;
color:#000;
font-family:Arial;
font-size:14px;
background-color:#FFF;
}
.leftline
{
width:210px;
overflow:hidden;
float:left;
height:100%;
position:fixed;
top:0px;
left:0px;
}
.header
{
width:100%;
float:none;
background:url(logo.png) top left no-repeat;
height:160px;
}
.leftmenu
{
padding:0px;
}
.leftmenu li
{
width:200px;
height:30px;
background:#000;
font-size:24px;
line-height:1.3;
float:none;
margin:10px 10px 10px 0px ;
text-align:right;
padding-right:10px;
list-style:none
}
.leftmenu li:hover
{
background:#FF0000;
}
a
{
color:#FFF;
text-decoration:none;
}
.content
{
}
.block
{
float:none;
margin-top:20px;
margin-left:250px;
margin-right:50px;
border-left:2px black solid;
}
.bl_head
{
color:#fff;
background:#000;
padding:10px 0px 10px 10px;
}
.bl_info
{
color:#000;
padding-left:10px;
padding-top:5px;
}
.line
{
display:block;
height:20px;
cursor:default;
}
.line:hover
{
background:red;
}
.tit
{
width:200px;
float:left;
}
.tty
{
}
По коду — это всё. Нужно лишь еще 2 файла и установить права. Первый файл — это, собственно, логотип. Полчаса GIMP'a и вуаля! Второй файл — тот самый временный. У меня он назван tmp. Всё это я бросил в одну большую кучу в директорию /var/www. Далее установил права на файлы. Пока эта версия находится в отладке, я решил сделать очешь щедрый поступок:
sudo chmod 777 /var/www/index.html
sudo chmod 777 /var/www/tmp
sudo chmod 777 /var/www/scr.pl
sudo chmod 777 /var/www/style.css
Собственно, сисиема готова к использованию. Но есть одно но: обновлять придется руками. Для отладки я прописал в башрц новый алиас. Саму систему я назвал Kansatsu, что означает «наблюдение». Посему выглядит алиас следующим образом:
alias kansatsu="sh /var/www/kansatsu.sh"
Автоматизация
Не дело иметь под рукой такую мощную ОСь и делать что-то руками. Здесь нас выручает старый добрый крон.
crontab -e

Как видно, я поставил обновление на каждые пять минут. Благодаря libnotify я получаю отчеты:

Веб-интерфейс же выглядит следующим образом:

Всё как и описывал: фиксированная панель слева, остальное с маргином на правый бок динамически масштабируется, информация разбита по блокам, подсветка строк.
Проект опенсурсный, если есть желание — улучшайте, используйте, славьте РМС.