Мониторинг системы с помощью RRDtool

  1. Введение
  2. Теория
  3. Установка
  4. Интерфейс командной строки
  5. Мониторинг загрузки процессора
  6. Мониторинг температуры процессора
  7. Мониторинг использования памяти
  8. Заключение

Введение

Думаю, практически каждый системный администратор видел красивые графики, отображающие какие-либо статистические данные за определенный период времени. Одним из примеров таких графиков может служить раздел статистики на этом сайте. Подобные графики можно нарисовать с помощью утилит из пакета RRDtool. RRDtool - набор утилит для работы с кольцевой базой данных (RRD), предназначенных для хранения, обработки и визуализации динамических (изменяющихся во времени) последовательностей данных, таких как сетевой трафик, температура, загрузка процессора и т.п. RRDtool очень легко интегрировать в bash скрипты, также есть привязки в виде библиотек к языкам Perl, Python, Ruby. Все действия я буду проводить на машине с ОС FreeBSD 8.3.

Теория

RRD - это кольцевая база данных. Главная особенность данной БД заключается в том, что она всегда имеет фиксированный размер. То есть когда, будет достигнут конец в каком-нибудь поле в такой БД, то данные будут записываться в самое начало, тем самым, затирая предыдущие. В RRD вместо таблиц, колонок и полей вводятся другие понятия. В ней есть так называемые источники данных (DS), которые сами по себе ничего не хранят. Для простоты их можно интерпретировать как обычные колонки в таблице. Хранение же данных возлагается на кольцевые архивы (RRA). RRA привязывается к источнику данных и обладает такими свойствами как: количество ячеек; применяемая функция консолидации; интервал времени, после которого к данным применяется функция консолидации, и результат ее работы записывается в очередную ячейку. Пожалуй, это все, что требуется знать о структуре данных в RRD.

Установка

На момент написания статьи в портах доступна версия 1.4.5. Итак, идем порты и ставим (обратите внимание на то, что в портах есть несколько версий данного пакета):
  1. # cd /usr/ports/databases/rrdtool
  2. # make install clean
Дополнительно к параметрам сборки по умолчанию я поставил галочку напротив DEJAVU. Замечание - если вы хотите подписывать графики русскими буквами, то необходимо поставить галочку на опцию DEJAVU, иначе у вас вместо русских букв будут кракозябры. К небольшому сожалению порт тянет за собой не мало зависимостей, например библиотеки glib, cairo, x11. После установки порта будет установлен необходимый для работы набор утилит. Все скрипты у меня хранятся в домашней директории пользователя root в папке rrd, причем для каждого отдельного типа статистики создана своя поддиректория. В каждой такой директории есть сама база данных и три скрипта, отвечающие за создание и обновление БД, а так же рисование графиков.
  1. # ls /root/rrd
  2. cpu
  3. mem
  4. temp
  5. # ls -l /root/rrd/mem
  6. -rwxr-x---  1 root  wheel      1204 24 апр 12:36 create.sh
  7. -rwxr-x---  1 root  wheel      1827 24 апр 12:49 graph.sh
  8. -rw-r-----  1 root  wheel  23976200 24 апр 21:15 mem.rrd
  9. -rwxr-x---  1 root  wheel       915 24 апр 00:18 update.sh
Следовать такой иерархии не обязательно, ведь вам может быть удобней хранить все это дело как-то по другому.

Интерфейс командной строки

С пакетом rrdtool ставится несколько утилит и одна из них имеет одноименное название - rrdtool. С помощью нее можно осуществлять различные манипуляции с кольцевой базой данных (создавать/изменять БД, обновлять/извлекать данные, строить графики и т.п.). Утилита имеет следующий синтаксис:
  1. rrdtool [options] command command_options
Далее я опишу некоторые команды (за более подробной информацией обращайтесь в документацию):
  • Создание кольцевой базы данных
    1. rrdtool create filename [--start|-b start time]
    2.         [--step|-s step]
    3.         [--no-overwrite|-O]
    4.         [DS:ds-name:DST:dst arguments]
    5.         [RRA:CF:cf arguments]
    Описание параметров:
    • filename - имя файла.
    • --start, -b - время в секундах, прошедших с начала эпохи Unix (1970-01-01 UTC), с которого происходит сбор статистики. RRDtool не будет принимать данные старее указанного здесь времени. По умолчанию: now - 10 секунд.
    • --step, -s - интервал, через который данные поступают в базу. По умолчанию: 300 секунд.
    • --no-overwrite, -O - не перезаписывать существующий файл с таким же именем.
    • DS:ds-name:DST:dst arguments - источник данных или проще - колонка в БД, в которой хранятся данные.
      • ds-name - имя колонки (не может быть длиннее 19 символов; допустимые символы: [a-zA-Z0-9_]).
      • DST - тип источника данных. Может принимать значения:
        • GAUGE - подходит для данных типа: температуры, загрузки процессора, объема памяти.
        • COUNTER - предназначен для хранения данных с счетчиков, например трафика. Подразумевается, что значение счетчика никогда не уменьшается, за исключением случая его переполнения.
        • DERIVE - счетчик, который может уменьшаться.
        • ABSOLUTE - тоже счетчик, только он сбрасывается после чтения.
        • COMPUTE - позволяет задать rpn-формулу для обработки данных.
      • heartbeat - максимальное кол-во секунд ожидания данных для ячейки, после которого значение помечается как UNKNOWN. Рекомендуется устанавливать в 2*step.
      • min - минимально допустимое значение. Если ограничений нет, то указывайте здесь U.
      • max - максимально допустимое значение. Если ограничений нет, то указывайте здесь U.
    • RRA:CF:xff:steps:rows - параметры кольцевых архивов (RRA). Каждый такой архив может содержать некоторые значения или статистику для DS за определенный период. Прежде чем попасть в архив, данные обрабатываются функцией консолидации (CF). Доступны следующие функции консолидации:
      • AVERAGE - среднее значение за определенный период
      • MIN - минимальное значение
      • MAX - максимальное значение
      • LAST - последнее значение
      xff - определяет долю неопределённых значений в интервале консолидации, при которой консолидированное значение ещё считается определённым (0..1). steps - определяет количество значений, для которых будет использована функция консолидации, после чего результат ее работы записывается в архив. rows - количество ячеек, используемое для хранения консолидированных данных.
  • Занесение данных в базу
    1. rrdtool update filename
    2.         [--template|-t ds-name:ds-name:...]
    3.         [--daemon <address>]
    4.         time|N:value[:value...]
    5.         at-time@value[:value...]
    6.         [ time:value[:value...] ..]
    Описание параметров:
    • filename - имя файла.
    • --template, -t - с помощью этой опции задается шаблон, который позволяет вам указать в какие источники данных и в каком порядке необходимо произвести запись в базу. Если указанных здесь источников данных нет в файле, то запись будет отменена и прога выкинет ошибку.
    • --daemon - если задан, то rrdtool будет пытать подключиться к демону rrdcached. Если попытка будет неудачной, то выдается сообщение об ошибке.
    • time|N:value[:value...] - данные, связанные со временем, которые пишутся в базу. Если во времени указать N, то будет подставлено текущее время. Время так же можно задавать в секундах с начала эпохи Unix или в стиле AT (за подробностями в документацию).
  • Получение данных из базы
    1. rrdtool fetch filename.rrd CF
    2.         [-r|--resolution resolution]
    3.         [-s|--start start] [-e|--end end]
    4.         [--daemon <address>]
    Описание параметров:
    • filename - имя файла.
    • CF - функция консолидации применяемая к данным, которые вы хотите получить.
    • -r, --resolution - требуемый интервал значений.
    • -s, --start - время, с которого выводить данные. По умолчанию: end-1day.
    • -e, --end - время, до которого выводить данные. По умолчанию: now.
    • --daemon - адрес демона rrdcached.
  • Получение информации о структуре базы данных в удобном формате
    1. rrdtool info filename.rrd
  • Изменение размера RRA
    1. rrdtool resize filename rranum GROW|SHRINK rows
    • filename - имя файла.
    • rranum - номер RRA. Номер можно узнать с помощью команды rrdtool info.
    • GROW|SHRINK - действие к RRA: увеличить/уменьшить.
    • rows - количество добавляемых/удаляемых ячеек.
  • Изменение некоторых характеристик базы данных
    1. rrdtool tune filename [options]
    С помощью данной команды можно изменить характеристики базы или отдельно взятого DS (например, переименовать его). За информацией обращайтесь в документацию.
  • Рисование графиков
    1. rrdtool graph filename [options]
    Вот описание некоторых опций (за полным списком обращайтесь в документацию):
    • filename - имя графического файла.
    • -s, --start seconds - время, с которого начать строить график. По умолчанию: end - 1 день.
    • -e, --end seconds - время, до которого строить график. По умолчанию: now.
    • -o, --logarithmic - логарифмическое масштабирование оси ординат.
    • -z, --lazy - генерировать новый графический файл, если предыдущий устарел или его не существует.
    • -g, --no-legend - не выводить описания графиков.
    • --legend-position=(north|south|west|east) - позиция для вывода описания графиков. По умолчанию: south.
    • --legend-direction=(topdown|bottomup) - выводить описание графиков по вертикали, с указанным здесь направлением.
    • --daemon - адрес демона rrdcached.
    • -j, --only-graph - вывести только график, без какого-либо текста.
    • -T, --tabwidth width - ширина табуляции в пикселях. По умолчанию: 40.
    • -E, --slope-mode - может немного улучшить внешний вид картинки.
    • -a, --imgformat PNG|SVG|EPS|PDF - формат графического файла.
    • -t, --title string - заголовок, помещаемый вверху графика.
    • -v, --vertical-label - вертикальный текст, слева от графика.
    • -w, --width - длина картинки в пикселях. По умолчанию: 400.
    • -h, --height - высота картинки в пикселях. По умолчанию: 100.
    • DEF:vname=rrd:ds-name:CF - данный параметр позволяет выдернуть данные из определенного источника данных (а точнее из кольцевого архива, связанного с ним) в базе и использоваит эти данные для построения графика.
      • vname=rrd - vname - имя переменной, через которую можно обращаться к данным, а rrd - путь до базы данных.
      • ds-name - имя источника данных в БД.
      • CF - функция консолидации, чтобы тулза знала из какого RRA выдергивать данные.
    • CDEF:vname=rpn-expression - позволяет задать новую переменную, которая будет содержать результат математического выражения, записанного в обратной польской нотации.
    • VDEF:vdefname=rpn-expression - позволяет задать переменную, которая будет содержать результат применяемой функции к данным. Функции могут быть такими: AVERAGE, MAXIMUM, MINIMUM, LAST.
    • PRINT:vdefname:format - выводит строку в раздел описания графика(ов). Как правильно оформить поле format смотрите в документации.
    • GPRINT:vdefname:format - работает так же, как и PRINT.
    • COMMENT:text - выводит обычный текст.
    • TEXTALIGN:{left|right|justified|center} - выравнивание текста в разделе описания графиков.
    • LINE[width]:vname[#rrggbb[aa][:[legend][:STACK]]] - рисует линию графика, определенной толщины (width) и для определенной переменной (vname). Можно задать цвет и описание (которое будет выводится в разделе описания графиков). Ключевое слово STACK значит, что данная линия будет рисоваться над предыдущей LINE или AREA.
    • AREA:vname[#rrggbb[aa][:[legend][:STACK]]] - данная опция аналогична LINE, только все параметры задаются для внутренней области графика (то есть для того, что под LINE)

Мониторинг загрузки процессора

Вот и настало время для первого практического примера )). Перед созданием БД сначала необходимо продумать какие данные будут храниться в ней. В большинстве Unix-подобных систем процессор может находиться в 5 состояниях (user, system, nice, intr, idle) и FreeBSD не исключение. Я хочу хранить каждое состояние отдельно, а если необходимо будет что-нибудь объединить, то простыми арифметическими действиями эту задачу очень просто решить. Итак, нам необходима база данных с 5 DS (источниками данных). Теперь нужно решить какие RRA понадобятся и за какой интервал времени одна ячейка будет хранить данные (чем меньше интервал, тем точнее будет график). Для построения более точного графика данные будут сниматься каждую минуту и будет несколько RRA (забыл сказать - данные будут храниться за год):
  1. Каждая ячейка хранит данные за каждый отсчет (то есть запись идет каждые 60 секунд). Чтобы хранить столько данных понадобится 365*24*60*60/60=525600 ячеек.
  2. Каждая ячейка хранит данные за 15 минут (то есть данные будут накапливаться в течении 15 минут, далее к ним применяется функция консолидации и только тогда происходит запись). Чтобы хранить столько данных понадобится 365*24*60*60/(60*15)=35040 ячеек.
  3. Каждая ячейка хранит данные за 30 минут. Чтобы хранить столько данных понадобится 365*24*60*60/(60*30)=17520 ячеек.
  4. Каждая ячейка хранит данные за 60 минут. Чтобы хранить столько данных понадобится 365*24*60*60/(60*60)=8760 ячеек.
P.S. На данный момент я использую только 1 RRA, остальные сделал на всякий случай, вдруг понадобятся.
Так же будут RRA с разными функциями консолидации, поэтому придется продублировать RRA для каждого интервала времени. Чтобы не набивать каждый раз все руками (а набивать много), лучше сразу писать скрипт. Вот так выглядит скрипт для создания требуемой БД:
  1. # cat /root/rrd/cpu/create.sh
  2. #!/bin/sh
  3.  
  4. RRD_BASE="/root/rrd/cpu/cpu.rrd"
  5.  
  6. # ds0 - user
  7. # ds1 - nice
  8. # ds2 - system
  9. # ds3 - intr
  10. # ds4 - idle
  11.  
  12. # Статистика хранится за год
  13. # А так же хранится среднее за 15,30,60 минут
  14.  
  15. rrdtool create $RRD_BASE --step 60 \
  16.                          DS:ds0:GAUGE:120:0:100 \
  17.                          DS:ds1:GAUGE:120:0:100 \
  18.                          DS:ds2:GAUGE:120:0:100 \
  19.                          DS:ds3:GAUGE:120:0:100 \
  20.                          DS:ds4:GAUGE:120:0:100 \
  21.                          RRA:AVERAGE:0.5:1:525600 \
  22.                          RRA:AVERAGE:0.5:15:35040 \
  23.                          RRA:AVERAGE:0.5:30:17520 \
  24.                          RRA:AVERAGE:0.5:60:8760 \
  25.                          RRA:MIN:0.5:1:525600 \
  26.                          RRA:MIN:0.5:15:35040 \
  27.                          RRA:MIN:0.5:30:17520 \
  28.                          RRA:MIN:0.5:60:8760 \
  29.                          RRA:MAX:0.5:1:525600 \
  30.                          RRA:MAX:0.5:15:35040 \
  31.                          RRA:MAX:0.5:30:17520 \
  32.                          RRA:MAX:0.5:60:8760
  33.  
  34. exit 0
Теперь нужно написать скрипт, который будет собирать данные, и наполнять базу. К сожалению, во FreeBSD нет стандартной утилиты, которая бы выводила загрузку процессора на экран в удобном для парсинга виде. Можно конечно снимать загрузку по SNMP, но устанавливать и настраивать дополнительный софт мне не охото (да и ни к чему это, потому что можно поступить проще). Поэтому пришлось написать свою программу для решения данной проблемы. Вот ее код:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <sys/param.h>
  5. #include <sys/resource.h>
  6. #include <sys/sysctl.h>
  7. #include <kvm.h>
  8. #include <fcntl.h>
  9.  
  10. static struct {
  11.     const char *name;
  12. } cpu_states[CPUSTATES] = {
  13.     "user", "nice", "system", "intr", "idle"
  14. };
  15.  
  16. static void error_log(const char *msg);
  17.  
  18. int main()
  19. {
  20.   int i;
  21.   kvm_t *km = NULL;
  22.   long cpu_load[CPUSTATES];
  23.   long cpu_load2[CPUSTATES];
  24.   long long total;
  25.  
  26.   km = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL);
  27.   if (km == NULL) error_log("kvm_open");
  28.  
  29.   if (kvm_getcptime(km, cpu_load) == -1){
  30.       kvm_close(km);
  31.       error_log("kvm_getcptime");
  32.   }
  33.   usleep(1000000);
  34.   if (kvm_getcptime(km, cpu_load2) == -1){
  35.       kvm_close(km);
  36.       error_log("kvm_getcptime");
  37.   }
  38.   kvm_close(km);
  39.  
  40.   total = 0;
  41.   for (i = 0; i < CPUSTATES; i++)
  42.       total += cpu_load2[i] - cpu_load[i];
  43.  
  44.   for (i = 0; i < CPUSTATES; i++)
  45.       printf("%s\t", cpu_states[i]);
  46.   putchar('\n');
  47.  
  48.   for (i = 0; i < CPUSTATES; i++)
  49.       printf("%.2f\t", (cpu_load2[i]-cpu_load[i])*100.0/total);
  50.   putchar('\n');
  51.  
  52.   return 0;
  53. }
  54.  
  55. static void error_log(const char *msg)
  56. {
  57.   printf("error: %s\n", msg);
  58.   exit(EXIT_FAILURE);
  59. }
Скомпилировать ее можно такой командой:
  1. gcc -o cpuload main.c -lkvm
Самое время перейти к написанию скрипта для сбора статистики. У меня он выглядит так:
  1. # cat /root/rrd/cpu/update.sh
  2. #!/bin/sh
  3.  
  4. RRD_BASE="/root/rrd/cpu/cpu.rrd"
  5.  
  6. str=`/root/rrd/cpu/cpuload | tail +2`
  7. cpu_user=`echo $str | awk '{ print $1; }'`
  8. cpu_nice=`echo $str | awk '{ print $2; }'`
  9. cpu_sys=`echo $str | awk '{ print $3; }'`
  10. cpu_intr=`echo $str | awk '{ print $4; }'`
  11. cpu_idle=`echo $str | awk '{ print $5; }'`
  12.  
  13. rrdtool update $RRD_BASE N:$cpu_user:$cpu_nice:$cpu_sys:$cpu_intr:$cpu_idle
  14.  
  15. exit 0
Графики я рисую таким скриптом:
  1. # cat /root/rrd/cpu/graph.sh
  2. #!/bin/sh
  3.  
  4. RRD_BASE="/root/rrd/cpu/cpu.rrd"
  5. DAYS="6h 1d 7d 30d 1y"
  6. DATE="`date '+%d-%m-%Y %H\:%M\:%S'`"
  7.  
  8. for day in $DAYS; do
  9.  
  10. rrdtool graph "/home/user/data/www/info-x.org/stats/cpu_${day}.png" \
  11.               --start end-${day} --slope-mode \
  12.               --title "Статистика использования процессора" \
  13.               --width 500 --height 200 \
  14.               --imgformat PNG \
  15.               DEF:cpu_user=$RRD_BASE:ds0:AVERAGE:step=1 \
  16.               DEF:cpu_nice=$RRD_BASE:ds1:AVERAGE:step=1 \
  17.               DEF:cpu_sys=$RRD_BASE:ds2:AVERAGE:step=1 \
  18.               DEF:cu_max=$RRD_BASE:ds0:MAX:step=1 \
  19.               DEF:cu_min=$RRD_BASE:ds0:MAX:step=1 \
  20.               DEF:cn_max=$RRD_BASE:ds1:MAX:step=1 \
  21.               DEF:cn_min=$RRD_BASE:ds1:MAX:step=1 \
  22.               DEF:cs_max=$RRD_BASE:ds2:MAX:step=1 \
  23.               DEF:cs_min=$RRD_BASE:ds2:MAX:step=1 \
  24.               TEXTALIGN:left \
  25.               COMMENT:"                                    " \
  26.               COMMENT:"Максимальная  " \
  27.               COMMENT:"Минимальная  " \
  28.               COMMENT:"Средняя\l" \
  29.               LINE2:cpu_user#FF3333:"Процессы пользователей\:" \
  30.               GPRINT:cu_max:MAX:"%22.2lf%%" \
  31.               GPRINT:cu_min:MIN:"%12.2lf%%" \
  32.               GPRINT:cpu_user:AVERAGE:"%8.2lf%%\l" \
  33.               AREA:cpu_user#FF6666 \
  34.               LINE2:cpu_sys#3366FF:"Процессы ядра\:" \
  35.               GPRINT:cs_max:MAX:"%31.2lf%%" \
  36.               GPRINT:cs_min:MIN:"%12.2lf%%" \
  37.               GPRINT:cpu_sys:AVERAGE:"%8.2lf%%\l" \
  38.               AREA:cpu_sys#6699FF \
  39.               LINE1:cpu_nice#48D1CC:"Процессы с измененным приоритетом\:" \
  40.               GPRINT:cn_max:MAX:"%11.2lf%%" \
  41.               GPRINT:cn_min:MIN:"%12.2lf%%" \
  42.               GPRINT:cpu_nice:AVERAGE:"%8.2lf%%\l" \
  43.               COMMENT:"\s" \
  44.               COMMENT:"\s" \
  45.               COMMENT:"Последнее обновление\: $DATE\r" \
  46.               > /dev/null 2>&1
  47.  
  48. done
  49.  
  50. exit 0
Ну и последним действием, все это дело нужно разместить в кроне:
  1. SHELL=/bin/sh
  2. PATH="/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin"
  3. #min   hour   day   month  day of week   command
  4. */1    *      *     *      *             /root/rrd/cpu/update.sh
  5. 0      */6    *     *      *             /root/rrd/cpu/graph.sh

Мониторинг температуры процессора

Температуру процессора можно снять несколькими способами. Если у вас материнская плата от ASUS, то можно загрузить модуль acpi_aiboost.ko, который позволит получить доступ к сенсорам материнской платы. Можно пойти другим путем - в современных процессорах есть собственные датчики температуры, доступ к ним можно получить, загрузив модуль amdtemp.ko для AMD или coretemp.ko для Intel. Доступ к данным можно получить через системное дерево MIB (sysctl). У меня стоит проц от AMD, поэтому я подгрузил соответствующий модуль для данного процессора, который в MIB добавил следующие элементы:
  • dev.amdtemp.0.sensor0.core0
  • dev.amdtemp.0.sensor0.core1
  • dev.amdtemp.0.sensor1.core0
  • dev.amdtemp.0.sensor1.core1
Соответственно я сделал 4 DS в базе данных, ну и несколько RRA к ним в таком же стиле, как в предыдущем разделе. Статистика будет храниться за месяц. Так как большой точности мне здесь не нужно, то данные будут сниматься раз в пять минут. Вот скрипт для создания базы:
  1. # cat /root/rrd/temp/create.sh
  2. #!/bin/sh
  3.  
  4. RRD_BASE="/root/rrd/temp/temp.rrd"
  5.  
  6. # Статистика хранится за месяц
  7.  
  8. # ds0 - sensor 0, core 0
  9. # ds1 - sensor 0, core 1
  10. # ds2 - sensor 1, core 0
  11. # ds3 - sensor 1, core 1
  12.  
  13. # Усредненные значения за 15,30,60 минут
  14. rrdtool create $RRD_BASE --step 300 \
  15.                           DS:ds0:GAUGE:600:U:U \
  16.                           DS:ds1:GAUGE:600:U:U \
  17.                           DS:ds2:GAUGE:600:U:U \
  18.                           DS:ds3:GAUGE:600:U:U \
  19.                           RRA:AVERAGE:0.5:1:8640 \
  20.                           RRA:AVERAGE:0.5:3:2880 \
  21.                           RRA:AVERAGE:0.5:6:1440 \
  22.                           RRA:AVERAGE:0.5:12:720 \
  23.                           RRA:MIN:0.5:1:8640 \
  24.                           RRA:MIN:0.5:3:2880 \
  25.                           RRA:MIN:0.5:6:1440 \
  26.                           RRA:MIN:0.5:12:720 \
  27.                           RRA:MAX:0.5:1:8640 \
  28.                           RRA:MAX:0.5:3:2880 \
  29.                           RRA:MAX:0.5:6:1440 \
  30.                           RRA:MAX:0.5:12:720
  31.  
  32. exit 0
Скрипт для съема и занесения данных в базу:
  1. # cat /root/rrd/temp/update.sh
  2. #!/bin/sh
  3.  
  4. RRD_BASE="/root/rrd/temp/temp.rrd"
  5.  
  6. # dev.amdtemp.0.sensor0.core0
  7. # dev.amdtemp.0.sensor0.core1
  8. # dev.amdtemp.0.sensor1.core0
  9. # dev.amdtemp.0.sensor1.core1
  10.  
  11. sc00=`sysctl -n dev.amdtemp.0.sensor0.core0 | awk '{ print substr($1, 1, index($1, "C")-1); }' | sed 's/,/\./'`
  12. sc01=`sysctl -n dev.amdtemp.0.sensor0.core1 | awk '{ print substr($1, 1, index($1, "C")-1); }' | sed 's/,/\./'`
  13. sc10=`sysctl -n dev.amdtemp.0.sensor1.core0 | awk '{ print substr($1, 1, index($1, "C")-1); }' | sed 's/,/\./'`
  14. sc11=`sysctl -n dev.amdtemp.0.sensor1.core1 | awk '{ print substr($1, 1, index($1, "C")-1); }' | sed 's/,/\./'`
  15.  
  16. rrdtool update $RRD_BASE N:$sc00:$sc01:$sc10:$sc11
  17.  
  18. exit 0
Скрипт для рисования графиков:
  1. # cat /root/rrd/temp/graph.sh
  2. #!/bin/sh
  3.  
  4. RRD_BASE="/root/rrd/temp/temp.rrd"
  5. DAYS="1d"
  6. DATE="`date '+%d-%m-%Y %H\:%M\:%S'`"
  7.  
  8. for day in $DAYS; do
  9.  
  10. rrdtool graph "/home/user/data/www/info-x.org/stats/temp_${day}.png" \
  11.               --start end-${day} --slope-mode \
  12.               --title "CPU temperature (C)" \
  13.               --width 500 --height 200 \
  14.               --imgformat PNG \
  15.               DEF:sc00=$RRD_BASE:ds0:AVERAGE:step=1 \
  16.               DEF:sc01=$RRD_BASE:ds1:AVERAGE:step=1 \
  17.               DEF:sc10=$RRD_BASE:ds2:AVERAGE:step=1 \
  18.               DEF:sc11=$RRD_BASE:ds3:AVERAGE:step=1 \
  19.               DEF:sc00_max=$RRD_BASE:ds0:MAX:step=1 \
  20.               DEF:sc01_max=$RRD_BASE:ds1:MAX:step=1 \
  21.               DEF:sc10_max=$RRD_BASE:ds2:MAX:step=1 \
  22.               DEF:sc11_max=$RRD_BASE:ds3:MAX:step=1 \
  23.               DEF:sc00_min=$RRD_BASE:ds0:MIN:step=1 \
  24.               DEF:sc01_min=$RRD_BASE:ds1:MIN:step=1 \
  25.               DEF:sc10_min=$RRD_BASE:ds2:MIN:step=1 \
  26.               DEF:sc11_min=$RRD_BASE:ds3:MIN:step=1 \
  27.               TEXTALIGN:center \
  28.               LINE1:sc00#00FF00:"Core 0 Sensor 0\t" \
  29.               GPRINT:sc00_max:MAX:"Max\: %1.0lf\t" \
  30.               GPRINT:sc00_min:MIN:"Min\: %1.0lf\t" \
  31.               GPRINT:sc00:AVERAGE:"Avg\: %1.0lf\l" \
  32.               LINE1:sc01#0000FF:"Core 0 Sensor 1\t" \
  33.               GPRINT:sc01_max:MAX:"Max\: %1.0lf\t" \
  34.               GPRINT:sc01_min:MIN:"Min\: %1.0lf\t" \
  35.               GPRINT:sc01:AVERAGE:"Avg\: %1.0lf\l" \
  36.               LINE1:sc10#FF0000:"Core 1 Sensor 0\t" \
  37.               GPRINT:sc10_max:MAX:"Max\: %1.0lf\t" \
  38.               GPRINT:sc10_min:MIN:"Min\: %1.0lf\t" \
  39.               GPRINT:sc10:AVERAGE:"Avg\: %1.0lf\l" \
  40.               LINE1:sc11#000000:"Core 1 Sensor 1\t" \
  41.               GPRINT:sc11_max:MAX:"Max\: %1.0lf\t" \
  42.               GPRINT:sc11_min:MIN:"Min\: %1.0lf\t" \
  43.               GPRINT:sc11:AVERAGE:"Avg\: %1.0lf\l" \
  44.               COMMENT:"\r" \
  45.               COMMENT:"Last updated\: $DATE\l" \
  46.               > /dev/null 2>&1
  47.  
  48. done
  49.  
  50. exit 0
Ну и в крон я добавил такие строки:
  1. */5    *      *     *      *             /root/rrd/temp/update.sh
  2. 0      */6    *     *      *             /root/rrd/temp/graph.sh

Мониторинг использования памяти

Для мониторинга памяти проще всего использовать данные из системного дерева MIB. В MIB можно найти данные о свободных/используемых страницах памяти. Если взять эти значения и умножить на размер страницы, то мы получим объем памяти в байтах. Для определения объема свободного/занятого свопа я распарсил вывод команды swapinfo.
  • vm.stats.vm.v_page_size - размер страницы в байтах
  • vm.stats.vm.v_free_count - объем свободной памяти в страницах
  • vm.stats.vm.v_inactive_count - объем неактивной памяти в страницах
  • vm.stats.vm.v_active_count - объем активной памяти в страницах
  • vm.stats.vm.v_wire_count - объем связанной памяти в страницах
  • vm.stats.vm.v_cache_count - объем кэшированной памяти в страницах
Для хранения всех данных понадобится 6 DS, ну и соответственно несколько RRA к ним. Снимать данные я буду каждые 5 минут. Статистика хранится в течении года. Скрипт для создания базы данных у меня выглядит так:
  1. # cat /root/rrd/mem/create.sh
  2. #!/bin/sh
  3.  
  4. RRD_BASE="/root/rrd/mem/mem.rrd"
  5.  
  6. # Статистика хранится за месяц
  7.  
  8. # ds0 - свободная память
  9. # ds1 - неактивная память
  10. # ds2 - активная память
  11. # ds3 - wired память
  12. # ds4 - кэшированая память
  13. # ds5 - использование свопа
  14.  
  15. # Усредненные значения за 15,30,60 минут
  16. rrdtool create $RRD_BASE --step 300 \
  17.                           DS:ds0:GAUGE:600:U:U \
  18.                           DS:ds1:GAUGE:600:U:U \
  19.                           DS:ds2:GAUGE:600:U:U \
  20.                           DS:ds3:GAUGE:600:U:U \
  21.                           DS:ds4:GAUGE:600:U:U \
  22.                           DS:ds5:GAUGE:600:U:U \
  23.                           RRA:AVERAGE:0.5:1:105120 \
  24.                           RRA:AVERAGE:0.5:3:35040 \
  25.                           RRA:AVERAGE:0.5:6:17520 \
  26.                           RRA:AVERAGE:0.5:12:8760 \
  27.                           RRA:MIN:0.5:1:105120 \
  28.                           RRA:MIN:0.5:3:35040 \
  29.                           RRA:MIN:0.5:6:17520 \
  30.                           RRA:MIN:0.5:12:8760 \
  31.                           RRA:MAX:0.5:1:105120 \
  32.                           RRA:MAX:0.5:3:35040 \
  33.                           RRA:MAX:0.5:6:17520 \
  34.                           RRA:MAX:0.5:12:8760
  35.  
  36. exit 0
Скрипт для съема и занесения данных в базу:
  1. # cat /root/rrd/mem/update.sh
  2. #!/bin/sh
  3.  
  4. RRD_BASE="/root/rrd/mem/mem.rrd"
  5.  
  6. page_size=`sysctl -n vm.stats.vm.v_page_size`
  7. mem_free=`sysctl -n vm.stats.vm.v_free_count`
  8. mem_inact=`sysctl -n vm.stats.vm.v_inactive_count`
  9. mem_use=`sysctl -n vm.stats.vm.v_active_count`
  10. mem_wired=`sysctl -n vm.stats.vm.v_wire_count`
  11. mem_cached=`sysctl -n vm.stats.vm.v_cache_count`
  12. swap_use=`swapinfo | tail +2 | awk '{ print $3; }'`
  13.  
  14. mem_free=$(($mem_free * $page_size))
  15. mem_inact=$(($mem_inact * $page_size))
  16. mem_use=$(($mem_use * $page_size))
  17. mem_wired=$(($mem_wired * $page_size))
  18. mem_cached=$(($mem_cached * $page_size))
  19.  
  20. rrdtool update $RRD_BASE N:$mem_free:$mem_inact:$mem_use:$mem_wired:$mem_cached:$swap_use
  21.  
  22. exit 0
График я рисую так:
  1. # cat /root/rrd/mem/graph.sh
  2. #!/bin/sh
  3.  
  4. RRD_BASE="/root/rrd/mem/mem.rrd"
  5. DAYS="1d 7d 30d 1y"
  6. DATE="`date '+%d-%m-%Y %H\:%M\:%S'`"
  7.  
  8. for day in $DAYS; do
  9.  
  10. rrdtool graph "/home/user/data/www/info-x.org/stats/mem_${day}.png" \
  11.               --start end-${day} --slope-mode --base 1024 \
  12.               --title "Статистика использования памяти" \
  13.               --width 500 --height 200 \
  14.               --imgformat PNG \
  15.               DEF:mem_free=$RRD_BASE:ds0:AVERAGE:step=1 \
  16.               DEF:mem_inact=$RRD_BASE:ds1:AVERAGE:step=1 \
  17.               DEF:mem_use=$RRD_BASE:ds2:AVERAGE:step=1 \
  18.               DEF:mem_wired=$RRD_BASE:ds3:AVERAGE:step=1 \
  19.               DEF:mem_cached=$RRD_BASE:ds4:AVERAGE:step=1 \
  20.               DEF:swap_use=$RRD_BASE:ds5:AVERAGE:step=1 \
  21.               CDEF:mem_free_mb=mem_free,1048576,/ \
  22.               CDEF:mem_inact_mb=mem_inact,1048576,/ \
  23.               CDEF:total_use=mem_use,mem_wired,+ \
  24.               CDEF:total_use_mb=total_use,1048576,/ \
  25.               CDEF:mem_cached_mb=mem_cached,1048576,/ \
  26.               CDEF:su_mb=swap_use,1024,/ \
  27.               VDEF:mem_free_max=mem_free_mb,MAXIMUM \
  28.               VDEF:mem_free_min=mem_free_mb,MINIMUM \
  29.               VDEF:mem_inact_max=mem_inact_mb,MAXIMUM \
  30.               VDEF:mem_inact_min=mem_inact_mb,MINIMUM \
  31.               VDEF:tu_max=total_use_mb,MAXIMUM \
  32.               VDEF:tu_min=total_use_mb,MINIMUM \
  33.               VDEF:mem_cached_max=mem_cached_mb,MAXIMUM \
  34.               VDEF:mem_cached_min=mem_cached_mb,MINIMUM \
  35.               VDEF:su_max_mb=su_mb,MAXIMUM \
  36.               VDEF:su_min_mb=su_mb,MINIMUM \
  37.               TEXTALIGN:center \
  38.               LINE2:total_use#00FF00:"Используемая память (active + wired)\l" \
  39.               AREA:total_use#00CC00 \
  40.               LINE1:swap_use#0000FF:"Использование раздела подкачки\l" \
  41.               AREA:swap_use#0000CC \
  42.               LINE1:mem_free#FF0000:"Свободная память (free)\l" \
  43.               LINE1:mem_inact#FFCC00:"Неактивная память (inactive)\l" \
  44.               LINE1:mem_cached#FF00CC:"Кешированная память (cache)" \
  45.               COMMENT:"\n" \
  46.               COMMENT:" \n" \
  47.               COMMENT:"               " \
  48.               COMMENT:"Максимум          " \
  49.               COMMENT:"Минимум          " \
  50.               COMMENT:"В среднем\l" \
  51.               COMMENT:"Используемая\:" \
  52.               GPRINT:tu_max:"%10.0lf Мб" \
  53.               GPRINT:tu_min:"%14.0lf Мб" \
  54.               GPRINT:total_use_mb:AVERAGE:"%16.0lf Мб\l" \
  55.               COMMENT:"Подкачка\:" \
  56.               GPRINT:su_max_mb:"%14.0lf Мб" \
  57.               GPRINT:su_min_mb:"%14.0lf Мб" \
  58.               GPRINT:su_mb:AVERAGE:"%16.0lf Мб\l" \
  59.               COMMENT:"Свободная\:" \
  60.               GPRINT:mem_free_max:"%13.0lf Мб" \
  61.               GPRINT:mem_free_min:"%14.0lf Мб" \
  62.               GPRINT:mem_free_mb:AVERAGE:"%16.0lf Мб\l" \
  63.               COMMENT:"Неактивная\:" \
  64.               GPRINT:mem_inact_max:"%12.0lf Мб" \
  65.               GPRINT:mem_inact_min:"%14.0lf Мб" \
  66.               GPRINT:mem_inact_mb:AVERAGE:"%16.0lf Мб\l" \
  67.               COMMENT:"Кешированная\:" \
  68.               GPRINT:mem_cached_max:"%10.0lf Мб" \
  69.               GPRINT:mem_cached_min:"%14.0lf Мб" \
  70.               GPRINT:mem_cached_mb:AVERAGE:"%16.0lf Мб\l" \
  71.               COMMENT:"\r" \
  72.               COMMENT:"Последнее обновление\: $DATE\r" \
  73.               > /dev/null 2>&1
  74.  
  75. done
  76.  
  77. exit 0
Ну и в кроне у меня есть такие строки:
  1. */5    *      *     *      *             /root/rrd/mem/update.sh
  2. 0      */6    *     *      *             /root/rrd/mem/graph.sh

Заключение

Надеюсь, в этой статье мне удалось достаточно подробно рассказать о том, как можно использовать RRDtool для мониторинга системы. Правда такой метод подходит, когда нужно мониторить всего одну или две машины. Для отслеживания работы парка машин лучше использовать специально разработанные программные комплексы (например, Cacti, Nagios, Zabbix и т.п.). Так же стоит отметить, что некоторые программы для мониторинга системы (например, Cacti) используют RDDtool для хранения данных и построения графиков на основе этих данных. Такие программы просто используют готовые шаблоны для визуализации данных, и в таком случае данная статья будет полезна, так как знать принцип работы RRDtool не помешает.

Комментарии

Аватар пользователя Владимир
Владимир
Russian Federation (Novosibirsk)Widnows XPGoogle Chrome 49.0.2623.112
чт, 2019-07-11 11:47

Нужна помощь в разборе параметров строки CDEF.

Аватар пользователя Гость
Гость
Russian Federation (Yekaterinburg)UnknownMozilla Firefox 68.0
чт, 2019-07-11 20:24

Что конкретно не получается?

Добавить комментарий

Filtered text

CAPTCHA
Этот вопрос предназначен для предотвращения автоматизированной обработки форм.
Fill in the blank.