Настройка PHP-FPM: используем pm static для максимальной производительности
Неотредактированная версия статьи была изначально опубликована на haydenjames.io и публикуется здесь с разрешения ее автора.
Я в двух словах расскажу, как лучше всего настроить PHP-FPM, чтобы увеличить пропускную способность, снизить задержку и более стабильно использовать процессорные ресурсы и память. По умолчанию строка PM (process manager, менеджер процессов) в PHP-FPM имеет значение dynamic, а если у вас не хватает памяти, то лучше установить ondemand. Давайте сравним 2 варианта управления на основе документации php.net и посмотрим, чем от них отличается мой любимый static pm для большого объема трафика:
pm = dynamic — количество дочерних процессов настраивается динамически на основе следующих директив: pm.max_children, pm.start_servers,pm.min_spare_servers, pm.max_spare_servers.
pm = ondemand — процессы создаются по требованию (в отличие от динамического создания, когда pm.start_servers запускаются при запуске сервиса).
pm = static — количество дочерних процессов фиксировано и указывается параметром pm.max_children.
Подробности см. в полном списке глобальных директив php-fpm.conf.
Сходства менеджера процесса PHP-FPM с регулятором частоты процессора
Это может показаться оффтопом, но я собираюсь связать это с темой настройки PHP-FPM. У кого хоть раз не тормозил процессор — на ноутбуке, виртуальной машине или выделенном сервере. Помните масштабирование частоты процессора? Эти параметры, доступные для nix и Windows, могут повысить производительность и скорость отклика системы, если изменить параметр регулятора процессора с ondemand на performance*. На этот раз давайте сравним описания и посмотрим на сходства:
Governor = ondemand — динамическое масштабирование частоты процессора в зависимости от текущей нагрузки. Резко переходит на максимальную частоту, а затем снижает ее, когда увеличиваются периоды простоя.
Governor = conservative = динамическое масштабирование частоты в зависимости от текущей нагрузки. Увеличивает и уменьшает частоту плавнее, чем ondemand.
Governor = performance — частота всегда максимальная.
Подробности см. в полном списке параметров регулятора частоты процессора.
Видите сходство? Я хотел показать это сравнение, чтобы убедить вас, что лучше всего использовать pm static для PHP-FPM.
Для регулятора процессора параметр performance помогает безопасно увеличить производительность, потому что она почти полностью зависит от лимита процессора сервера. Кроме этого, конечно, есть еще такие факторы, как температура, заряд аккумулятора (в ноутбуке) и другие побочные эффекты постоянной работы процессора на 100%. Настройка performance обеспечивает самую быструю работу процессора. Почитайте, например, о параметре force_turbo в Raspberry Pi, с которым панель RPi будет использовать регулятор performance, где улучшение производительности будет заметнее из-за низкой тактовой частоты ЦП.
Использование pm static для достижения максимальной производительности сервера
Параметр PHP-FPM pm static во многом зависит от свободной памяти на сервере. Если памяти мало, лучше выбрать ondemand или dynamic. С другой стороны, если у вас есть память, можно избежать лишних издержек менеджера процесса PHP, установив pm static на максимальную емкость сервера. Другими словами, если все хорошо подсчитать, нужно установить pm.static на максимальный объем процессов PHP-FPM, которые могут выполняться, не создавая проблем с нехваткой памяти или кэша. Но не так высоко, чтобы перегрузить процессоры и накопить кучу операций PHP-FPM, ожидающих выполнения.
На скриншоте выше у сервера установлено pm = static и pm.max_children = 100, и это занимает примерно 10 ГБ из имеющихся 32. Обратите внимание на выделенные столбцы, тут и так все понятно. На этом скриншоте было примерно 200 активных пользователей (более 60 секунд) в Google Analytics. На этом уровне примерно 70% дочерних процессов PHP-FPM все еще бездействуют. Это значит, что PHP-FPM всегда установлен на максимальный объем ресурсов сервера независимо от текущего трафика. Простаивающий процесс ждет пиков трафика и реагирует моментально. Вам не приходится ждать, пока pm создаст дочерние процессы, а потом завершит их, когда истечет период pm.process_idle_timeout. Я установил очень большое значение для pm.max_requests, потому что это рабочий сервер без утечек памяти в PHP. Вы можете установить pm.max_requests = 0 со static, если полностью уверены в имеющихся и будущих скриптах PHP. Но лучше перезапускать скрипты со временем. Установите большое число запросов, ведь мы хотим избежать лишних издержек pm. Например, хотя бы pm.max_requests = 1000 — в зависимости от количества pm.max_children и числа запросов в секунду.
На скриншоте показана команда Linux top, фильтрованная по u (user) и имени пользователя PHP-FPM. Показано только первые 50 или около того процессов (точно не считал), но, по сути, top показывает топ статистики, которая помещается в окно терминала. В этом случае с сортировкой по % ЦП (%CPU). Чтобы посмотреть все 100 процессов PHP-FPM, выполните команду:
Когда использовать pm ondemand и dynamic
Если использовать pm dynamic, возникают подобные ошибки:
WARNING: [pool xxxx] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning 32 children, there are 4 idle, and 59 total children
Попробуйте изменить параметр, ошибка никуда не денется, как описывается в этом посте на Serverfault. В этом случае значение pm.min было слишком маленьким, а раз веб-трафик сильно меняется и у него есть высокие пики и глубокие спады, сложно адекватно настроить pm dynamic. Обычно при этом используется pm ondemand, как советуют в том же посте. Но это еще хуже, ведь ondemand завершает бездействующие процессы до нуля, когда трафика мало или нет вообще, и в итоге вы все равно будете мучиться с издержками при изменении трафика. Если, конечно, вы не установили огромное время ожидания. И тогда лучше уж использовать pm.static + высокое число pm.max_requests.
PM dynamic и особенно ondemand могут пригодиться, если у вас несколько пулов PHP-FPM. Например, вы размещаете несколько аккаунтов cPanel или несколько веб-сайтов в разных пулах. У меня есть сервер, где, допустим, 100+ аккаунтов cpanel и примерно 200 доменов, и pm.static или даже dynamic меня бы не спас. Тут нужен только ondemand, ведь больше двух третей веб-сайтов получают мало трафика или вообще никакого, а с ondemand все дочерние процессы отвалятся, что сэкономит нам уйму памяти! К счастью, разработчики cPanel это заметили и установили значение по умолчанию ondemand. Раньше, когда значением по умолчанию был dynamic, PHP-FPM вообще не подходил для нагруженных общих серверов. Многие использовали suPHP, потому что pm dynamic потреблял память даже при бездействующих пулах и аккаунтах cPanel PHP-FPM. Скорее всего, при хорошем трафике вы не будете размещаться на сервере с большим числом пулов PHP-FPM (общий хостинг).
Заключение
Если вы используете PHP-FPM и трафик у вас серьезный, менеджеры процессов ondemand и dynamic для PHP-FPM будут ограничивать пропускную способность из-за присущих им издержек. Изучите свою систему и настройте процессы PHP-FPM в соответствии с максимальной емкостью сервера. Сначала задайте pm.max_children в зависимости от максимального использования pm dynamic или ondemand, а затем увеличьте это значение до уровня, где память и процессор будут работать без чрезмерной перегрузки. Вы заметите, что с pm static, раз у вас все хранится в памяти, пики трафика со временем будут вызывать меньше пиков для процессора, а средние значения нагрузки сервера и процессора выровняются. Средний размер процесса PHP-FPM зависит от веб-сервера и требует настройки вручную, поэтому более автоматизированные менеджеры процессов — dynamic и ondemand — более популярны. Надеюсь, статья была полезной.
UPD Добавлена диаграмма бенчмарка ab. Если процессы PHP-FPM находятся в памяти, производительность увеличивается за счет потребления памяти, где они сидят и ждут. Найдите для себя оптимальный вариант.