Система мониторинга: зачем?
Многие из вас, кому довелось отвечать за небольшую подсеть, сталкивались с проблемой учёта работоспособности пары десятков машин. Либо Вам просто захотелось иметь возможность в любой момент времени из любой точки планеты узнать как себя чувствует ваша торрент-качалка, оставленная включенной дома.
Лично я разрабатывал эту систему для решения проблемы скрытого удаленного наблюдения за вверенными мне компами. На этапе реализации мне предложили получить некоторый профит с этого проекта и сделать все на 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
![](https://habrastorage.org/storage2/f55/aa0/53f/f55aa053f70e2a6fb44e3eedbeada9b7.png)
Как видно, я поставил обновление на каждые пять минут. Благодаря libnotify я получаю отчеты:
![](https://habrastorage.org/storage2/9a2/f29/caf/9a2f29cafdd5ce01d254b46500e9c786.png)
Веб-интерфейс же выглядит следующим образом:
![](https://habrastorage.org/storage2/279/aec/30d/279aec30d2d79174c3a3ffa9263ed441.png)
Всё как и описывал: фиксированная панель слева, остальное с маргином на правый бок динамически масштабируется, информация разбита по блокам, подсветка строк.
Проект опенсурсный, если есть желание — улучшайте, используйте, славьте РМС.