Как получить статистику в графиках или collectd+rrd

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

Итак, что имеем? Имеем сервер (или рабочий компьютер) под управлением Linux (в моём случае — Ubuntu).

Что желаем? Желаем видеть, насколько загружены его ресурсы (сетевой траффик, объём памяти, свободное место на дисках etc) в красивых графиках.

Что ж, начнём работать.

Подразумевается, что веб-сервер у нас установлен. Для примера будет использован адрес сервера 192.168.1.1. Нам понадобятся ещё кое-какие программы, но это потом — так сказать, добавить по вкусу.

Итак, этап первый. Установка collectd и rrdtool. У меня в Ubuntu это делается одной командой:

sudo apt-get install collectd rrdtool

Собственно, rrdtool нам даже настраивать не надо — оно просто будет работать сразу. А вот collectd необходимо немного настроить. Мой конфиг:

# Наименование компьютера, на котором будут собираться данные
# Очень важный момент, так как collectd создаёт папку с именем хоста, чтобы хранить там данные
Hostname "sGate"
# Папка, в которой будут собиратся данные по хостам
BaseDir "/usr/lib/collectd/rrd"

# Указываем, какие плагины желаем использовать
LoadPlugin logfile
LoadPlugin syslog
LoadPlugin cpu
LoadPlugin df
LoadPlugin interface
LoadPlugin network
LoadPlugin memory
LoadPlugin rrdtool
LoadPlugin swap
LoadPlugin hddtemp

# Далее указываем настройки плагинов
# Многие плагины работают с параметрами по умолчанию, которые нет необходимости настраивать
<plugin logfile>
        LogLevel "info"
        File "/var/log/collectd.log"
        Timestamp true
        PrintSeverity false
</plugin>

<plugin syslog>
        LogLevel info
</plugin>

<plugin network>
# Настройки для собирающего сервера
    <listen "192.168.1.1" "25826">
        SecurityLevel none
        Interface "eth1"
    </listen>
# Настройки для передающей программы
    Server "192.168.1.1"
</plugin>

# Указываем имя сетевого интерфейса, за траффиком которого будем следить
<plugin interface>
        Interface "eth1"
</plugin>
# Указываем имена устройств, за которыми будем следить
<plugin df>
        Device "/dev/sdb1"
        Device "/dev/sdc1"
        Device "/dev/sdb2"
        Device "/dev/sdd4"
</plugin>

Выполняем команду

sudo /etc/init.d/collectd restart

После этого в папке /usr/lib/collectd/rrd должна появится папка именем хоста (у меня это sGate), в которой будут папки, содержащие базы rrd. Для работы некоторых плагинов необходимы дополнительные программы. Например, для hddtemp необходима программа hddtemp. В Ubuntu она устанавливается командой:

sudo aptitude install hddtemp

Теперь, когда данные собираются, надо настроить построение графиков по этим данным. Для этого создадим файл creategraph.sh в любой любимой папке, например, в /usr/lib/collectd/rrd. Вот пример, который работает у меня:

#!/bin/bash
# Первый аргумент (обязательный) - источник данных (all - все, что есть)
# Второй аргумент (обязательный) - временной диапазон (all - все, что предусмотрены)
# Третий аргумент (small) - если указан, то делается маленькая картинка (превью)

# Все следующие аргументы необязательные, они просто добавятся к параметрам вызова rrdtool
# Текстовые параметры в строке задаются с помощью экранированных кавычек: --title \"Пример заголовка\"

# Эта переменная содержит все источники данных, которые нам нужны. Она стоит тут, чтобы её было легко пополнить или изменить
allsrc="cpu eth1 memory df temp"
# Эта переменная содержит все временнные диапазоны,которые нам нужны. Она стоит тут, чтобы её было легко пополнить или изменить
alltime="6h 1day 1week 1month 3month 6month 1year"

# Определяем глобальные переменные
# Путь до баз rrd, которые созданы collectd
dbpath=/usr/lib/collectd/rrd/sGate
# Путь, куда складывать картинки
wwwpath=/var/www/html/graph/
# Цвет фона картинки
colorBack=#333333
# Цвет фона графика
colorCanvas=#000000
# Цвет верхней и левой границы вокруг картинки
colorA=#333333
# Цвет правой и нижней границы вокруг картинки
colorB=#333333
# Цвет рамки вокруг цветовых обозначений графиков
colorFrame=#000000
# Цвет стрелок абсцисс и ординат на графике
colorArrow=#999999
# Цвет малых рисок на графике
colorGrid=#999999
# Цвет больших рисок на графике
colorMGrid=#999999
# Цвет шрифта
colorFont=#eeeeee
# Шрифт по умолчанию для всего
fontDefault="8:'DejaVu Sans Mono'"
# Шрифт для заголовка
fontTitle="10:'DejaVu Sans Mono'"
# Шрифт для значений данных на шкалах графиков
fontAxis="8:'DejaVu Sans Mono'"
# Шрифт для отображения названия единиц измерения
fontUnit="8:'DejaVu Sans Mono'"
# Шрифт для отображения описания графиков
fontLegend="8:'DejaVu Sans Mono'"
# Шрифт для отображения подписи
fontWatermark="6:'DejaVu Sans Mono'"
# Основные параметры построения графика
#--lazy
mainpars="--border 0 --width 1140 --height 300 --font-render-mode light -e now --imgformat PNG --slope-mode --right-axis 1:0 -E --grid-dash 1:1 \
--color SHADEA$colorA --color SHADEB$colorB --color BACK$colorBack --color CANVAS$colorCanvas --color FRAME$colorFrame --color ARROW$colorArrow \
--color GRID$colorGrid --color MGRID$colorMGrid --color FONT$colorFont \
--font DEFAULT:$fontDefault --font TITLE:$fontTitle --font AXIS:$fontAxis --font UNIT:$fontUnit --font LEGEND:$fontLegend --font WATERMARK:$fontWatermark"

# Получаем первый аргумент
if [[ "all" == ${1} ]];then src=$allsrc;else src=${1};fi
# Получаем второй аргумент
if [[ "all" == ${2} ]];then time=$alltime;else time=${2};fi
# Получаем третий аргумент
# Если указан, то его в дополнительных параметрах не учитываем
if [[ "small" == ${3} ]]
then
 preview="--only-graph -w 300 -h 150"
 small="_preview"
 nmax=3
else
 preview=""
 small=""
 nmax=2
fi
# Получаем остальные аргументы и складываем в одну переменную
n=1
for i in "$@"
do
    #echo "Вы написали: ${i}"
    if [[ $n > $nmax ]]; then addpars="$addpars${i} "; fi
    n=$(( $n + 1 ))
done
# Начинаем основной перебор - по источникам данных
for cur_src in $src
do
 # относительно источника данных подготавливаем уникальные параметры графика
 case $cur_src in
  cpu)
  title="CPU"
  params=" $allpar\
 --vertical-label \"% загрузки\"\
 --right-axis-format %0.1lf\
 --rigid \
 DEF:s0=$dbpath/cpu-0/cpu-system.rrd:value:MAX\
 DEF:u0=$dbpath/cpu-0/cpu-user.rrd:value:MAX\
 DEF:w0=$dbpath/cpu-0/cpu-wait.rrd:value:MAX\
 DEF:s1=$dbpath/cpu-1/cpu-system.rrd:value:MAX\
 DEF:u1=$dbpath/cpu-1/cpu-user.rrd:value:MAX\
 DEF:w1=$dbpath/cpu-1/cpu-wait.rrd:value:MAX\
 CDEF:u=u0,u1,+\
 CDEF:uw=u,w0,+,w1,+\
 CDEF:uws=u,s0,+,s1,+,w0,+,w1,+\
 COMMENT:\"\t\"\
 AREA:uws#ff7272:\"Система\"\
 AREA:uw#5e97ec:\"Ожидание\"\
 AREA:u#fde965:\"Пользователь\l\""
  ;;
  eth1)
  title="Внешний канал"
  params=" $allpar\
 --vertical-label \"Мегабит в секунду\" \
 DEF:a=$dbpath/interface/if_packets-eth1.rrd:rx:MAX \
 DEF:b=$dbpath/interface/if_packets-eth1.rrd:tx:MAX \
 DEF:a_e=$dbpath/interface/if_errors-eth1.rrd:rx:MAX \
 DEF:b_e=$dbpath/interface/if_errors-eth1.rrd:tx:MAX \
 CDEF:b_neg=b,-1,* \
 CDEF:b_eneg=b_e,-1,* \
 COMMENT:\"\t\"\
 AREA:a#54eb48:\"Загрузка входящего канала\" \
 LINE1:a#37a92e: \
 LINE1:a_e#FF0000: \
 AREA:b_neg#4169E1:\"Загрузка исходящего канала\l\" \
 LINE1:b_neg#253c7e: \
 LINE1:b_eneg#FF0000:"
  ;;
  memory)
  title="ОЗУ"
 params=" $allpar\
 --vertical-label \"Объём ОЗУ\" \
 --base 1024 \
 DEF:f=$dbpath/memory/memory-free.rrd:value:MAX \
 DEF:c=$dbpath/memory/memory-cached.rrd:value:MAX \
 DEF:b=$dbpath/memory/memory-buffered.rrd:value:MAX \
 DEF:u=$dbpath/memory/memory-used.rrd:value:MAX \
 CDEF:fcbu=f,c,+,b,+,u,+ \
 CDEF:cbu=c,b,+,u,+ \
 CDEF:bu=b,u,+ \
 COMMENT:\"\t\"\
 AREA:fcbu#d9f6e8:\"Свободная\" \
 AREA:cbu#aaf5d0:\"Кэш\" \
 AREA:bu#00ff82:\"Буферы\" \
 AREA:u#00b35b:\"Используется\l\""
  ;;
  df)
  title="Свободное дисковое пространство"
 params=" $allpar\
 --vertical-label \"Объём\" \
 --base 1024 \
 DEF:home=$dbpath/df/df-home.rrd:free:MAX \
 DEF:mus=$dbpath/df/df-mnt-terramus.rrd:free:MAX \
 DEF:tor=$dbpath/df/df-mnt-terrator.rrd:free:MAX \
 DEF:other=$dbpath/df/df-mnt-terraother.rrd:free:MAX \
 COMMENT:\"\t\"\
 LINE1:home#FF0000:\"Домашняя папка\" \
 LINE1:tor#FFff00:\"Торренты\" \
 LINE1:other#FF00ff:\"Всякая всячина\" \
 LINE1:mus#00ff00:\"Музыка\l\""
  ;;
  temp)
  title="Свободное дисковое пространство"
 params=" $allpar\
 --vertical-label \"Температура °C\" \
 --rigid \
 DEF:v0=$dbpath/hddtemp/temperature-8-0.rrd:value:MAX \
 DEF:v16=$dbpath/hddtemp/temperature-8-16.rrd:value:MAX \
 DEF:v32=$dbpath/hddtemp/temperature-8-32.rrd:value:MAX \
 DEF:v48=$dbpath/hddtemp/temperature-8-48.rrd:value:MAX \
 COMMENT:\"\t\"\
 LINE1:v0#FF0000:\"/dev/sda\" \
 LINE1:v16#FFff00:\"/dev/sdb\" \
 LINE1:v32#0000ff:\"/dev/sdc\" \
 LINE1:v48#00ff00:\"/dev/sdd\l\""
 ;;
 esac
 # Внутренний цикл - по диапазонам времени
 for cur_time in $time
 do
  case $cur_time in
   6h)
    title_res="$title - шесть часов"
   ;;
   1day)
    title_res="$title - сутки"
   ;;
   1week)
    title_res="$title - неделя"
   ;;
   1month)
    title_res="$title - месяц"
   ;;
   3month)
    title_res="$title - квартал"
   ;;
   6month)
    title_res="$title - полгода"
   ;;
   1year)
    title_res="$title - год"
   ;;
  esac
  fname="_$cur_time$small.png"
  # относительно времени подготавливаем уникальные параметры графика
  command="rrdtool graph $wwwpath$cur_src$fname --title \"$title_res\" $mainpars $params -s -$cur_time $addpars $preview"
  # Выполняем команду
  eval $command
 done
done

Разумеется, необходимо проверить, что папка, указываемая в переменной wwwpath, существует. Делаем файл исполняемым — теперь можно проверить его работу:

./creategraph.sh eth1 all

Скрипт должен создать семь графиков, если вы ничего не меняли в creategraph.sh.

Остаётся лишь настроить в cron'е запуск нашего скрипта. У меня это делается следующим образом:

*/5 *   * * *   avesnin    /usr/lib/collectd/rrd/ all 6h > /dev/null 2>&1
*/5 *   * * *   avesnin    /usr/lib/collectd/rrd/creategraph.sh all 6h small > /dev/null 2>&1
*/30 *  * * *   avesnin    /usr/lib/collectd/rrd/creategraph.sh all 1day > /dev/null 2>&1
0 *     * * *   avesnin    /usr/lib/collectd/rrd/creategraph.sh all 1week > /dev/null 2>&1
0 */6   * * *   avesnin    /usr/lib/collectd/rrd/creategraph.sh all 1month > /dev/null 2>&1
0 */12  * * *   avesnin    /usr/lib/collectd/rrd/creategraph.sh all 3month > /dev/null 2>&1
0 0     * * *   avesnin    /usr/lib/collectd/rrd/creategraph.sh all 6month > /dev/null 2>&1
0 0     1 * *   avesnin    /usr/lib/collectd/rrd/creategraph.sh all 1year > /dev/null 2>&1

Скачать конфигурационные файлы:

Графики
Графики