Однажды в студёную зимнюю пору один из продакшн серверов стал вести себя, мягко говоря, странно. Простейшие операции типа aptitude update и freshclam стали занимать до 5 минут времени и вызывать повышения load average до 5.0.
Тревожным сигналом стало и увеличение количества рабочих процессов mysql до 30-40 (с 1-2 инстансов майскл в нормальном режиме), а также появление в slow query log запросов с временем выполнения в 5 минут! При этом даже без кеширования нормальное выполнение такого запроса происходит за 0.04 секунды максимум..
Чтобы диагностировать источник проблем были предприняты попытки найти и внедрить систему оповещений по электронной почте. Использующийся на сервере munin достаточной детализированности информации не обеспечивал.
За основу был взят скрипт nixCraft project, так как он уже обладал базовым функционалом – умел определять load average (la) и сравнивать его с граничным значением, при пересечении которого направлять электронное письмо администратору.
Оригинальный скрипт, конечно же, оказался неработоспособен, потому что его съел хитропопый парсер, поэтому пришлось долго выяснять в чём причина неработоспособности и некоторые вещи переписать с нуля.
Возможности
Итак, скрипт hugin.sh позволяет
- Посылать email-оповещение при пересечении системой значения load average, указанного администратором
- Указывать в отчёте информацию о свободном месте на дисках
- Указывать в отчёте информацию об iowait системы
- Указывать в отчёте информацию об интенсивности сетефого трафика в килобайтах и пакетах
- Указывать в отчёте 5 самых активных потребителей ресурсов процессора
- Указывать в отчёте 5 самых активных потребителей оперативной памяти
- Указывать в отчёте количество активных соединений к порту 80
- Указывать в отчёте количество открытых дескрипторов файлов веб-сервером nginx
- Указывать в отчёте количество открытых дескрипторов файлов базой данных mysql
- Указывать в отчёте открытые сервером порты с указанием процессов, которых их слушают
- Указывать в отчёте подробный список обрабатываемых mysql запросов в данный момент (processlist)
- Указывать в отчёте вывод top для анализа вручную
Исходный код
Собственно сам скрипт:
#!/bin/bash # # hugin.sh is simple bash script to notify admin by email if load crossed certain limit # version 1.2 # # Copyright (C) 2005 nixCraft project # Copyright (C) 2012 farmal.in # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # Set up limit below NOTIFY="4.0" # admin user email id EMAIL="admins" # Subject for email SUBJECT="Alert $(hostname) load average" # ----------------------------------------------------------------- # Os Specifc tweaks do not change anything below ;) OS="$(uname)" TRUE="1" if [ "$OS" == "FreeBSD" ]; then TEMPFILE="$(mktemp /tmp/$(basename $0).tmp.XXX)" FTEXT='load averages:' elif [ "$OS" == "Linux" ]; then TEMPFILE="$(mktemp)" FTEXT='load average:' fi # get first 5 min load F5M="$(uptime | awk -F "$FTEXT" '{ print $2 }' | cut -d, -f1 | sed 's/ //g')" # 10 min F10M="$(uptime | awk -F "$FTEXT" '{ print $2 }' | cut -d, -f2 | sed 's/ //g')" # 15 min F15M="$(uptime | awk -F "$FTEXT" '{ print $2 }' | cut -d, -f3 | sed 's/ //g')" # mail message # keep it short coz we may send it to page or as an short message (SMS) echo "Load average Crossed allowed limit $NOTIFY (is now $F15M)." >> $TEMPFILE echo "Hostname: $(hostname)" >> $TEMPFILE echo "Local Date & Time : $(date)" >> $TEMPFILE # Look if it crossed limit # compare it with last 15 min load average RESULT=$(echo "$F15M > $NOTIFY" | bc) # if so send an email if [ "$RESULT" == "$TRUE" ]; then echo "----------------FREE-----------------------" >> $TEMPFILE free -mt >> $TEMPFILE echo "----------------IOSTAT---------------------" >> $TEMPFILE # input/output statistics to monitor wheather problem is in NIC, RAM or HDD iostat >> $TEMPFILE echo "---------------NETWORK---------------------" >> $TEMPFILE # display network statistics (transfer rates in pakets and kilobytes) sar -n DEV 1 3 >> $TEMPFILE echo "---------------CPU-------------------------" >> $TEMPFILE # display the top CPU consumers ps aux | sort -nk +3 | tail -5 >> $TEMPFILE echo "---------------RAM-------------------------" >> $TEMPFILE # display the top memory consumers ps aux | sort -nk +4 | tail -5 >> $TEMPFILE echo "----------NUMBER of 80 port connections----" >> $TEMPFILE # how many are there connections to 80 port netstat -an |grep :80 |wc -l >> $TEMPFILE echo "---------------NGINX OPEN FILES------------" >> $TEMPFILE ulimit -n >> $TEMPFILE for pid in `pidof nginx`; do echo "$(< /proc/$pid/cmdline)"; egrep 'files|Limit' /proc/$pid/limits; echo "Currently open files: $(ls -1 /proc/$pid/fd | wc -l)"; echo; done >> $TEMPFILE echo "---------------NETSTAT---------------------" >> $TEMPFILE # display open ports (server only) netstat -lnpt >> $TEMPFILE echo "---------------MYSQL OPEN FILES------------" >> $TEMPFILE ulimit -n >> $TEMPFILE for pid in `pidof mysqld`; do echo "$(< /proc/$pid/cmdline)"; egrep 'files|Limit' /proc/$pid/limits; echo "Currently open files: $(ls -1 /proc/$pid/fd | wc -l)"; echo; done >> $TEMPFILE echo "---------------MYSQL-PROCESSLIST-----------" >> $TEMPFILE mysqladmin -v processlist extended-status -u root -pMYSQLPASSWORD >> $TEMPFILE echo "---------------TOP-------------------------" >> $TEMPFILE # full top listing top -b -n1 >> $TEMPFILE mail -s "$SUBJECT" "$EMAIL" < $TEMPFILE fi # remove file rm -f $TEMPFILE |
Установка
cd /usr/local wget http://farmal.in/scripts/hugin.sh chmod +x hugin.sh |
Заносим скрипт в планировщик крон, чтобы он выполнялся каждые 10 минут с высоким приоритетом:
crontab -e 2,12,22,32,42,52 * * * * nice -n -5 /usr/local/hugin.sh |
Добавляем элиасы для sendmail:
vim /etc/aliases admins: root, ваш@email.com |
В самом скрипте заменяем MYSQLPASSWORD на пароль root’а mysql, либо создаём корректный .my.cnf с эти данными в домашней папке пользователя, от имени которого будет запускаться скрипт.
Системные требования
Для работы hugin.sh кроме стандартных программ понадобятся:
- bc
- sar
- iowait
Мне скрипт помог определить, что проблема была в винчестере сервера, отчего происходили затыки (mysql активно работает с диском). Надеюсь, он поможет и вам! Если кто-нибудь доработает под свои нужды – буду рад опубликовать исправленную и доработанную версию.
Зачем писать про FreeBSD
if [ “$OS” == “FreeBSD” ]; then
и потом использовать
free
pidof
/proc/$pid
і т.д.
Лучше сразу
if [ “$OS” == “FreeBSD” ]; then
echo “This OS is not supported”
exit 1
fi
:)
да, возможно так и стоит сделать :) это как раз та часть, которая осталась от первоначальной версии скрипта, а с БСД семейством я знаком только по книжкам в детстве из которых вынес насколько и почему бсд круче линуксов.. а на практике приходится работать с debian и centos :)
Ловите:
hugin.sh: 35: [: Linux: unexpected operator
hugin.sh: 38: [: Linux: unexpected operator
hugin.sh: 52: hugin.sh: cannot create : Directory nonexistent
hugin.sh: 53: hugin.sh: cannot create : Directory nonexistent
hugin.sh: 54: hugin.sh: cannot create : Directory nonexistent
hugin.sh: 61: [: 1: unexpected operator