Сейчас под этот сайт специально пришлось развернуть отдельные вебсервер, php и MySQL, чтобы его проблемы не приводили к неработе других сайтов на этом же сервере. И пришлось написать специальный скрипт, который отслеживает порождённые им зависания и перезапускает nginx/php/mysql по потребности.
Сайт: | Евразия | Русская Сила |
---|---|---|
Софт | Apache2.2 +mod_php 5.2 + MySQL 5.1 | nginx + php-fpm 5.6 + mysql 5.6 |
CMS | Самописный движок | WordPress 4.7 с темой оформления Surfarama. WP-FFC + WP File Cache (более 3 гиг места на винте занято файловым кешом) |
Кеширование | отсутсвует | 256М Memcached 1.4 |
Память оперативная всего | 1.3 Гб | 3.5 Гб |
Память оперативная подробно, примерно | 420М MySQL + 900M apache+php | 1540M MySQL + 256M Memcached + 1300Гб php-fpm + 440M nginx |
Жор процессора сравнительный | 1 | 10-20 |
Число запросов MySQL на новость | 4 | 11-15 |
Число запросов MySQL на категорию | 11 | 22-90 |
Число запросов MySQL на главную | 80 | 150 |
Число материалов | 50 тысяч | 37 тысяч |
Посещаемость, тысяч в сутки | 7 | 0.7 |
Комментарии | работоспособны, с капчей | Не работают |
отклик страниц | менее секунды | от 0.6 до 6 секунд страница новости, от 20 до 60 секунд - страница категории (не из кеша) |
отклик полнотекстового поиска | 3-6 секунд. | Бесконечность - не работает |
Особенности настройки админки | разделение на два разных сайта - админка и "для посетителей", иначе проблемы безопасности неустранимы | Введено ограничение на приход админов с определённых IP, иначе трафик попыток перебора паролей задалбывает |
Особенности настройки для посетителей | В конфиге nginx большое количество сложных конструкций, минимизирующих возможности атаки и работающих с memcached |
#!/usr/local/bin/bash # ================================== ======================================== # Sergej Qkowlew V2.0 2016-08-10 Сергей Яковлев # # restart nginx, php-fpm, mysql ser- Перезапуск Nginx, php-fpm, MySQL сервера # vers in the SAME jail. Checks con- в ЭТОМ ЖЕ джейле. Проверяет коннект, со- # nect, create database if needed. здаёт тестовую базу если её нет. # ---------------------------------- ---------------------------------------- # Get $basepath $IpAddr from config. Берём из основной части пару параметров. # May be setted manually, of course. Но можно и задать тут непосредственно. RetPath=`pwd` cd /root/hosting . ./config >/dev/null 2>&1 . ./functions >/dev/null 2>&1 # basepath=/home/www/servers # IpAddr=86.62.112.121 # ---------------------------------- ---------------------------------------- # Configurable parameters of script: Конфигурируемые параметры этого скрипта: # path to http://ip host of server Путь к хосту http://ip этого сервера WwwDir=$basepath/nodomain Html=html SqMaxLines=40 SqLineBegin="mysql_restart" # filenames имена файлов SqCheck_Config_Name=.check_config.php SqCheck_Mysql_Name=.check_mysql.php SqCheck_Alive_Name=.check_alive.php # start/stop mysql server script скрипт остановки/запуска mysql MySqlServer=/usr/local/etc/rc.d/mysql-server # test Read Only base parameters Параметры соединения с тестовой базой MySqlAddr=$IpAddr MySqlBase=nodomain MySqlUser=nodomain MySqlPassword=TestMySqlDkflkeEERTTTG MySqlFrom=$IpAddr # database creation script full line Команда создания базы данных (скриптом) # with Read Only access for our user с доступом только на чтение нашему узеру MySqlCreate=". ./db_ro $MySqlUser $MySqlBase $MySqlPassword $MySqlFrom" # database check full line w/o php Команда проверки базы данных без php MySqlCheck="/usr/local/bin/mysqlcheck --host=$MySqlAddr --user=$MySqlUser --password=$MySqlPassword -q -s --databases $MySqlBase" PID_FILE=/var/run/502.pid [ -z "$IpAddr" ] && IpAddr=`ifconfig | awk '($1=="inet"){if ($2 != "127.0.0.1") { print $2; } }'` [ -z "$IpAddr" ] && exit # ================================== ======================================== # Main executable code begins Содержательный исполняемый код скрипта # ================================== ======================================== # Help Помощь if [ "$1" == "help" ] then echo '============================================================================ Without parameters - manual check. Без параметров - ручная проверка help - this help - этот текст помощи cron - every minute check. - ежеминутная проверка из крона http://$IpAddr/$SqCheck_Alive_Name - здесь смотреть из веба лог рестартов php http://$IpAddr/$SqCheck_Mysql_Name - здесь смотреть из веба лог рестартов mysql ---------------------------------- ---------------------------------- ' exit fi # ----------------------------------- ------------------------------------ # check if already running Проверяем, не запущен ли уже if [ -e $PID_FILE ] then [ "$1" == "cron" ] || echo "Exiting: $0 already running or previous run was completed with errors" exit else touch $PID_FILE # Nginx # ---------------------------------- ---------------------------------------- wget --output-document=/dev/null -t 1 --timeout=1 -q -o /dev/null http://$IpAddr/robots.txt >/dev/null 2>&1 result="$?" ; [ -z "$1" ] && echo -n "nginx check: result of wget: $result; " if [ ".$result" = ".0" ] then [ -z "$1" ] && echo "ok." else logger -t watchdog "$IpAddr timeout error for nginx detected" killall -9 nginx >/dev/null 2>&1 rm /var/run/nginx.pid /usr/local/etc/rc.d/nginx stop >/dev/null 2>&1 /usr/local/etc/rc.d/nginx start >/dev/null 2>&1 logger -t watchdog "restarted nginx by $0 $1" [ -z "$1" ] && echo "restarted nginx by $0" fi # Php # ---------------------------------- ---------------------------------------- wget --output-document=/dev/null -t 1 --timeout=9 -q -o /dev/null http://$IpAddr/${SqCheck_Alive_Name} >/dev/null 2>&1 result="$?" ; [ -z "$1" ] && echo -n "php check: result of wget: $result; " if [ ".$result" = ".0" ] then [ -z "$1" ] && echo "ok." else logger -t watchdog "$IpAddr 502 error for php detected" # /usr/local/etc/rc.d/php-fpm stop >/dev/null 2>&1 killall -9 php-fpm SqPrev="`head -n $SqMaxLines $WwwDir/${Html}/${SqCheck_Alive_Name}`" echo "'; ?>" >$WwwDir/${Html}/${SqCheck_Alive_Name} echo "$SqPrev">>$WwwDir/${Html}/${SqCheck_Alive_Name} [ -d $basepath/rusila.su/${Html}/ ] && cat $WwwDir/${Html}/${SqCheck_Alive_Name} >$basepath/rusila.su/${Html}/${SqCheck_Alive_Name} sleep 2 /usr/local/etc/rc.d/php-fpm start >/dev/null 2>&1 logger -t watchdog "restarted php-fpm by $0 $1" [ -z "$1" ] && echo "restarted php-fpm by $0" sleep 3 fi # Mysql # ---------------------------------- ---------------------------------------- # Create config .php file if needed. Создаём php конфигурационный файл if [ ! -s "$WwwDir/${SqCheck_Config_Name}" ] || [ ".$1" = ".force" ] then echo "'; echo \$line ; return FALSE; } else { echo 'Trying mysqli_connect... OK.
'; echo \$line ; mysqli_close(\$link); return TRUE; } } // exists mysqli_connect if (function_exists('mysql_connect')) { \$link = mysql_connect('$MySqlAddr', '$MySqlUser', '$MySqlPassword', '$MySqlBase'); if (!\$link) { header('HTTP/1.0 404 Not Found'); echo 'Trying mysql_connect... Error!
'; echo \$line ; return FALSE; } else { echo 'Trying mysql_connect... OK.
'; echo \$line ; mysql_close(\$link); return TRUE; } } // exists mysql_connect echo 'No valid mysql/mysqli connect function, cannot check
'; echo \$line ; } // function SqMySqlCheck ?> ">$WwwDir/${SqCheck_Config_Name} fi # ---------------------------------- ---------------------------------------- # create main URI file for checking. Создаём файл который проверять по http if [ ! -s "$WwwDir/$Html/${SqCheck_Mysql_Name}" ] || [ ".$1" = ".force" ] then echo "" >$WwwDir/$Html/${SqCheck_Mysql_Name} fi # ---------------------------------- ---------------------------------------- # Check URL Проверяем URL wget --output-document=/dev/null -t 1 --timeout=1 -q -o /dev/null http://$IpAddr/${SqCheck_Mysql_Name} >/dev/null 2>&1 result="$?" ; [ -z "$1" ] && echo -n "mysql check: result of wget: $result; " if [ ".$result" = ".0" ] then [ -z "$1" ] && echo "ok." else # Errorlevel = 8 from wget - 404 Код возврата 8 из wget - ошибка 404 logger -t watchdog "$IpAddr mysql check no answer detected by $0 $1" if [ ! -z "$MySqlCheck" ] && $MySqlCheck >/dev/null 2>&1 then logger -t watchdog "but command-line mysql check success $0 $1" DbCreated= else $MySqlServer stop >/dev/null 2>&1 sleep 10 $MySqlServer start >/dev/null 2>&1 logger -t watchdog "restarted MySQL server by $0 $1" sleep 10 fi if [ -z "$MySqlCreate" ] || [ -z "$MySqlCheck" ] then DbCreated= else if $MySqlCheck >/dev/null 2>&1 then DbCreated= else $MySqlCreate >/dev/null 2>&1 logger -t watchdog "database $MySqlBase created by $0 $1" DbCreated=", and database $MySqlBase [re]created" fi fi # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -. # Collect previous restart lines Собираем строки о прошлых рестартах SqPrev=`cat $WwwDir/$Html/${SqCheck_Mysql_Name} | grep $SqLineBegin | head -n $SqMaxLines ` SqNow=`date` echo " $SqPrev '); ?>" >$WwwDir/$Html/${SqCheck_Mysql_Name} [ -z "$1" ] && echo "restarted mysql by $0" fi [ -e "${PID_FILE}" ] && rm "${PID_FILE}" fi # else [ -e "$PID_FILE" ]