В рамках работ по оптимизации работы ресурса продолжаем чудить.
И сегодня мы попробуем убрать c нашего вебсервера самое святое — апач.
В прошлых статьях (1) (2) мы уже выполнили работу по распределению нагрузки между nginx, умеющим быстро отдавать статический контент, и apache, умеющим обрабатывать контент динамический. Для большинства CMS этого должно быть достаточно — при правильном подборе параметров в apache2.conf все летает довольно шустро. Однако, в Worpress eсть одна прекрасная особенность — он писался для обеспечения максимальной простоты, расширяемости и совместимости, поэтому код его не оптимизирован под быстроту выполнения.
Я не считаю себя профессионалом в программировании, но запросы вордпресса в БД вводят меня в ступор:
SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts WHERE 1=1 AND wp_posts.post_type = 'post' AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private') ORDER BY wp_posts.post_date DESC LIMIT 0, 10
В чем сакральный смысл условия 1=1?
Почему нельзя нормализовать таблицы, избавившись от текстового поиска?
На практике же все эти «архитектурные особенности» выливаются в следующее: при большом количестве запросов (я прочувствовал это, когда ко мне в гости пришел какой-то ботнет из Китая) форки апача загоняют процессор (C2D E8400) в полку, а после завершения некоторые из них зомбифицируются.
Пользователь при такой ситуации либо будет довольно долго ждать прогрузки страницы, либо получит error 503 от nginx.
Альтернативой этому может быть либо установка апача в режим worker (тогда он будет создавать не процессы, а потоки, которые завершаются относительно безболезненно), либо установка альтернативного back-end для обработки динамического контента.
Я выбрал для этих целей модуль php-fpm.
Установка
Пакет входит в репозитарии Ubuntu, поэтому установка производится одной коммандой:
sudo apt-get install php5-cli php5-common php5-mysql php5-gd php5-cgi php5-fpm php-pear php5-mcrypt
Настройка php-fpm
Внесем необходимые изменения в конфигурацию php-fpm:
mcedit /etc/php5/fpm/php.ini
cgi.fix_pathinfo = 0 #общая рекомендация для устранения проблемы с безопасностью #Изменим макс размер загружаемых файлов post_max_size = 20M upload_max_filesize = 20M
Отредактируем конфигурацию пула:
mcedit /etc/php5/fpm/pool.d/www.conf
security.limit_extensions = .php .php3 .php4 .php5 #ограничит выполнение файлов по расширению имени
listen = /var/run/php5-fpm.sock #определяем сокет для связи nginx с php-fpm
listen.owner = www-data #Пользователь которому разрешено слушать сокет. Тот же, что и у nginx.
listen.group = www-data
listen.mode = 0660
pm.max_children = 7 #Максимальное кол-во процессов, которые может запустить инстанс php-fpm
pm.start_servers = 3 #Максимальное кол-во процессов php-fpm, запускаемых при старте
Перезапустим php-fpm:
/etc/init.d/php-fpm restart
Настройка nginx
Добавим новый инстанс сайта и настроим его:
mcedit /etc/nginx/sites-available/blog.sigillite.net
Приведем конфиг к виду:
server { listen 80; root /var/www/sitepath; index index.html index.htm index.php; server_name blog.sigillite.net www.blog.sigillite.net; access_log /var/log/nginx/accesslogpath.log; error_log /var/log/nginx/errorlogpath.log; location / { try_files $uri $uri/ /index.php?q=$uri&$args; } location ~ .php$ { #Раньше здесь мы отправляли все php-файлы на обработку апачу #Теперь нужно отправлять на php-fpm fastcgi_buffers 8 256k; fastcgi_buffer_size 128k; fastcgi_intercept_errors on; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; #Укажем путь к сокету, который мы прописывали в настройках пула php-fpm fastcgi_pass unix:/var/run/php5-fpm.sock; } location ~* .(css|js|png|jpg|jpeg|gif|ico)$ { expires 1d; } location ~* "/.(htaccess|htpasswd)$" { deny all; # запретить чтение файлов .ht* return 404; # вернуть код ошибки }
Добавим сайт в активные и перезапустим nginx:
ln -s /etc/nginx/sites-available/blog.sigillite.net /etc/nginx/sites-enabled/blog.sigillite.net /etc/init.d/nginx restart
Теперь по адресу blog.sigillite.net доступна версия этого блога, где в качестве back-end используется php-fpm
Сравнения, итоги, замечания
Потребление памяти у php-fpm на 1 соединение на 10% выше, чем у apache:
nginx+apache:
В отличии от apache, php-fpm
- не создает зомби-процессов
- быстро завершает неиспользуемые процессы
- потребляет меньше процессорного времени на 1 форк
Визуально выглядит так: при нагрузочном тестировании сайта (c помощью LoadImpact, например) апач выдерживает 198 одновременных соединений, после чего процессор уходит в полку, php-fpm выдерживает 250 одновременных соединений при уровне загрузки процессора около 90%.
После спада нагрузки php-fpm закрывает форки моментально, апач — в течение 20-30 секунд.
Функционал
Несмотря на все достоинства, php-fmp имеет 1 существенный недостаток: у него нет mod_rewrite. Т.е. .haccess — не работает, и соответственно все плагины для WP, которые его используют для работы (а это многие кешеры\ускорители\обезопасиватели) — тоже.
Все правила, описанные в .htaccess необходимо описать непосредственно в конфиге сайта nginx, преобразовав перед этим в понятный ему формат. Для преобразования синтаксиса существует онлайн-конвертор, проще всего выносить содержимое .htaccess в отдельный файл, который подключается к конфигу сайта директивой include. Таким образом, если сайт уже настроен, все плагины подключены и .htaccess модифицироваться не будет — можно конвертировать его, инклюдить и получать тот же функционал, что и при использовании апача с mod_rewrite.
Ссылки по теме: