Мониторинг для бедных или SAR + MySQL + Gnuplot

в 15:35, , рубрики: Gnuplot, mysql, SAR, системное администрирование, метки: ,

Почему именно SAR?

Я уже довольно давно занимаюсь мониторингом. Поэтому по роду своей деятельности частенько сталкиваюсь с нестандартными ситуациями, когда приходится придумывать различные «велосипеды», для того, чтобы мониторить хост. Например, мы будем рассматривать ситуацию, когда у нас есть сервер (виртуалки или VDS), который очень ограничен в ресурсах.

Существует множество хороших систем мониторинга, таких как Zabbix, Nagios, Cacti и т.д. Но для нашей ситуации все они не подходят, в силу ясных причин — они сами потребляют ресурсы, которых у нас итак не очень много. Сразу возникает вопрос, как быть? И тут к нам на помощь спешит SAR.

Установка и настройка SAR

SAR (System Activity Report) — это очень мощная утилита, для сбора статистической информации о производительности системы. Входит в пакет sysstat. Поэтому, если у вас на сервере еще нет пакета sysstat, устанавливаем его. Например, для Debian:

# apt-get install sysstat

Затем нам потребуется настроить сам SAR. Включаем sysstat, для этого правим файл:

# vim /etc/default/sysstat

И меняем строку ENABLED=«false» на ENABLED=«true». После чего нам нужно поправить задание в cron.

# vim /etc/cron.d/sysstat

Я собираю данные каждыю минуту, поэтому в моей системе это выглядит так (у вас может быть немного иначе):

# Activity reports every 1 minutes everyday
* * * * * root command -v debian-sa1 > /dev/null && debian-sa1 1 1

# Additional run at 23:59 to rotate the statistics file
59 23 * * * root command -v debian-sa1 > /dev/null && debian-sa1 60 2

Перезапускаем сервис sysstat:

# service sysstat restart

На этом настройка окончена.

Использование SAR

Я не буду подробно описывать, как использовать SAR. Для этого существует множество статей в интернете, да и сам SAR предоставляет довольно обширную документацию. Все что вам нужно, это man sar, а там, думаю, разберетесь.

Учим SAR заливать репорты в MySQL

Да-да, вы все правильно прочитали. Конечно, мы можем использовать SAR по своему прямому назначению, вызывать его из консоли и получать репорты в таком виде:

$ sar -s 07:00:00 -e 07:10:00
Linux 3.2.0-4-amd64 (mind-x)    02.03.2015      _x86_64_        (1 CPU)

07:00:01        CPU     %user     %nice   %system   %iowait    %steal     %idle
07:01:01        all      0,64      0,00      0,15      0,10      0,00     99,11
07:02:01        all      0,03      0,00      0,02      0,00      0,00     99,95
07:03:01        all      0,03      0,00      0,02      0,00      0,00     99,95
07:04:01        all      0,03      0,00      0,02      0,02      0,00     99,93
07:05:01        all      0,05      0,00      0,03      0,00      0,00     99,92
07:06:01        all      0,63      0,00      0,17      0,10      0,00     99,11
07:07:01        all      0,03      0,00      0,02      0,00      0,00     99,95
07:08:01        all      0,02      0,00      0,02      0,00      0,00     99,97
07:09:01        all      0,03      0,00      0,02      0,00      0,00     99,95
Среднее:     all      0,17      0,00      0,05      0,02      0,00     99,76

Но согласитесь, смотреть в эти цифры после долгого рабочего дня совсем не хочется, глаза болят и т.д. Что же делать? Как работать со столь важной информацией?

Признаюсь, я целый вечер думал над этой проблемой. Все решения мне казались странными. Возможно, решение, которое я придумал, тоже является странным, но почему я захотел подружить SAR с MySQL? Постараюсь объяснить далее.

Если вы внимательно читали часть про настройку SAR, то вы, наверно, заметили, что SAR ротирует свои журналы после полуночи. Сделано это специально, чтобы разбивать журналы по дням. Мне захотелось получать данные непрерывно (ведь далее мы будем строить по ним графики), чтобы я мог выбрать только нужный мне столбец, за определенное время и т.д. И тут я задумался, а почему не заливать все эти репорты в базу данных? Так как на сервере уже был MySQL, то выбор стал очевиден.

Итак, дружим SAR и MySQL

Я перерыл весь интернет по этому поводу, но никакой информации практически нет, только пару статей, результатом прочтения которых стал скрипт. Но прежде чем мы его разберем, давайте подготовим базу.

Создаем базу данных sysstat:

CREATE DATABASE `sysstat`;

Создаем пользователя sysstat (вы можете дать этому пользователю любые другие права).

CREATE USER 'sysstat'@'localhost' IDENTIFIED BY 'some_pass';
GRANT ALL PRIVILEGES ON sysstat.* TO 'sysstat'@'localhost';

Создаем таблицы, у меня это выглядит примерно так:

CREATE TABLE `host_health_cpu` (
  `datetime` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `pct_user` decimal(10,2) DEFAULT NULL,
  `pct_nice` decimal(10,2) DEFAULT NULL,
  `pct_system` decimal(10,2) DEFAULT NULL,
  `pct_iowait` decimal(10,2) DEFAULT NULL,
  `pct_steal` decimal(10,2) DEFAULT NULL,
  `pct_idle` decimal(10,2) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `host_health_memory` (
  `datetime` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `kbmemfree` int(11) DEFAULT NULL,
  `kbmemused` int(11) DEFAULT NULL,
  `per_memused` decimal(10,2) DEFAULT NULL,
  `kbbuffers` int(11) DEFAULT NULL,
  `kbcached` int(11) DEFAULT NULL,
  `kbcommit` int(11) DEFAULT NULL,
  `per_commit` decimal(10,2) DEFAULT NULL,
  `kbactive` int(11) DEFAULT NULL,
  `kbinact` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `host_health_la` (
  `datetime` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `runq_sz` int(11) DEFAULT NULL,
  `plist_sz` int(11) DEFAULT NULL,
  `ldavg_1` decimal(10,2) DEFAULT NULL,
  `ldavg_5` decimal(10,2) DEFAULT NULL,
  `ldavg_15` decimal(10,2) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `host_health_net` (
  `datetime` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `iface` varchar(7) DEFAULT NULL,
  `rxpck_persec` decimal(10,2) DEFAULT NULL,
  `txpck_persec` decimal(10,2) DEFAULT NULL,
  `rxbyt_persec` decimal(10,2) DEFAULT NULL,
  `txbyt_persec` decimal(10,2) DEFAULT NULL,
  `rxcmp_persec` decimal(10,2) DEFAULT NULL,
  `txcmp_persec` decimal(10,2) DEFAULT NULL,
  `rxcst_persec` decimal(10,2) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Все, теперь у нас все готово, для того, чтобы заливать репорты в MySQL.

Разбираем скрипт

В скрипте мы будем использовать утилиту sadf, которая может отображать данные, собранные SAR, в различных форматах, например, в CSV, JSON, XML. Нам понадобится CSV. Сам скрипт не очень большой и не сложный для понимания, дополню лишь то, что я удаляю первые 2 столбца из вывода sadf, в них содержится информация о хосте и интервале между снятиями данных. Хост в нашем случае один, а интервал я и так знаю, поэтому эта информация мне не нужна.

#!/bin/bash
#Инициализируем переменные
WORKDIR=/var/cache/sar
FMATDIR=/var/cache/sar_data
SADF=`which sadf`
HEAD=`which head`
AWK=`which awk`
#Копируем последний журнал в рабочую директорию
cd ${WORKDIR}
COPY=`which cp`
SAR_LOG=/var/log/sysstat
cd ${SAR_LOG}
LATEST_DATA=`ls -tlr ${SAR_LOG} | tail -1 | awk '{print $9}'`
${COPY} ${LATEST_DATA} ${WORKDIR}

for file in `dir ${WORKDIR}`;
do
#Выбираем репорты за последние 6 минут. Для этого создаем переменную $TIME
UNIXTIME=$((`date +%s`-300))
TIME=`date -d@${UNIXTIME} +%H:%M:%S`
#Статистика по CPU
${SADF} -d ${file} -s ${TIME} -t | cut -d ';' -f3,5- > "${FMATDIR}"/"${file}"-host_health_cpu.csv
#Сетевая статистика
${SADF} -d ${file} -s ${TIME} -t -- -n DEV | cut -d ';' -f3- > "${FMATDIR}"/"${file}"-host_health_net.csv
#Размеры очередей и LA
${SADF} -d ${file} -s ${TIME} -t -- -q | cut -d ';' -f3- > "${FMATDIR}"/"${file}"-host_health_la.csv
#Статистика по использованию памяти
${SADF} -d ${file} -s ${TIME} -t -- -r | cut -d ';' -f3- > "${FMATDIR}"/"${file}"-host_health_memory.csv
done
#Заливаем данные в MySQL
cd ${FMATDIR}
MYSQL=`which mysql`
USER='user'
PASS='some_pass'
HOST='localhost'
DB='sysstat'
for file in `dir -d *`;
do
${MYSQL} -u${USER} -p${PASS} -h${HOST} -D${DB} -e "LOAD DATA LOCAL INFILE '${FMATDIR}/${file}' INTO TABLE `echo ${file} | sed 's/.csv//g' | awk -F'-' '{print $2}'` FIELDS TERMINATED BY ';' IGNORE 1 LINES;"
done
#Удаляем временные файлы
rm -fr ${FMATDIR}/*.csv
rm -fr ${WORKDIR}/sa*

В общем, такой вот скрипт. Для того, чтобы заливка началась, добавляем его в cron. Вызывать будем раз в 5 минут.

 */5 * * * *     bash /usr/local/sbin/sar.sh

Интересной особенностью SAR является то, что когда мы вызываем его как sar -s, указывая время начала, то само это время не входит в репорт, поэтому вызывая в скрипте sar за последние 6 минут, в выводе мы получаем репорт за 5 минут. Это обязательно надо учитывать. Иначе будут «дырки» или дубли в MySQL.

В результате у вас получится примерно следующее:

mysql> SELECT * FROM host_health_memory WHERE datetime > NOW() - INTERVAL 10 MINUTE;
+---------------------+-----------+-----------+-------------+-----------+----------+----------+------------+----------+---------+
| datetime            | kbmemfree | kbmemused | per_memused | kbbuffers | kbcached | kbcommit | per_commit | kbactive | kbinact |
+---------------------+-----------+-----------+-------------+-----------+----------+----------+------------+----------+---------+
| 2015-03-02 08:36:01 |   1381896 |    679396 |       32.00 |    104044 |   155520 |   803420 |      38.00 |   484244 |  142688 |
| 2015-03-02 08:37:01 |   1377476 |    683816 |       33.00 |    104068 |   155668 |   810284 |      39.00 |   487632 |  142808 |
| 2015-03-02 08:38:01 |   1377476 |    683816 |       33.00 |    104096 |   155672 |   810284 |      39.00 |   487668 |  142804 |
| 2015-03-02 08:39:01 |   1377476 |    683816 |       33.00 |    104120 |   155680 |   810524 |      39.00 |   487832 |  142804 |
| 2015-03-02 08:40:01 |   1372416 |    688876 |       33.00 |    104160 |   155684 |   819104 |      39.00 |   490708 |  142816 |
| 2015-03-02 08:41:01 |   1377104 |    684188 |       33.00 |    104276 |   155700 |   810524 |      39.00 |   488008 |  142808 |
| 2015-03-02 08:42:01 |   1379228 |    682064 |       33.00 |    104288 |   155708 |   816640 |      39.00 |   486392 |  142632 |
| 2015-03-02 08:43:01 |   1378980 |    682312 |       33.00 |    104328 |   155708 |   816744 |      39.00 |   486680 |  142628 |
| 2015-03-02 08:44:01 |   1378608 |    682684 |       33.00 |    104356 |   155716 |   816932 |      39.00 |   486936 |  142636 |
| 2015-03-02 08:45:01 |   1371564 |    689728 |       33.00 |    104392 |   155720 |   827704 |      40.00 |   491912 |  142648 |
+---------------------+-----------+-----------+-------------+-----------+----------+----------+------------+----------+---------+

Что делать с этими данными?

Вы можете делать с ними все, что угодно. Я чуть выше писал, что на основе этих данных мы будем рисовать графики. Сделать это можно любым доступным способом. Здесь все зависит лишь от вашей фантазии.

Gnuplot

Мой выбор выпал на Gnuplot. Это довольно удобный инструмент для построения графиков, диаграмм и т.д. По Gnuplot есть огромная документация на его родном сайте, поэтому часть про его возможности и как его использовать мы пропустим.

Я написал вот такой скрипт:

#!/bin/bash
sleep 10
# Параметры по умолчанию
width="640"
high="480"
outfile="graph.svg"
format="svg"

NO_ARGS=0
E_OPTERROR=65

if [ $# -eq "$NO_ARGS" ]  # Сценарий вызван без аргументов?
then
  echo "Скрипт запущен без параметров!
     Для правильной работы используйте параметры:
    -o  Указывает имя файла графика (по умолчанию "graph.svg")
    -f  Указывает формат файла графика (по умолчанию "svg")
    -w  Ширина файла (по умолчанию "640")
    -h  Высота файла (по умолчанию "480")
    -t  Тип графика
Повторите вашу попытку."
  exit $E_OPTERROR
fi
#Выбираем необходимые параметры
while getopts ":h:w:t:f:o:" Option
do
  case $Option in
    h) high="$OPTARG";;
    w) width="$OPTARG";;
    f) format="$OPTARG";;
    o) outfile="$OPTARG";;
    t) type="$OPTARG";;
  esac
done
shift $(($OPTIND - 1))

#Записываем результат во временный файл /tmp/datatmp
if [ ${type} == 'cpu' ]
then
    #Создаем заголовок графика
    title="Распределение нагрузки на CPU, %"
    QUERY="select time(datetime), pct_idle as idle, pct_iowait as iowait, pct_system as system, pct_user as user from host_health_cpu where now() - interval 1 hour < datetime;"
    format_y='set format y "%.0f%%";'
elif [ ${type} == 'mem' ]
then
    title="Использование памяти, B"
    QUERY="select time(datetime),(kbmemfree * 1024) as Free,(kbmemused * 1024) as Used,(kbbuffers * 1024) as Buffers,(kbcached * 1024) as Cached from host_health_memory where now() - interval 1 hour < datetime;"    
    format_y="set format y '%.1s%cB'"
elif [ ${type} == "net" ]
then
    title="Входящий и исходящий трафик, B/s"
    QUERY='select time(datetime), (rxbyt_persec * 1024) as Rx, (txbyt_persec * 1024) as Tx from host_health_net where now() - interval 1 hour < datetime and iface="eth0";'
    format_y="set format y '%.1s%cB/s'"
elif [ ${type} == "la" ]
then
    title="Load Average"
    QUERY="select time(datetime),ldavg_1 as LoadAvg1, ldavg_5 LoadAvg5,ldavg_15 as LoadAvg15 from host_health_la where now() - interval 1 hour < datetime;"
    format_y="set format y '%.2f'"
fi

#Делаем выборку из базы и сохраняем во временный файл
MYSQL=`which mysql`
USER="user"
PASS="some_pass"
DB="sysstat"
${MYSQL} -u${USER} -p${PASS} -D${DB} -B -r -e "${QUERY}" >> /tmp/datatmp_${type}

#Подсчитываем количество столбцов в файле.
cols=`awk '{print NF}' /tmp/datatmp_${type} | sort -nu | tail -n 1`
#Определяем временные промежутки
hour_ago=`${MYSQL} -u${USER} -p${PASS} -D${DB} -N -e "select time(datetime) from host_health_cpu where now() - interval 1 hour < datetime limit 1;"`
now=`${MYSQL} -u${USER} -p${PASS} -D${DB} -N -e "select time(datetime) from host_health_cpu where now() - interval 1 hour < datetime order by datetime desc limit 1;"`

#Рисуем график
gnuplot << EOP
#Указываем формат файла и его размер
set terminal ${format} size ${width},${high}

#Указываем выходной файл
set output ${outfile}

#Рисуем легенды
set key autotitle columnhead
set key outside center bottom
set key horizontal
#Рисуем заголовок
set style fill transparent solid 0.5 noborder
set title "${title}"

#Делаем ось Х в формате отображения дат
set xdata time
set timefmt "%H:%M:%S"
set xrange ["${hour_ago}":"${now}"]
set xtics format "%H:%M"

#Указываем имена осей
set xlabel "Время"
set ylabel "${title}"
set grid
set yrange [0:*]
${format_y}
#Получаем конечный результат.
plot for [i=2:${cols}] "/tmp/datatmp_${type}" using 1:i  smooth unique with filledcurve x1

EOP
#Удаляем временный файл.
rm /tmp/datatmp_${type}
echo "Image write to "${outfile}""
exit 0;

Запускаем его примерно так:

$ sar-plot.sh -t mem -o /path/to/memory.svg

В результате получается вот такой график за 1 час.
Мониторинг для бедных или SAR + MySQL + Gnuplot - 1
Если добавить этот скрипт в cron и вызывать его, например, раз в 5 минут, то у нас всегда будут свежие графики. Например у меня сделано вот так:

*/5 * * * *     bash /usr/local/sbin/sar-plot.sh -t cpu -o /path/to/images/cpu.svg
*/5 * * * *     bash /usr/local/sbin/sar-plot.sh -t mem -o /path/to/images/memory.svg
*/5 * * * *     bash /usr/local/sbin/sar-plot.sh -t net -o /path/to/images/network.svg
*/5 * * * *     bash /usr/local/sbin/sar-plot.sh -t la -o /path/to/images/la.svg

Далее эти изображения можно добавить на HTML-страницу, сделать чтобы она автоматически обновлялась. Таким образом мы можем мониторить производительность хоста практически в реальном времени (с задержкой 5 минут). Чего для нас в принципе достаточно.

Заключение

SAR — очень удобный инcтрумент, незаменимый в работе системного администратора. Описаный мной способ мониторинга хорошо подходит для тех, кто очень ограничен в ресурсах, не хочет поднимать на сервере Zabbix или по различным другим причинам.
Вы можете развить это решение под себя:

  • Добавить другие метрики, которые не описаны в статье, например, можно добавить статистику по активности дисковой подсистемы.
  • Вы можете рисовать графики любым другим способом, например, с использованием Javascript-библиотек.
  • Вы можете доработать систему так, чтобы при достижении каких-нибудь критических значений, на вашей веб-странице отбражались алерты, и, например, отправлялись сообщения себе на почту.

Все зависит от вашего желания. Удачи!

Автор: Archymind

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js