Опыт оптимизации окружения VPS за $10 для персональной сети WordPress

Эта небольшая статья посвящена описанию моих действий по приведению VPS за $10/месяц в приемлемое состояния для работы персональной сети сайтов на WordPress в одиночном и сетевом режиме (WordPress Network Mode).

Интро


Итак, дорогие мои хай-тек специалисты и случайные читатели, история эта началась в конце апреля 2012 года, когда мой любимый городской провайдер-монополист наотрез отказал мне в дальнейшем размещении моего трех-ядерного атлона в своей стойке.

Я живу в маленьком курортном городке (3 тыс.мы-местные и 50 тыс.понаехали-тут-летом) и большинство моих персональных проектов посвящено родному и любимому городу. Пара городских вебкамер, погода, справочники и т.д. Кроме того, я ещё чуток борюсь за справедливость и права: организовал небольшой проектик на домене TV — снимаем репортажи про безпредел чиновников и т.д...

Так вот о чем это я… ах да! Стоял, значит, двух-юнитовый серверочек (его и сервером тяжко-то назвать) совершенно бесплатно в стойке у провайдера, у которого стоек было аж 3 и 2 из них стояли пустые. Почему же бесплатно? Потому что этому самому провайдеру я помогал развивать корпоративные сайты. Кроме того, других людей, с аналогичными моим знаниями в нашем маленьком городке просто нет — далекая периферия, умные быстро уезжают в Москву и другие зарубежья.

В общем, крутились на AthlonX3 / 4Gb RAM мои проекты и я совершенно не заморачивался по вопросу оптимизации, наладки — делал себе сайтики на любимом wordpress и даже не думал, что когда-нибудь халява и кончится.

После очередной публикации любительского репортажа-недокомпромата на местного мэра ко мне подошел директор провайдера (из той же партии что и мэр) и порекомендовал в течении суток забрать информацию с сервера. Компроматики-то на нём лежали. Ещё хорошо, что подошел и сказал.

Суток уже не оставалось. А проектики мои уже и в SERP'е хорошо сидели, и позиции терять не хотелось… Суть — за 12 часов был найден первый попавшийся дешевый VPS за $10 в Германии (только лишь бы не в моей стране), софт поставлен из репозиториев какой попало, данные были перелиты, DNS'ы перепрописаны и с горем пополам все более менее работало, правда еле еле шевелилось.

Кстати, ограничение в $10 взялось не просто так — именно такая сумма у меня была на тот момент на webmoney, накапало в Директе. Собственно, этот бюджет поднялся всего на $2 — за дополнительный IP. Сейчас мне этот VPS обходится в $12.

Лишь бы поисковики видели


Параметры VPS: CentOS 5, XEN, 2.8Ghz, 256 Mb RAM, 15 GB SATA, траф — безлимитный. Второпях было установлено nginx, apache+mod_php+eaccelerator, mysql, memcache. Представьте себе — время тикает, надо перелить около 12 гиг из одной страны в другую, а ещё поставить, настроить, и желательно так что бы поисковики ничего не поняли :)

Я вроде как и не программист — так себе, быдлоламо-кодер, и не администратор — знаю 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 и конечно оф.мануал. После увеличения кешей, и уменьшения кол-ва конекшенов вся работа VPS значительно ускорилась, load average упал, освободилось треть памяти.

Однако, 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 перестает обрабатывать запросы. И вот тут я подошел к тому, что на VPS стояло, но не использовалось должным образом. Memcached и соответствующее расширение для php — memcache. Database cache и Object cache в W3TC был срочно перенастроен на Memcached. Я конечно читал, что memcached на локалхосте не даёт прироста производительности по сравнению с disk cache, однако в моём случае прирост был очень ощутим. Без тестов — визуально. Видимо это связано со значительным IO на VPS.

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. (округлено в меньшую сторону).

Обновление от сегодня, 12.12.2012

Меня реально утомил постоянно падающий PHP-FPM. Но сегодня, всю ночь у меня был потрясающий сексразбор ключей конфигурации перед компиляцией PHP 5.4.9. Пока я разобрался как собирать его, и его модули из исходников — думал, крыша съедет. Еле собрал, запустил. Сайты работают. User cache в APC не вызывает дэдлок. На момент апдейта публикации — PHP-FPM Uptime 9 hours and 15 minutes без единного нарекания. Однако, я так и не смог понять, как можно скомпилировать PHP в минимальной рабочей сборке под WordPress. Сейчас все модули вкомпилены в сам PHP и я не знаю, как правильно их выключить. Уважаемое сообщество, подскажите пожалуйста, как это можно сделать? Какие реально модули нужны для оптимальной работы WordPress с максимальной минимизацией бинарников PHP?

вывод php -m
# php -m
[PHP Modules]
apc
bcmath
calendar
Core
ctype
curl
date
dom
ereg
exif
fileinfo
filter
ftp
gd
gettext
hash
iconv
intl
json
libxml
mbstring
mcrypt
memcache
mhash
mysql
mysqli
mysqlnd
openssl
pcre
posix
Reflection
session
SimpleXML
sockets
SPL
standard
sysvmsg
sysvsem
sysvshm
tokenizer
xml
zip
zlib


Вечернее дополнение того же дня:

Благодаря указанию aktuba, я обратил внимание на важность реакции сервера на заголовоки If-Modified-Since или If-None-Match. У меня ни nginx ни W3TC почему-то не отдавали в соответствующих случаях 304 Not Modified. По итогу, я добавил в контекст server конфигурации nginx для одного WP-сайта директивы: if_modified_since before; etag on; Это весьма положительно повлияло на снижение количества запросов к серверу. Посмотрю по статистике запросов и включу это на всех сайтах.

Кроме того, в контекст events конфигурации nginx была добавлена директива worker_aio_requests 128; для увеличения производительности aio.

На синтетике ab были получены следующие результаты:
ab -n 10000 -c 1000 http://*.org/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking *.org (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:        nginx
Server Hostname:        *.org
Server Port:            80

Document Path:          /
Document Length:        22307 bytes

Concurrency Level:      1000
Time taken for tests:   4.812 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Total transferred:      229199440 bytes
HTML transferred:       226639120 bytes
Requests per second:    2078.22 [#/sec] (mean)
Time per request:       481.181 [ms] (mean)
Time per request:       0.481 [ms] (mean, across all concurrent requests)
Transfer rate:          46516.29 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        6  150  69.8    147     309
Processing:   105  288  70.8    276     600
Waiting:        5  144  50.0    145     511
Total:        215  439  93.6    448     783

Percentage of the requests served within a certain time (ms)
  50%    448
  66%    467
  75%    477
  80%    498
  90%    541
  95%    629
  98%    634
  99%    774
 100%    783 (longest request)


Как видно, значительно ускорилась отдача статики. Это отлично, даже более чем. Хотя, в данном случае я ещё грешу на тот факт, что это может быть был разгружен IO в ноде в эти 4 секунды, но это наврядли. Цикл аналогичных тестов подтвердил оную картину.


Аттач:

crontab bash-скрипт для проверки 'адекватности' php-fpm с дефолтным пулом www
#!/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

apc.ini
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=/apc.shm.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


выдержки из nginx.conf (которые оч.глубоко в гугле...)
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;


wp-net.conf (для работы Wordpress Network с w3tc page disk cache, конф подключается к nginx)
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;
        }
}


важное из /etc/my.cnf
[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



Господа специалисты, я понимаю, критиковать часто приятно, однако я не специалист, и делюсь даже не столько своими наработками с новичками, а показываю, как можно нащупать путь, удобный каждому. Хотя конечно — критикуйте! И особенно полезны будут дельные рекомендации сообщества — хочу попробовать, потестировать на этом VPS, и дополнить статью если будет прирост.

Хорошего вам настроения
Поделиться публикацией
Похожие публикации
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама
Комментарии 79
  • +6
    Бред какой-то. У вас что-то с диском, раз nginx выдает 1 респонс в секунду на статику. И что от тюнинга сети вдруг все залетало. На моей VPS, которая, к слову, сильно дешевле вашей, обрабатываются 100+ php-запросов без проседаний.
    • 0
      Хм, касательно статики прошу посмотреть сей лог, минуту назад как из консоли:
      ab -n 10000 -c 1000 http://*.org/
      This is ApacheBench, Version 2.3 <$Revision: 655654 $>
      Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
      Licensed to The Apache Software Foundation, http://www.apache.org/
      
      Benchmarking *.org (be patient)
      Completed 1000 requests
      Completed 2000 requests
      Completed 3000 requests
      Completed 4000 requests
      Completed 5000 requests
      Completed 6000 requests
      Completed 7000 requests
      Completed 8000 requests
      Completed 9000 requests
      Completed 10000 requests
      Finished 10000 requests
      
      
      Server Software:        nginx
      Server Hostname:        *.org
      Server Port:            80
      
      Document Path:          /
      Document Length:        22307 bytes
      
      Concurrency Level:      1000
      Time taken for tests:   4.959 seconds
      Complete requests:      10000
      Failed requests:        0
      Write errors:           0
      Total transferred:      228906082 bytes
      HTML transferred:       226318546 bytes
      Requests per second:    2016.42 [#/sec] (mean)
      Time per request:       495.928 [ms] (mean)
      Time per request:       0.496 [ms] (mean, across all concurrent requests)
      Transfer rate:          45075.28 [Kbytes/sec] received
      
      Connection Times (ms)
                    min  mean[+/-sd] median   max
      Connect:        0  175 220.2    126    1195
      Processing:    13  296  98.9    323     498
      Waiting:        2  141  77.2    122     347
      Total:        149  471 252.1    457    1564
      
      Percentage of the requests served within a certain time (ms)
        50%    457
        66%    482
        75%    492
        80%    514
        90%    616
        95%    709
        98%   1458
        99%   1554
       100%   1564 (longest request)
      



      Относительно Вашего VPS и его настройки, не могли бы Вы поделиться своими наработками, ибо 100+ php-запросов без проседаний, на мой взгляд, отличный результат. Если у вас WordPress и на дешевом VPS — Вы отлично поработали и мне хотелось бы научиться у Вас такому мастерству. И если Вас не затруднит, было бы интересно увидеть, что за VPS и почем.
      • 0
        У меня не wordpress, а проект на codeigniter. И, вроде, ничего особо не настраивал для производительности. У вас с нодой что-то конкретно не так, что nginx выдает так мало статики.
        • 0
          Спасибо за ответ, он заставил меня задуматься о дальнейших исследованиях настроек nginx. Дело в том, что я не смог найти VPS дешевле $10 в месяц (со вторым IP получается $12 в месяц) и считал, что это нормальное поведение для него. Вы меня озадачили тем, что нужно как-то выяснить, как ведет себя эта XEN-нода (я правильно назвал?), только вот я не знаю как. Гугл-обучение мне в помощь, но это будет позже. Конечно, после выяснения всех нюансов я дополню статью свежими изысканиями. Вы не могли бы подсказать, где можно посмотреть предложения по VPS дешевле моего?
        • 0
          Относительно codeigniter не сталкивался, но слышал. Но, уверен, у WordPress во много раз большие аппетиты. Например, установленные на этом же VPS DataLife Engine и OpenCart (с sample-content) выдают примерно 30-50 r.p.s. в моих пробах с siege. Но и это, я уверен, весьма жалкий показатель, и он сейчас таков потому, что php весьма «тучен» (вкомпилена куча модулей в него и я не могу понять, как их грамотно отключить, так что бы ничего не поломать, надо ещё читать и читать). По результатам перекомпиляции PHP в будущем обязательно сделаю апдейт статьи.
          • 0
            Аппетиты вордпресса сильно зависят от разработчика. Скажем достался мне один проект как-то на «пофиксить» — там вывод каждой строки вызывал запрос к базе. В среднем страница генерилась 2 секунды. Потратив пару часов + установив плагин кэша прожорство уменьшилось в 4 раза, и уверен там можно было еще что-то сделать.

            Но я все же предпочитаю проекты на фреймворках (CI, Yii, Symfony2) с которыми и работать приятнее, и с производительностью проблем нету (если и есть то это исключительно вина разработчика).
            WP же использовал раньше только для простеньких сайтов визиток только из-за удобной админки. Но если логика сайта посложнее сайта визитки или там добавляются какие-то плюшки, дешевле и быстрее написать узкоспециализированную CMS под эти задачи на базе популярного фреймворка.
            • 0
              Благодарю за рекомендации. Я начинаю присматриваться к Yii. И я согласен с Вами относительно WordPress'а. Однако я никогда не думал, что сайты о небольшом курортном городке будут популярны так, как сейчас, поэтому и делал на WordPress.

              На мой взгляд, сейчас имеет смысл на примере WP провести полную оптимизацию системы в целом, что бы было достаточно «граблей и шишек» в этом отношении (я не нанимаю администратора с его кучей «шишек», всё своими силами). А после тюнинга можно будет вплотную занятся проектами, тем более, как оказалось, народ интересуется.
      • 0
        Давненько читал её в оригинале. Единственное, что в ней понравилось — это вариант отдачи кешированных гзиппованных страниц. Обработке зарегистрированных пользователей там совершенно не уделено внимания. До Варниша пока не добрался — поковыряю его, как время будет. Спасибо за ссылку на русский перевод.
      • 0
        По-моему логичнее было указать каталог /tmp вместо /dev для параметра mmap_file_mask
        • 0
          Насколько я понял, /dev/shm/ — это shared memory (конечно я могу ошибаться). Но судя по операциям — гораздо шустрей диска.
          • +1
            В таком случае, судя по документации, нужно писать apc.mmap_file_mask=/apc.shm.XXXXXX
            • 0
              Да, вы конечно правы. Перепроверил документацию, исправляю на VPS и в статье.
        • 0
          Проверяйте скорее скорость файловой системы:
          dd if=/dev/zero of=olo_test bs=64k count=16k conv=fdatasync

          Что-то не так в вашем десятидолларовом королевстве :(
          • 0
            Благодарю за помощь. Вот результат.
            # dd if=/dev/zero of=olo_test bs=64k count=16k conv=fdatasync
            16384+0 записей считано
            16384+0 записей написано
            1073741824 bytes (1,1 GB) copied, 9,16004 секунд, 117 MB/s
            

            Как Вы его оцените? Я, например, довольно слабо себе представляю, что такое 117 MB/s в понятиях «очень медленно» | «медленно» | «нормально» | «быстро» | «очень быстро». Перед запуском команды я бегло просмотрел man. Насколько я понял, последняя цифра и есть приговор. Теперь оценить бы его.

            Хотя (включаю мозг), гиг за 9 секунд, вроде вполне себе прилично.

            P.S. «десятидолларовом королевстве» — классная фраза, очень понравилась :)
            • 0
              Да, нет — со скоростью файловой системой всё нормально.
              где-то между «нормально» и «быстро».
              • 0
                Можете прогнать тесты с serverbear.com/add-benchmark (предупреждаю, это займёт время — в моём случае заняло чуть меньше часа). Там же можно удобно выбрать сервер по параметрам (стоимость, память, расположение и т.д.).
                • 0
                  Благодарю за рекомендацию. Конечно, в данном случае хотелось бы собственных шишек. Ресурс Вами предложенный изучу безотлагательно.
            • +1
              Интересно вы apc вы размещаете в памяти и выдаете ему 120Мб если у вас всего в системе 256 Mb RAM.
              С ума можно сойти с такими ресурсами выжимать все, гораздо проще взять у тех же фаствпс тариф с 1200 Mb 1700 Mhz за те же деньги, чем заставлять систему постоянно драться за ресурсы.
              • 0
                Вы, видимо, очень избалованы оперативкой. Нормально написанные CMS без труда работают на VPS с 128мб оперативкой, и еще и остается.
                • +1
                  Сферические VPS в вакууме =) А если база уже 2Гб?)
                  Я сторонник того, что быстрее деньги заработать на более дорогой тариф, чем тратить значительно большее время на ужимки и пользователей мучить долгой отдачей страниц и нестабильным поведением сайта в пики.
                  Тоже так развлекался с зажатыми ресурсами и оптимизацией под них, но это интереса ради, если сайт уже бизнес, то это выходит себе дороже.
                  • +2
                    А если база уже 2ГБ, то и не нужно покупать VPS за $10.
                    • 0
                      Сферические базы в 2GB :) Что вы туда положили, картинки?!
                      Смотрю на одном из своих сайтов база почти в 1 млн. записей, занимает меньше 200MB…
                      • 0
                        Нет, флудеры, комменты + личка = 1.1Гб в базе.
                      • 0
                        До бизнеса моим сайтам ещё очень далеко (с Директа капает $15 в месяц). А клиентский мизер может свободно уехать на какой-нибудь шаред-хостинг и жить на кешах, или вообще на статике. Его все равно никто годами не обновляет.
                    • 0


                      Может я не прав, но мне кажется драка за ресурсы не такая уж активная, а как Вы считаете?

                      После установки PHP из сорцов значительно вырос показатель. Наверняка надо пересобирать PHP с вырезанием ненужных библиотек. Но я не знаю, как это сделать так, что бы все работало корректно.
                      • 0
                        Мне интересно больше на сколько система в swap уходит.
                        • 0
                          Подавляющее количество времени это выглядит так:





                          Я уверен, что это из-за объема бинарника PHP.
                          • 0
                            Все не так и плохо, интересно если вообще с swap уходить не будет на сколько быстрее будет.
                            • 0
                              В текущей ситуации — в свап будет валиться в любом случае. На данный момент отдача практически постоянно кешированная. Я уверен, вся проблема в php, который надо перекомпилить с вырезанием ненужных модулей.

                              Сейчас это выглядит так:
                              1355347892-clip-4kb
                    • 0
                      Сколько усилий, лишь бы не 512 мегов оперативы :)
                      • 0
                        К сожалению, моё хобби пока само себе не заработало на 512 мегов. Но я думаю, в скором времени этот рубеж в Директе будет пройден. В любом случае, опыт оптимизации будет полезен при любой конфигурации, Вы так не считаете?
                        • 0
                          Есть куча VPS-ок где за те же 10 баксов >512 оперативки.
                          • 0
                            А как с вопросом стабильности, надежности и постоянства? У Inferno на searchengines хорошие отзывы.
                            • 0
                              Пока не жалуюсь. Для серьезных проектов не подходит конечно, но оно и понятно. Серьезные проекты не хостятся на VPS-ке за 10 баксов. А так для странички/бложика/сайта визитки вполне подходит.
                              • 0
                                Мое мнение может покажется несколько странным, но пока проект не обрел бизнес-модель — это хобби. А Вы на каких VPS-ках бложики держите? Ссылка была в тему :)
                    • 0
                      Вечером, после установки worker_aio_requests 128; в контексте events конфига nginx — вся статика стала откликаться гораздо быстрее. Даже очень.

                      Господа и дамы специалисты, подскажите пожалуйста, как правильно скомпилировать минимализированный PHP? Он у меня слишком большой из-за кучи вкомпиленых модулей. Кушает ресурсов очень и очень много и в этом сейчас я вижу главный затык.
                      • 0
                        Кстати, в документации написано:
                        Для работы AIO нужно выключить sendfile

                        на ваше счастье там есть еще строка
                        Явно выключать sendfile не нужно, так как при использовании directio он выключается автоматически.
                        • 0
                          А какой смысл использовать AIO при отдаче статики? Насколько я помню профит от AIO реально можно получить только при отдаче больших файлов, хотя бы в несколько мегабайт.
                          • 0
                            В документации пример как раз для папки с видео был =)
                          • 0
                            Ооо, благодарю Вас за указание на мою невнимательность.

                            Только что попробовал: при общих равных (с учетом ручного open_file_cache prime) aio проигрывает sendfile в моём случае 400 rps. ~2000 против ~2400. При этом Time per request примерно на 15% ниже (лучше) у sendfile. Отключаем aio, оставляем sendfile.

                            И это во много раз больше того показателя (60 r.p.s.), который я озвучил в статье, как первую «победу». Сейчас я понимаю, что 60 — это просто смешно.
                            • 0
                              AIO с таким конфигом у вас просто не работает. Опять же отсылаю вас к документации: nginx.org/r/aio/ru
                              • 0
                                А как определить, что работает — aio или sendfile?
                                • 0
                                  По размеру запрашиваемого файла. Если вы выставили directio 5m;, то файлы меньшего размера будут отдаваться без использования directio и соотвественно aio в linux.

                                  Поскольку у вас получилось аж 2000 запросов в секунду (2000 * 5 Мб = 10 000 Мб/сек), то я сделал вывод, что тестировали вы на файлах гораздо меньшего размера.
                            • +1
                              Кстати, в документации написано:
                              Для работы AIO нужно выключить sendfile

                              Эта строчка относится к AIO на FreeBSD.
                            • 0
                              А без APC и без других акселераторов тут вообще никак?
                              Уверен на 70%, что причина в нём.
                              Где-то читал тесты всех акселераторов для PHP, где был сделан вывод, что все они так или иначе вешают процессы PHP/apache через какое-то время (в Apache это совершенно не поддажющиеся анализу периодичный Segmentation fault). На низких нагрузках не заметно. На 100к хитах уже очень напрягает (апач приходилось рестартовать скриптом раз в 2 часа).

                              Еще момент:
                              Мало кто пишет и знает, что eAccelerator в mod_php Apache сильно увеличивает размер каждого процесса php. Дело в том, что в таком случае память eaccelerator.shm_size не общая shared (как везде пишут), а для каждого чайлда отдельная. Вроде как, для php-fpm кушается память для каждого воркера (точно чтиал про такое в xcache).

                              В общем, просто попробуйте отключить APC и посомтрите что будет.
                              Не знаю как в APC, но советую последить за размером процессов.
                              • 0
                                На моих проектах APC значительно снижает время работы скрипта + уменьшает потребеление памяти на запрос. Так что думаю увеличение потребления памяти воркерами это не такая уж и большая проблема.
                                • 0
                                  Без акселераторов точно никак. APC однозначно ускоряет работу PHP во много раз. Для примера, генерация главной страницы сайта на домене .TV без акселератора происходит от 19 до 26 секунд (это чуть более 400 запросов в базу, при этом ни один не является медленным). С акселератором (opcode cache) первая генерация (prime opcode cache) — 3-8 секунд, последующие — 0.5-0.8 секунд. Т.е. прирост скорости более 3000%, что является значительным.

                                  Да и Facebook наглядно показывает, как работает APC.
                                  • 0
                                    мне кажется что 400 запросов на страницу это даже для супер-пупер соц-сети на WP многовасто как-то. Быть может неправильная архитектура и не хватает кеширования?
                                    • 0
                                      Вы абсолютно правы относительно количества запросов. Это недопустимо много, и причина кроется в весьма «кривой» используемой теме. Для себя я уже поставил во втором приоритете сделать новую тему «руками». Кроме того, после прохождения мной определенного тренинга я сейчас понимаю, что визуальная подача информации на .TV совершенно не соответствует самой информации, предлагаемой сайтом.
                                • +1
                                  хм, по опыту — оптимизируйте WP, плагины там, тему. Минимальными телодвижениями можно улучшить в разы. По дефолту идет куча обращений в базу, которые легко удаляются, плагины надо пересмотреть — у WP нет никаких политик вокруг плагинов и тем и в результате они написаны ужасно, в основном.
                                  Потом уже под оптимизированный WP надо тюнить систему.

                                  Не берусь судить, но раньше на мелкие VPS ставили FreeBSD, потому как сама операционка была менее требовательная и тюнилась лучше.
                                  • 0
                                    Полностью согласен про WP. Для крупных проектов думаю отказаться от него, а вот для мелких — его следует полностью оптимизировать. Однако вопрос про перекомпиляцию PHP все ещё в силе.
                                    • 0
                                      Прирост от собственной сборки PHP по сравнению с установкой из пакетов есть? Я как-то пробовал на своей локальной машине экспериментировать. Собрал себе nginx свой и свой php. Прирост производительности конечно был но не большой.
                                      • 0
                                        С учетом того, что я не умею правильно собирать PHP (1 раз смог собрать, так что бы сайты работали; все модули вкомпилены внутрь) — прироста не просто нет, есть явное и заметное снижение производительности PHP. И это приоритет номер один — правильно собрать PHP, без лишних модулей.

                                        Но проблема стоит в том, что я не знаю, какие модули правильно будет вкомпилить в PHP, а какие сделать загружаемыми (.so). Мало того, я даже не представляю, какие модули являются реально необходимыми для WP. Именно поэтому я и прошу сообщество Хабра помочь мне в этой нелегкой для меня задаче.
                                        • 0
                                          Насколько я помню всякие штуки как префетчи сильно упрощают жизнь при загрузке библиотек (этим операционная система должна заниматься). Более того, воркеры PHP-FPM уже загрузили нужные библиотеки или нет? Я к сожалению этого не проверял.
                                          • 0
                                            Что такое «префетчи»?

                                            Не совсем понял фразу «воркеры PHP-FPM уже загрузили нужные библиотеки или нет?»
                                            • 0
                                              Во всех нормальных осях реализована предзагрузка в оперативную память наиболее используемых библиотек. Ну мол что бы снизить время запуска. Если честно мало чего могу сказать по этому поводу.

                                              А что касается PHP-FPM… ну как бы фишка FPM что воркеры все время висят в памяти и ждут запросы на обработку. Логично предположить что библиотеки необходимые они загрузили в память сразу же. А расходы на вызовы методов библиотечных настолько несущественны что их можно не учитывать.
                                              • 0
                                                Спасибо за подробный ответ относительно FPM.

                                                Вы не знаете, какие PHP-модули реально нужны исходя из следующего:
                                                # php -m
                                                [PHP Modules]
                                                apc
                                                bcmath
                                                calendar
                                                Core
                                                ctype
                                                curl
                                                date
                                                dom
                                                ereg
                                                exif
                                                fileinfo
                                                filter
                                                ftp
                                                gd
                                                gettext
                                                hash
                                                iconv
                                                intl
                                                json
                                                libxml
                                                mbstring
                                                mcrypt
                                                memcache
                                                mhash
                                                mysql
                                                mysqli
                                                mysqlnd
                                                openssl
                                                pcre
                                                posix
                                                Reflection
                                                session
                                                SimpleXML
                                                sockets
                                                SPL
                                                standard
                                                sysvmsg
                                                sysvsem
                                                sysvshm
                                                tokenizer
                                                xml
                                                zip
                                                zlib
                                                
                                                [Zend Modules]
                                                
                                                

                                                • 0
                                                  Я без понятия что используется в WP. Думаю там не используются рефлексии, думаю что SPL там не используется, но может использоваться в плагинах, ровно как и рефлексии. Один из драйверов mysql точно не используется, какой именно, это уже надо настройки смотреть. Думаю что не используется DOM и SimpleXML (опять же если только плагины используют). Словом если вам так критично потребление памяти, то для того библиотеки эти и выносятся в so файлы, что бы на все процессы в память библиотека загружалась один раз. Тут нужно выбирать что вам нужно, производительность (не думаю что она уж так сильно растет, думаю вам пару милисикунд погоды не сыграют. Намного профитнее тогда уже сессии хранить в памяти, хотя опять же для этого должна быть свободная память.
                                                  • 0
                                                    Ооо, это действительно полезный ответ. Я хотел бы уточнить, правильно ли я Вас понял: если модули вынесены в so — то они загружаются один раз для всех процессов php?
                                                    • 0
                                                      Я конечно могу ошибаться, но вроде как в этом суть Shared Objects (.so) так же как и Dll
                                                      • 0
                                                        Исходя из Ваших слов я перекомпилировал PHP, по максимуму вынес все модули в so. В общем, стало чуть чуть быстрее, но не намного. Следующий и самый важный этап — это оптимизация Wordpress.
                                  • 0
                                    Опыт оптимизации окружения

                                    Где-то тут пропущено слово "неудачный".

                                    Конфиг nginx просто ужасен. Я промолчу про кучу кривых if-ов и регулярок, но вот чем объяснить, например, это: fastcgi_connect_timeout 1800s; — представить даже трудно.
                                    • 0
                                      Наверняка Вы правы. Я был бы Вам признателен, если бы Вы написали, каким образом нужно изменить настройки.

                                      1800s — это вполне актуальный параметр при загрузке пользователем пары сотен мегабайт фотографий. Меньшее значение не давало такой возможности. Я Был бы Вам признателен за помощь в работе над ошибками, это будет весьма полезно.
                                      • 0
                                        Сначала стоит оптимизировать код, снизить нагрузку на базу, уменьшить время генерации страницы, добавить кэшер какой-нибудь ну и т.д., а потом уже заниматься более детальной оптимизацией (параметры PHP-FPM, настройки nginx, настройки MySQL… есть много вещей сильно влюящих на производительность и не требующих таких лютых извращений).
                                        • 0
                                          На данный момент, за счет нескольких уровней кеширования нагрузка на базу значительно упала. В любом случае, там где раньше было около 400 запросов — сейчас выполняется 9-17 запросов, причем довольно быстро. Запросы/результаты/части запросов и результатов кешируются на уровне БД и в APC user cache.
                                        • +1
                                          1. Настройки большинства директив по-умолчанию имеют вполне разумные значения, взятые отнюдь не с потолка, и изменяя их без четкого понимания что вы делаете, и для чего меняете, какую проблему решаете — будет только хуже.
                                          2. Есть же документация, в чем дело? Причем на родном языке: nginx.org/r/fastcgi_connect_timeout/ru Почему никто даже не смотрит в документацию, зато каждый пишет свой «туториал» едва только впервые настроил свой домашний сервер? Она же для вас и пишется. [...] Даже значение по-умолчанию в 60s достаточно велико: в обычных условиях connect() (man connect) выполняется менее чем за единицы миллисекунд. Установив значение в 60 минут вы просто лишили nginx и себя возможности узнать о проблемах с бэкендом. Ни один клиент не будет ждать 60 минут пока сервер сообразит, что «тут уже делать нечего» и вернет 502. В итоге со стороны клиента это будет выглядеть как зависший сервер, а с вашей стороны как клиент оборвавший соединение (ушел на другой сайт или закрыл браузер). То же касается других таймаутов, что вы наоптимизировали. Взять вот fastcgi_read_timeout. Что написано? Процитирую:
                                          Таймаут устанавливается не на всю передачу ответа, а только между двумя операциями чтения.
                                          и это там не сейчас появилось.
                                          • 0
                                            Благодарю за ответ. Я прочитал документацию и несколько раз пробовал менять настройки. Однако, для загрузок файлов не помогало ничего, кроме увеличения соответствующих тайм-аутов.

                                            Но ради интереса я, следуя Вашим словам, закомментировал все fastcgi timeout директивы. В итоге при попытке загрузки пары десятков фотографий всего лишь по 3 Мб я, как и раньше, получаю 502 ошибку строго через 60 секунд. При этом, естественно, фотографии не загрузились и соответствующий скрипт не смог их обработать. У Вас наверняка есть идеи, какие ещё нужно подкрутить настройки, кроме fastcgi тайм-аутов, дабы фотографии загружались и могли быть обработаны с помощью php-fpm?
                                            • 0
                                              Очевидно крутить нужно не nginx, а ваше приложение, в которое всё упирается. И 502 ошибка спустя минуту ясно говорит о том, что у вас с обработкой таких запросов большая проблема.

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

                                              2. Вы в любом случае не можете обработать за единицу времени более фотографий, чем позволяют ваши ресурсы. И заставлять пользователя ждать, пока эти ресурсы найдуться — очень плохая идея. Вам нужно вынести саму обработку в фон. Пусть nginx принимает файл, сообщает об этом бэкенду, а тот ставит задачу в очередь и сразу после этого возвращает 200-ый ответ пользователю, который говорит о том, что файл успешно загружен. А некий отдельный процесс, работающий независимо слушает очередь заданий, и по мере того, как он заканчивает обработку одной фотографии, переходит к следующему заданию. В итоге, и фотографии будут обработаны быстрее, и пользователь может продолжать пользоваться сайтом, или загружать ещё фотографии, не дожидаясь пока они будут обработаны, и сервер будет при этом устойчив к подобного рода нагрузкам.
                                              • 0
                                                Я как раз думал над возможностью такой реализации. Что Вы думаете о nginx upload и nginx upload progress?
                                                • 0
                                                  Оба модуля не имеют к этой задаче никакого отношения. Nginx upload — очень специфический модуль, плохо себе представляю, в каких случаях он требуется. Upload progress — это такой устаревший способ для отображения прогресса загрузки, утратил актуальность с появлением поддержки XMLHTTPRequest2 в большинстве браузеров.
                                                  • 0
                                                    Насколько я понял, Nginx upload может загрузить файлы и только после окончания загрузки обратиться к бэк-энду для дальнейшей обработки. Подробной документации я почему-то не нашел — только примеры реализации. Скажите, насколько я ошибаюсь?
                                                    • 0
                                                      Насколько я понял, Nginx upload может загрузить файлы и только после окончания загрузки обратиться к бэк-энду для дальнейшей обработки.
                                                      Это nginx делает всегда и без этого модуля.
                                                      • 0
                                                        Хм, если это так — значит есть некая проблема при загрузке, которую я пока не могу диагностировать. Спасибо огромное за детальные и очень полезные ответы.
                                      • 0
                                        Интересно узнать, как обстоят дела. Какой опыт появился за это время?

                                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.