Хроники переезда: оптимизация (часть 2)

В рамках работ по оптимизации работы ресурса продолжаем чудить.

И сегодня мы попробуем убрать 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:

Буфер обмена03nginx+php-fpm:

Буфер обмена04Стабильность работы:

В отличии от 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.

 

Ссылки по теме:

На сайте убунту

На хабре

На howitmades

На manualpages

 

Добавить комментарий

Войти с помощью: