- PVSM.RU - https://www.pvsm.ru -
Эта небольшая статья посвящена описанию моих действий по приведению за $10/месяц в приемлемое состояния для работы персональной сети сайтов на WordPress в одиночном и сетевом режиме (WordPress Network Mode).
Итак, дорогие мои хай-тек специалисты и случайные читатели, история эта началась в конце апреля 2012 года, когда мой любимый городской провайдер-монополист наотрез отказал мне в дальнейшем размещении моего трех-ядерного атлона в своей стойке.
Я живу в маленьком курортном городке (3 тыс.мы-местные и 50 тыс.понаехали-тут-летом) и большинство моих персональных проектов посвящено родному и любимому городу. Пара городских вебкамер, погода, справочники и т.д. Кроме того, я ещё чуток борюсь за справедливость и права: организовал небольшой проектик на домене TV — снимаем репортажи про беспредел чинушей и т.д...
Так вот о чем это я… ах да! Стоял, значит, мой двух-юнитовый серверочек (его и сервером тяжко-то назвать) совершенно бесплатно в стойке у провайдера, у которого стоек было аж 3 и 2 из них стояли пустые. Почему же бесплатно? Потому что этому самому провайдеру я помогал развивать корпоративные сайты. Кроме того, других людей, с аналогичными моим знаниями в нашем маленьком городке просто нет — далекая периферия, умные быстро уезжают в Москву и другие зарубежья.
В общем, крутились на AthlonX3 / 4Gb RAM мои проекты и я совершенно не заморачивался по вопросу оптимизации, наладки — делал себе сайтики на любимом wordpress и даже не думал, что когда-нибудь халява и кончится.
После очередной публикации любительского репортажа-недокомпромата на местного мэра ко мне подошел директор провайдера (из той же партии что и мэр) и порекомендовал в течении суток забрать сервер. Компроматики-то на нём лежали. Ещё хорошо, что подошел и сказал.
Суток уже не оставалось. А проектики мои уже и в SERP'е хорошо сидели, и позиции терять не хотелось… Суть — за 12 часов был найден первый попавшийся дешевый
Кстати, ограничение в $10 взялось не просто так — именно такая сумма у меня была на тот момент на webmoney, накапало в Директе. Собственно, этот бюджет поднялся всего на $2 — за дополнительный IP. Сейчас мне этот
Параметры
Я вроде как и не программист — так себе,
быдлоламо-кодер, и не администратор — знаю yum install, yum erase, make && make install… Но зато я более-менее владею незаменимым навыком гугл-обучения.
Изначально я поставил apache фронт-эндом. Это было нечто :) ab2 со старого сервера показывало не более 1.4 requests per second. Nginx я тогда видел в первый раз. Поставил, почитал (чуток, времени не было) кое-как накопипастил конфигов — завелось. И даже стало быстрее. После установки eaccelerator результат ab2 в 6 r.p.s мне показался просто отличным и я это всё дело так и оставил.
Никакого mysqltuner.pl, никакого выбора/оптимизации apache mpm, даже nginx кушал столько памяти, что 80% забитого свопа мне казалось нормальным. Но видимо это было из-за стресса :) На мой взгляд, сайты не тормозили, ну чуток задумывались. Но зная свою аудиторию — я прекрасно понимал, что мои посетители никуда не уйдут.
Сайтики росли и ширились — неспешно, но выйдя в TOP-5 Google и Яндекс по запросам относительно моего города-курорта, сайты летом стали часто недоступны (RAM/SWAP — 100%). Поскольку времени заниматься оптимизацией летом реально не было — я задачу отложил на конец года.
С конца апреля до 7 декабря в таком режиме «еле-едет» сайтики мои наработали вот такой статистики:

.SU — это WordPress Network — сетка сайтов, посвященная моему городу. Остальные — «одиночные» WordPress'ы. P.S. Когда всё разнесено на поддомены — удобнее обслуживать. Кстати, я только сегодня прочитал про вынесения статики на поддомен, буду пробовать, но позже.
(Прошу в комментариях не описывать в подробностях, как плох WordPress, его код и какое у него ресурсопотребление. Сам столкнулся с этим, как головой об стену. Возможно, это суть следующей статьи.)
Кстати, важным будет упомянуть тот факт, что за этот период у меня начали появляться клиенты в городе, которым нужно было сделать сайт. Я особо не заморачивался — штамповал на WP. И в один прекрасный момент, с поднятием очередного сайта, это всё нагромождение WP стало работать из рук вон плохо.
yum update принес много обновлений. Но они особо не помогли, да и не могли помочь. Я знал, что надо переходить на php-fpm и заводить кеширование. Для моего случая кеширование наиболее актуально, поскольку абсолютное большинство моих сайтов практически статично — expire в пару недель всех устраивает. А те сайты, которые не статичны (а это в первую очередь, тяжелый WordPress Network), можно аккуратненько подкрутить так, что бы кеш обновлялся строго там где надо и в то время в которое надо.
В общем, php-fpm без бубнов ставиться не хотел. Через пяток другой репозиториев нашел скомпиленый пакет, со всеми необходимыми мне опциями. В общем-то можно было из сорцов — но не хотелось. Установил, по быстрому настроил… httpd stop && php-fpm start — ООО! 9 r.p.s! Иду на рекорд! Шутка конечно.
php-frm стоит, а eaccelerator-то пришлось снести — не совместим, нужно новый. Гугл-обучение — оказывается этот акселератор уже давно устарел. yum install php-pecl-apc. Поставилось, опять не без бубнов — поднастроил, завелось. Генерация страниц стала работать как-то очень странно. Первое открывание долгое — потом на какое-то время сайты начинают летать. Потом тупить. Потом опять летать. Долгое гугл-обучение привело меня к описанию использования APC в Facebook. Это оказалось полезным, многое стало понятным.
Настроил php-fpm. Лазаю по сайту — тут вдруг 502, F5, F5, 504. Что за байда! php-fpm перестает откликаться через какое-то время. Опять читаю — памяти мало, поставил больше. Лучше, но ещё через некоторое время опять 502. Читаю, читаю, memleaks, deadlocks, полный комплект.
Сношу APC, ставлю XCache. Вообще не завелось. Ладно. Надо думать и читать. Ставлю обратно APC. Опять настраиваю. С другим конфигом пока вылетать перестало. Да и скорость значительно поднялась — время генерации особенно простых страниц уменьшилось в несколько раз (2,7s -> 0.8s). Да, ребятушки, 2,7s — это у прокачанного WordPress простая страница. А если плагинов понавесить — как у меня на .TV — так там вообще без оптимизации только генерация 19-25s.
И тут вдруг я понял! Сначала нужно настроить весь окружной софт, который обкатан у множества людей и великолепно справляется со своими обязанностями. Я бросил ковырять php-fpm+apc — и начал настраивать с самого «переднего» края — с сетевого стека операционной системы. Рекомендаций в сети много, всё подробно расписано — повторять не буду. Настроил, проверил, nginx стал шевелиться гораздо быстрей, да и mysql чуток ускорился.
Далее nginx — уменьшил кол-во воркеров до 1 и конекшенов до 8196. Включил sendfile и aio, проверил величину directio. Подкрутил output_buffers согласно мануалу. И вот! Я получил на статике чуть больше 60 r.p.s. Я подумал, что первая победа есть и нужно продолжать. Аналогичным образом прошелся по php-fpm (пока не трогая apc). Конекшены, лимиты. Затем mysql. В этом мне помог mysqltuner.pl и конечно оф.мануал. После увеличения кешей, и уменьшения кол-ва конекшенов вся работа
Однако, php-fpm продолжал периодически падать. Много читал ссылки в гугле. APC бажный — но он и такой хорош. Вернемся к нему чуть позже.
В качестве промежуточного решения я решил попробовать поставить на все WordPress плагин кеширования — W3-Total-Cache. На будущее хотелось использовать nginx fastcgi cache с модулем cache purge (попробовал — не понравилось, ниже напишу почему). W3TC было выбрано как “быстро-поставил-перед-сном», да и в моем случае – это было оптимальное решение. Если бэк-энд упадет – фронт-энд будет кормить людей статикой (но не все так гладко).
Готовим W3TC следующим образом — Page cache: Disk Enchanced, Database cache — APC, Object cache — APC. Expire — по вкусу. На кодексе вордпресса нашел полурабочий конфиг для раздачи page cache из W3TC напрямую с фронт-энда (через nginx не запуская php-fpm), доработал его. Рестарт и только тут я начинаю понимать, что значит, когда сайты работают быстро. Статика улетает из-под nginx'а ну очень быстро. На чистой статике я получал в ab2 до 800 (!!!) r.p.s.
Конечно, когда я сел писать этот текст — я уже знаю, что ab2 это плохо, а siege — это хорошо. Но на тот момент я этого не знал. И всё же! Сравните начальный показать 1.4 и промежуточный 800! Но на этом всё не закончилось, а только началось! Но только на следующее утро.
Проснулся утром от очередной СМСки Яндекса, что сайты давненько в ауте. php-fpm не отвечал на запросы фронт-энда. В результате долго ковыряния, перезапусков и перенастроек выяснилось, что чистка user cache в apc вызывает какой-то мертвый лок в результате чего, php-fpm перестает обрабатывать запросы. И вот тут я подошел к тому, что на
php-fpm ПОЧТИ перестал лочиться, но это почти. Меня не устраивало — я же не могу постоянно сидеть и проверять, работает он или нет. Да и как так — вроде продакшн решение (малюсенькое, но все же), а бэк-энд падает. Плохо. Скачал сорцы последнего APC, компилил с разными опциями. Наибольшую производительность показали Spinlocks, но они и самые падучие (экспериментальные всё ж таки). В общем, на дэдлоки при чистке user cache (db, object в контексте wp+w3tс) это никак не повлияло, как висло так и виснет. В итоге, user cache лежит на memcached, и ведёт себя шустро.
Что я только не пробовал. Остановился по итогу на APC с mmap memory и pthread mutex Locks locking. Написал небольшой скриптик для крона, который раз минуту проверяет живость php-fpm. И поставил fastcgi timeout в nginx в 2 минуты. Пока вдруг тупняк – php-fpm упал – пользователь чуточек пождет (с учетом моей аудитории). Другого решения пока не нашел — если кто сталкивался или знает — предлагайте.
Отдельная песня — это nginx fastcgi cache. После его инициализации nginx cache manager скушал 60Мб памяти из общих 256, показал производительность ниже, чем просто nginx + w3tc (pagecache disk enhanced) и был благополучно отключен.
На сейчас я остановился на следующем софте: nginx 1.3.8, php 5.3.19 (+php-fpm), apc 3.1.19, memcached 1.4.15, mysql 5.0.95 обслуживают несколько одиночных WordPress и одну WordPress Network c активированным плагином W3 Total Cache
PHP-сессии находятся в memcached, user cache (db, object) -> memcached, apc.XXXXXX в /dev/shm/, apc работает исключительно как оптимизатор и кешер опкода php.
ab2 на полностью кэшированных сайтах выдает около 800 r.p.s. на блочно кешированных около 200 r.p.s. (округлено в меньшую сторону).
Меня реально утомил постоянно падающий PHP-FPM. Но сегодня, всю ночь у меня был потрясающий сексразбор ключей конфигурации перед компиляцией PHP 5.4.9. Пока я разобрался как собирать его, и его модули из исходников — думал, крыша съедет. Еле собрал, запустил. Сайты работают. User cache в APC не вызывает дэдлок. На момент апдейта публикации — PHP-FPM Uptime 9 hours and 15 minutes без единного нарекания. Однако, я так и не смог понять, как можно скомпилировать PHP в минимальной рабочей сборке под WordPress. Сейчас все модули вкомпилены в сам PHP и я не знаю, как правильно их выключить. Уважаемое сообщество, подскажите пожалуйста, как это можно сделать? Какие реально модули нужны для оптимальной работы WordPress с максимальной минимизацией бинарников PHP?
#!/bin/sh
POOL=`curl --retry 3 --retry-delay 1 --connect-timeout 2 --max-time 2 --silent http://127.0.0.1/fpmstatus | grep pool | awk '{ print $2 }'`
if [ "$POOL" != "www" ]
then
echo -------------------------------------------------------------------------- >>/var/log/php-fpm/restarts.log
echo `date` PHP-FPM $host:$port is DOWN >>/var/log/php-fpm/restarts.log
echo `date` first try sigterm PHP-FPM... >>/var/log/php-fpm/restarts.log
killall -9 php-fpm >>/dev/null
sleep 5
echo `date` second try sigterm PHP-FPM... >>/var/log/php-fpm/restarts.log
killall -9 php-fpm >>/dev/null
sleep 5
echo `date` restarting PHP-FPM NOW >>/var/log/php-fpm/restarts.log
/etc/init.d/php-fpm restart >>/dev/null
echo -------------------------------------------------------------------------- >>/var/log/php-fpm/restarts.log
else
sleep 1
fi
exit
Два килла используется потому что бывает так повиснет, что только со второго раза сносится.
Внимание! php-fpm и nginx должен быть настроен для корректной работы location /fpmstatus
extension = apc.so
apc.enabled=1
apc.shm_size=128M
apc.num_files_hint=0
apc.user_entries_hint=0
apc.ttl=0
apc.use_request_time=1
apc.user_ttl=0
apc.gc_ttl=3600
apc.cache_by_default=1
apc.filters
apc.mmap_file_mask=/dev/shm/apc.XXXXXX
apc.file_update_protection=10
apc.enable_cli=0
apc.max_file_size=1M
apc.stat=1
apc.stat_ctime=0
apc.canonicalize=1
apc.write_lock=0
apc.rfc1867=0
apc.rfc1867_prefix =upload_
apc.rfc1867_name=APC_UPLOAD_PROGRESS
apc.rfc1867_freq=0
apc.rfc1867_ttl=3600
apc.include_once_override=0
apc.lazy_classes=0
apc.lazy_functions=0
apc.coredump_unmap=0
apc.file_md5=0
apc.localcache=1
apc.localcache.size=1024
worker_processes 1;
timer_resolution 100ms;
worker_rlimit_nofile 4096;
worker_priority -5;
events {
worker_connections 8196;
use epoll;
}
sendfile on;
aio on;
directio 5m;
output_buffers 8 128k;
tcp_nopush on;
tcp_nodelay on;
server_tokens off;
reset_timedout_connection on;
open_file_cache max=8192 inactive=60s;
open_file_cache_valid 60m;
open_file_cache_min_uses 2;
open_file_cache_errors on;
map $host $sid {
wp-net.su -999;
forum.wp-net.su 2;
about.wp-net.su 4;
info.wp-net.su 5;
test.wp-net.su 6;
}
server {
listen IP.IP.IP.IP:80;
server_name wp-net.su *.wp-net.su;
access_log /var/log/nginx/nginx_($host)_access.log;
error_log /var/log/nginx/nginx_(ALL.wp-net.su)_error.log error;
root /var/www/vhosts/wp-net.su;
index index.php;
location = /favicon.ico { log_not_found off; access_log off; }
location = /robots.txt { allow all; log_not_found off; access_log off; }
location ~ /. { deny all; }
location ~* /(?:uploads|files)/.*.php$ { deny all; }
if ($request_uri ~* "/files/(.*)"){ set $rtfile $1; }
location ^~ /files {
try_files /wp-content/blogs.dir/$sid/$uri /wp-includes/ms-files.php?file=$rtfile;
expires 7d;
}
location ^~ /blogs.dir {
internal;
alias /var/www/vhosts/wp-net.su/wp-content/blogs.dir;
access_log off;
log_not_found off;
expires 7d;
}
location ~* ^.+.(js|css)$ { expires 2d; }
location ~* ^.+.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
expires 7d;
}
set $cache_uri $request_uri;
if ($request_method = POST) { set $cache_uri 'null cache'; }
if ($query_string != "") { set $cache_uri 'null cache'; }
if ($request_uri ~* "(/wp-admin/|/xmlrpc.php|/wp-(app|cron|login|register|mail).php|wp-.*.php|/feed/|index.php|wp-comments-popup.php|wp-links-opml.php|wp-locations.php|sitemap(_index)?.xml|[a-z0-9_-]+-sitemap([0-9]+)?.xml)") { set $cache_uri 'null cache'; }
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_logged_in") { set $cache_uri 'null cache'; }
location / {
try_files /wp-content/w3tc-$host/pgcache/$cache_uri/_index.html $uri $uri/ /index.php?$args;
}
location ~ .php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+.php)(/.+)$;
include /etc/nginx/fastcgi.conf;
fastcgi_connect_timeout 1800s;
fastcgi_read_timeout 1800s;
fastcgi_send_timeout 1800s;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_intercept_errors off;
fastcgi_pass phpfpm;
}
}
[mysqld_safe]
key_buffer=8M
max_allowed_packet=1M
max_heap_table_size=24M
table_cache=1024
sort_buffer_size=512K
read_buffer_size=512K
read_rnd_buffer_size=2M
net_buffer_length=20K
thread_stack=640K
thread_cache=4
tmp_table_size=8M
query_cache_limit=8M
query_cache_size=16M
skip-locking
skip-networking
skip-bdb
skip-innodb
skip-thread-priority
old-passwords=0
thread_concurrency=1
max_connections=16
Господа специалисты, я понимаю, критиковать часто приятно, однако я не специалист, и делюсь даже не столько своими наработками с новичками, а показываю, как можно нащупать путь, удобный каждому. Хотя конечно — критикуйте! И особенно полезны будут дельные рекомендации сообщества — хочу попробовать, потестировать на этом
Хорошего вам настроения
Автор: olo
Источник [2]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/mysql/22425
Ссылки в тексте:
[1] VPS: https://www.reg.ru/?rlink=reflink-717
[2] Источник: http://habrahabr.ru/post/162295/
Нажмите здесь для печати.