Мониторинг сети при помощи rrdtool

Vsevolod Stakhov


Мониторинг сети и оборудования входит в одну из основных обязанностей администратора. Например, постоянное слежение способно выявить узкие места сети, показать рост ее загрузки, увидеть расход аппаратных ресурсов на серверах, вовремя обнаружит атаку и многое другое. Мониторинг можно делать самыми разнообразными способами, но, наверное, самым удобным является графическое представление данных, полученных от различных счетчиков. В даной статье речь пойдет об одной из наиболее грамотно организованных программ для выполнения таких действий, а именно о rrdtool (http://www.rrdtool.com/). Rrdtool была разработана в качестве альтернативы mrtg (написанной, кстати, тем же автором). Основной целью автора было ускорение работы и добавление расширенных возможностей по построению графиков, а также устойчивость к сбоям. Rrdtool является очень "чистой" в плане понимания логики работы, но за это пришлось поплатиться отсутствием внешнего файла конфигурации. Этот недостаток пыталсиь исправить во многих fron-end'ах к rrdtool (http://www.rrdtool.com/rrdworld/), но, на мой взгляд, иногда проще написать грамотный скрипт, чем ковыряться со множеством настроек в запутанном конфигурационном файле.Тем более, что для сбора данных с различных счетчиков выполняются практически аналогичные действия.

Итак, рассмотрим общую логику работы rrdtool. Вначале мы создаем так называемую round-robin database (кольцевая или закольцованная база), в которую можно записывать показания счетчиков. Почему round-robin - т.к. в базе хранится только определенное количество ячеек данных, иначе постоянно увеличивающееся количество поступающих данных просто напросто переполнит файл данных лишней, устаревшей информацией. Возникает закономерный вопрос - как сделать график, например, за год работы? Было выбрано очень грамотное решение - записывать в одну ячейку данных несколько показателей, производя над ними некую статистическую функцию. В каждую round-robin базу данных можно записывать несколько рядов ячеек, принимающих показатели из одного источника, но по-иному комбинируя поступающие данные. Ну а график строится банальным извлечением данных из базы за определенный период. Как все это выглядит на практике? В первую очередь нужно решить, за какими данными мы будем следить. Естественно, на первый план выходит контроль трафика марщрутизаторов, коммутаторов, нагрузка аппаратной части серверов (загрузка ЦП, использование жестких дисков, контроль работы сетевых интерфейсов, объем свободной памяти и прочее), нагрузка на сетевые сервисы, такие как http, ftp, proxy сервера. И первый вопрос, который встает перед нами, - как собирать статистическую информацию. Наиболее общим решением является использование протокола snmp, но это не всегда является приемлемым для некоторых целей, например, статистики http сервера. Тогда на помощь приходят различные утилиты анализаии log-файлов, счетчики программных firewall'ов. Если используется snmp, то для снятия статистики достаточно иметь программы, вроде snmpget и документацию по OID'ам соответствующего оборудования (например, по оборудованию cisco всегда можно найти список поддерживаемых OID'ов для конкретного устройства). В противном случае приходится вручную задавать команду для анализа необходимых параметров. Главные критерии в выборе метода - скорость, доступность и надежность. Конечно, необходимо учесть, что ценность статистической информации в ее постоянном контроле, то есть если взглянуть на состояние системы в данный момент, то вряд ли можно понять, справляется ли она с нагрузкой, увидеть рост нагрузки и выявить момент, когда что-либо будет нуждаться в замене или усовершенствовании. Определившись с источником информации, можно приступить к написанию скрипта для rrdtool. Для начала некоторые сведения о структуре скриптов. Изначально создаются 2 или три скрипта, один из которых выполняется однократно, создавая round-robin базы данных при помощи rrdtool create. Далее необходимо переодически запускать скрипт обновления round-robin databases (далее rrdb), который снимает показания с различных сенсоров и передает их в rrdb при помощи rrdtool-update, кроме того в этот скрипт можно включить генерацию графиков (rrdtool graph). В некоторых случаях можно вынести генерацию графиков в cgi скрипт, тогда каждый раз при вызове cgi скрипта будут генерироваться графики. Второй вариант подходит при использовании mod_expire (модуль apache, позволяющий указать периоды устаревания статических данных, таких как рисунки) и является весьма практичным, т.к. графики обновляются тогда, когда запрашиваются данные, и только в том случае, если старые данные устарели (очень большое преимущество по сравнению c mrtg). При создании скрипта обновления rrdb необходимо учесть, что вызываться он будет весьма часто и при большом количестве собираемых данных может сильно "тормозить" систему. Для таких языков, как perl и python написаны соответствующие библиотеки для доступа к rrdb, поэтому часто целесообразно использовать именно их. Однако, в данной статье я буду ориентироваться все же на shell скрипты, т.к. наиболее ресурсоемким является вызов внешних программ сбора данных, таких как snmpget.

Установка rrdtool проблем вызывать не должна - изучаем http://www.rrdtool.com/download.html и выбираем нужную версию в тербуемом формате (для FreeBSD, например, лучше всего использовать систему портов). Rrdtool содержит также несколько сторонних библиотек, таких как libgd (для генерации графики), libpng (для генерации png изображений), а также zlib (для компрессии изображений). Установка проблем не вызывает, только нужно указать в аргументах к configure --enable-shared=yes для создания разделяемых библиотек rrdtool для дальнейшего их использования другими программами.

Итак, для начала опишу применение утилит из пакета rrdtool. Для создания rrdb используется команда rrdtool create, имеющая следующий синтаксис:

rrdtool create filename.rrd --step длительность_шага DS:имя_источника_данных:тип_источника:интервал_определенности:min:max RRA:функция_консолидации:достоверность:отсчетов_на_ячейку:число_ячеек

Думаю, необходимы некоторые пояснения. Параметр filename.rrd указывает имя файла rrdb (может быть абсолютным или относительным, что, конечно же, решается на этапе подстановки самой shell), --step длительность_шага - задает число секунд между предполагаемыми обновлениями rrdb, далее следуют определения источников данных (DS). Строки DS описывают источники данных, которые будут поступать в rrdb, параметры DS-line означают следующее:

Приведу пример создания rrdb:

rrdtool create /usr/local/var/rrdtool/myrouter.rrd --step 300        \
            DS:input:COUNTER:600:U:U			\
            DS:output:COUNTER:600:U:U			\
            RRA:AVERAGE:0.5:1:600			\
            RRA:AVERAGE:0.5:6:700			\
            RRA:AVERAGE:0.5:24:775			\
            RRA:AVERAGE:0.5:288:797			\
            RRA:MAX:0.5:1:600				\
            RRA:MAX:0.5:6:700				\
            RRA:MAX:0.5:24:775				\
            RRA:MAX:0.5:288:797				\

Данная команда создает rrdb myrouter.rrd для снятия статистики с сетевого интерфейса маршрутизатора, предполагая, что данные в нее будут поступать раз в 5-ть минут, задаются два источника данных: счетчик принятых и отправленных байт, при этом счетчик не имеет ограничений ни сверху, ни снизу. Создаются также 8-мь рядов, четыре из которых используют для консолидации данных функцию AVERAGE, а остальные - MAX. При этом создаются следующие промежутки: 600 ячеек по 5-ть минут, 700 ячеек по 30 минут (5*6), 775 ячеек по два часа (5*24), а также 797 ячеек, хранящих статистику отсчетов за день. В данном случае имитируется поведение mrtg, обрабатывающей такие же временные промежутки.

Для внесения данных со счетчиков в rrdb используется команда rrdtool update, имеющая достаточно простой формат:

rrdtool update имя_rrdb.rrd время:значение_DS1[:значение_DS2[:...]]
параметр "время" может быть заменен символом 'N', что означает текущее время, также допускается задавать время в unix формате (вывод команды date -u +%s) или в формате at(1). Хотя можно не заморачиваться этими проблемами и просто использовать значение 'N'. Значения DS указываются в том порядке, в котором они были определены в rrdtool create (допускается альтернативный порядок, но тогда имена DS должны быть перечислены указанием опции -t ds-name:ds-name:...). В качестве значения можно использовать символ 'U', обозначающий неопределенное значение. За один вызов rrdtool update в rrdb может быть передано несколько отсчетов, для чего можно указать несколько строк time:value[:value], разделенных пробелом. Скрипт, вызывающий rrdtool update лучше всего запускать через интервал step, указанный при создании rrdb. Приведу пример скрипта, осуществляющего обновление счетчиков принятых и отправленных байт через маршрутизатор, на базе freebsd; также производится мониторинг загрузки процессора:

#!/bin/sh
cpuload=`sysctl vm.loadavg | awk '{printf("2k 100 %s *  p", $4)}' | dc | awk -F'.' '{print $1}'`
inbytes=`ipfw show 1 | awk '{print $3}'`
outbytes=`ipfw show 2 | awk '{print $3}'`

/usr/local/bin/rrdtool update /usr/local/var/rrdtool/cpu.rrd N:${cpuload}
/usr/local/bin/rrdtool update /usr/local/var/rrdtool/myrouter.rrd N:$inbytes:$outbytes

Соответственно правила ipfw, обеспечивающие учет трафика выглядят следующим образом:

# ipfw list
00001 count ip from me to any
00002 count ip from any to me

Счетчик загрузки процессора создается следующим образом:

rrdtool create /usr/local/var/rrdtool/cpu.rrd -s 60 \
        DS:cpuusage:GAUGE:600:0:U \
        RRA:AVERAGE:0.5:1:483840 \
        RRA:MIN:0.5:1440:1 \
        RRA:MAX:0.5:1440:1

Отмечу, что для данного счетчика используется тип GAUGE.

Далее перейдем к процедуре создания графиков. Для этого используется команда rrdtool graph, на мой взгляд, наиболее сложная по синтаксису и самая тяжелая для понимания. Здесь я привожу достаточно краткое ее описание, за более подробной информацией советую обратиться к странице Сергея Богомолова [1] или к странице man rrdgraph (1).

Вызов rrdtool graph имеет, в общем, следующий формат:

rrdtool graph filename [опции] [опеределния переменных] [задание графических элементов]

Из опций можно выделить следующие наиболее употребительные:

Tip: опцию --color можно использовать несколько раз для указания цвета различных элементов.

Это, разумеется, неполный список опций, но для большинства задач его вполне хватает. В rrdtool graph есть, собственно говоря, два способа задания переменных. Один - непосредственно из rrdb, а другой - на базе оценки некоторого выражения, составленного в обратной польской нотации. Рассмотрим оба этих способа. Для извлечения данных из rrdb используется следующая конструкция: DEF:имя_переменной=rrdb:ds-name:функция_консолидации где имя_переменной - символьное имя переменной, rrdb - путь к rrdb файлу, ds-name - имя ряда, указанного в rrdtool create параметром DS:name:..., функция консолидации - любая, из рассмотренных ранее, используется в том случае, если отсчеты происходили чаще, чем разрешение графика (идеальный вариант - один отсчет на один пиксель), если разрешение выше частоты отсчетов, то график масштабируется.

Следующий способ задания переменных графика заключается в оценке выражения, записанного в обратной польской нотации (те, кто знаком, например, с калькулятором dc или некоторыми настольными калькуляторами HP, знают, что это такое). Идея записи такова: числа помещаются в стек, операторы извлекают нужное количество чисел из стека, проводят над ними необходимые операции и помещают результат в стек. Например, выражение

2 3 +

будет обработано следующим образом:

поместить в стек число 2

2 -> .

поместить в стек число 3

2 -> 3 -> .

взять из стека два числа и сложить их, результат поместить обратно в стек

5 -> .

Выражения в обратной польской нотации очень легко обрабатывать программно (для обычной записи необходимо строить бинарное дерево операндов). Теперь вернемся к rrdtool:

запись переменной таким способом выглядит следующим образом:

CDEF:имя_переменной=выражение

Операнды в выраженнии разделяются запятыми. Список операторов довольно широк: стандартные арифметические (+, -, *, /, %), тригонометрические (SIN, COS), алгебраические (LOG, EXP, FLOOR, CEIL), сравнения (GT, GE (>=), LT, LE (<=), EQ, работают так - берутся два числа из стека, сравниваются, в стек помещается 0 или 1, например 1,2,LT поместит в стек 0 (аналогично 1>2)), условный оператор IF (из стека берутся три значения, если последнее истинно (не ноль) - в стек помещается второе ыражение, если ложно - то первое, например 1,2,LT,1,0,IF будет расцениваться так, если 1>2, то поместить в стек 1, иначе поместить 0), статистические (MIN, MAX, берут из двух значений из стека минимальное и максимальное соответственно), ограничения (LIMIT, берет три значения из стека: var,low,high - если var не лежит между low и high, то в стек помещается UNKNOWN, иначе - var). Есть еще ряд операций для работы непосредственно со стеком, но применяются они довольно редко, потому рассматриваться здесь не будут.

Теперь перейдем к этапу задания отрисовки графика. Для этого существует ряд параметров. Общий вид директив, задающих линии и области на графике таков: Тип:имя_переменной[#rgbcolor[:легенда]]

где #rgbcolor - цвет, заданный в RGB стандарте, легенда - подпись под графиком, описывающая линию или область. Поле "тип" может принимать следующие значения:

HRULE - горизонтальная линия на графике (идет параллельно шкале времени) VRULE - вертикальная линия на графике (переменная должна быть временного типа, для чего в CDEF существуют функция TIME) LINE{1,2,3} - линия, заданной толщины (1 - невидимая, 3 - самая толстая) AREA - закрашенная область STACK - рисует линию над предыдущей линией (заданной LINE?, AREA или другим STACK) на расстоянии, равном значению переменной. Для вывода текстовой информации под графиком используется директива GPRINT:имя_переменной:функция_консолидации:формат параметр "формат" определяет метод вывода чисел с плавающей точкой (%lf - фиксированная запятая, %lg - автовыбор, %le - экспоненциальный) и выравнивание:

Например %lf\c. Также можно добавлять %s, что вставит дополнительный пробел по вертикали между текстом и лекендой, например %lg%s\r. Приведу пример скрипта, который создает графики загрузки CPU и сетевого интерфейса:

#/bin/sh
WWWPREFIX=/usr/local/www/htdocs/rrdtool
RRDPREFIX=/usr/local/var/rrdtool

/usr/local/bin/rrdtool graph $WWWPREFIX/cpuusage.png --width 400 --start -1d --end now
                --title "CPU usage"                                             \
                DEF:cpuusage=$RRDPREFIX/cpu.rrd:cpuusage:AVERAGE 	        \
                "CDEF:percent=cpuusage"                                         \
                CDEF:huge=percent,50,GT,percent,0,IF                            \
                CDEF:good=percent,50,GT,0,percent,IF                            \
                HRULE:100#0000FF:"Maximum allowed"                              \
                AREA:good#00FF00:"Good load"                                    \
                AREA:huge#FF0000:"Overload!!"

/usr/local/bin/rrdtool graph $WWWPREFIX/myrouter.png --width 400 --start -1d --end now
            --title "Network usage"                                             \
            DEF:inoctets=$RRDPREFIX/myrouter.rrd:input:AVERAGE  	        \
            DEF:outoctets=$RRDPREFIX/myrouter.rrd:output:AVERAGE    		\
            "CDEF:inbits=inoctets,8,*"                                          \
            "CDEF:outbits=outoctets,8,*"                                        \
            AREA:inbits#00FF00:"In traffic"                                     \
            LINE1:outbits#0000FF:"Out traffic"

Графики, сгенерированные данным скриптом будут выглядеть следующим образом:

В графике загрузки CPU задаются две зоны - нормальная нагрузка и перегрузка. Перегрузка наступает при загруженности процессора на более, чем 50%. В графике загрузки сети происходит перерасчет байтов, возвращаемых счетчиками ipfw в биты, в которых принято мерить нагрузку сети. В данном примере генерируются однодневные графики. Изменив переменную $PAST можно добиться генерации графиков за нужный период времени.

Для запуска скрипта обновления и генерации картинок каждую минуту пишем в /etc/crontab следующую строчку:

*  *  *  *  *  root /usr/local/libexec/rrdtools-update.sh > /dev/null

Для более же удобной генерации картинок можно использовать специальный cgi интерпертатор rrdcgi, позволяющий вставлять теги вида <RRD::COMMAND/>. Генерация графика с его помощью будет выглядеть следующим образом:

#!/usr/local/bin/rrdcgi
<HTML>
<HEAD><TITLE>RRDCGI Demo</TITLE></HEAD>
<BODY>
<H1>Demo of CPU usage graph</H1>
<P>
<RRD::GRAPH ../htdocs/images/cpu.png --lazy --width 400 --start -1d --end now
            --title "CPU usage"  
	     --imginfo '>IMG SRC=/images/%s WIDTH=%lu HEIGHT=%lu >'
            DEF:cpuusage=/usr/local/var/rrdtool/cpu.rrd:cpuusage:AVERAGE 	        
            "CDEF:percent=cpuusage"                                         
            CDEF:huge=percent,50,GT,percent,0,IF                            
            CDEF:good=percent,50,GT,0,percent,IF                            
            HRULE:100#0000FF:"Maximum allowed"                              
            AREA:good#00FF00:"Good load"                                    
            AREA:huge#FF0000:"Overload!!">
</P>
</BODY>
</HTML>

Учтите, что директория, куда производится запись картинок должна быть доступна для записи пользователю, под которым работает apache (по умолчанию nobody:nobody). Параметр --lazy указан, чтобы не "дергать" rrdgraph при чрезмерно частом обновлении страницы.

В данной статье я не буду приводить много примеров графиков. В дебрях интернета я нашел набор скриптов, который позволит решить большинство проблем и выполнить мониторинг основных параметров FreeBSD маршрутизатора. Хотя, чаще всего бывает полезнее использовать snmp для сбора разнообразной информации из гетерогенной сети.

Учтите, что главное в мониторинге - его постоянность, поэтому компьютер, на котором накапливаются статистические данные должен быть хорошо защищен и стабилен. Только так можно проследить динамику развития инфраструктуры сети и предложить замену узких мест (подойти к начальству и продемонстрировать графики производительности намного эффективнее, нежели пытаться объяснить все на пальцах: "Ну эта, тормозит!"). Rrdtool дает гораздо больше возможностей, нежели mrtg, поэтому совершенствовать графики и скрипты можно постоянно, заставляя их показывать ту информацию, которая нужна. Конечно же, обычно наиболее полезным является сбор статистики каждого устройства за определенный промежуток времени (день, месяц, год). Как вы могли убедиться, реализовать такое поведение не должно составить труда.