Оптимизация PHP-FPM: максимальная производительность и pm static

Давайте кратко рассмотрим, как лучше настроить PHP-FPM для высокой пропускной способности, низкой задержки и более стабильного использования процессора и памяти. В большинстве дефолтных настроек PHP-FPM есть строка с PM (process manager), установленным в dynamic, а также рекомендации по использованию ondemand, в том случае если вы столкнулись с проблемами доступной памяти. Однако, давайте взглянем в документацию на php.net и сравним оба варианта управления, а также сравним с моей любимой настройкой под высокую посещаемость… pm = static:

pm = dynamic – количество дочерних процессов устанавливается динамически, основываясь на следующих директивах: pm.max_childrenpm.start_serverspm.min_spare_serverspm.max_spare_servers.

pm = ondemand – процессы плодятся по требованию (при необходимости, в отличие от динамического варианта, где pm.start_servers запускаются при запуске сервиса).

pm = static – количество дочерних процессов фиксировано директивой pm.max_children.

…вы можете посмотреть полный список директив php-fpm.conf для получения дополнительной информации.

Менеджер процессов (process manager) PHP-FPM-а схож с CPUFreq Governor

Это может показаться немного не по теме, но я надеюсь связать его с нашей оптимизацией PHP-FPM. Да, все мы когда-то натыкались на проблемы с производительностью процессора, будь то ноутбук, виртуальная машина или выделенный сервер. Помните масштабирование частоты процессора? (CPUFreq governor) Эти параметры, доступные на *nix и Windows, могут повысить производительность и отзывчивость системы путем изменения настройки CPU governor с ondemand на performance. Сейчас давайте сравним описания и поищем сходства:

Governor = ondemand – Динамически увеличивает/уменьшает тактовую частоту процессора в зависимости от загруженности системы. Выводит до максимальной частоты, а потом уменьшает по мере увеличения времени простоя.

Governor = conservative – Похож на ondemand, но более экономный (предпочтение отдаётся меньшим тактовым частотам). Частота растёт более плавно.

Governor = performance – Поддерживает процессор(ы) на максимальной тактовой частоте.

… для дополнительной информации см. полный список настроек CPUFreq governor.

Заметили сходство? Я хотел сначала использовать это сравнение, чтобы более наглядно и лучше описать рекомендацию использовать pm static для PHP-FPM в качестве вашего первого выбора.

Настройка performance в CPU governor – это довольно безопасный прирост производительности, потому что это почти полностью зависит от предела процессора вашего сервера. Но есть несколько побочных эффектов (при постоянном удерживании частоты вашего процессора на 100%) – такие, как нагрев, время автономной работы (ноутбук) и другие. Однако это действительно самый быстрый параметр для вашего процессора.

Использование pm = static для максимальной производительности вашего сервера

Настройка pm = static в PHP-FPM сильно зависит от того, сколько свободной памяти на сервере. В основном, если вы страдаете от нехватки памяти сервера, то pm ondemand или dynamic могут оказаться лучшими вариантами. С другой стороны, если у вас достаточно свободной памяти, вы можете избежать большей части накладных расходов менеджера процессов, установив pm static до максимальной емкости сервера. Другими словами, когда вы делаете расчёты, pm.static нужно установить на максимальное количество PHP-FPM процессов, которые могут выполняться без создания проблем доступности памяти или кеша; однако, не так высоко, чтобы перегрузить процессор(ы) и иметь кучу отложенных операций PHP-FPM-а.

У этого сервера установлен pm = static и pm.max_children = 100, которые используют максимум около 10 ГБ из 32 ГБ установленных. Обратите внимание на выделенные столбцы, которые говорят сами за себя. В момент этого скриншота было около 200 активных пользователей (за последние 60 секунд). При таком уровне пользователей, около 70% из дочерних процессов PHP-FPM по-прежнему простаивает. Это означает, что PHP-FPM настроен так, что всегда использует максимум возможности ресурсов вашего сервера вне зависимости от текущего трафика. Процессы, находящиеся в простое, остаются «онлайн», ожидая всплеска трафика, чтобы мгновенно ответить, а не ждать PM пока он насоздаёт дочерних процессов, а потом ещё будет убивать их после того, как истечёт pm.process_idle_timeout. В моих настройках pm.max_requests установлен чрезвычайно высоко, т. к. это боевой сервер, в котором не должно быть утечки памяти в PHP. Вы можете использовать pm.max_requests = 0 при статическом режиме, если у вас есть 110% уверенности в ваших нынешних и будущих PHP-скриптах. Однако, рекомендуется перезапускать скрипты время от времени. Устанавливайте ваше количество запросов до перезапуска в наибольшее значение, чтобы избежать оверхеда PM-а. Например, как минимум pm.max_requests = 1000… основывайтесь на вашем количестве pm.max_children и количестве запросов в секунду.

Тут отображаются не все процессы, а только та часть, что вместилась в ваше терминальное окно. В нашем случае отсортированных по %CPU (потреблению процессора). Чтобы увидеть все 100 PHP-FPM процессов, вы можете использовать что-то вроде этого:

top -bn1 | grep php-fpm

Когда использовать pm ondemand и dynamic

Используя режим 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 была слишком низкой, а т. к. трафик сильно колеблется, режим dynamic достаточно сложно настроить правильно. Общий совет: используйте ondemandкак советуют в этом же вопросе. Однако что еще хуже, т. к. ondemand будет завершать процессы в простое вплоть до 0 когда мало трафика, то после вы получите настолько много накладных расходов, насколько скакнёт трафик. Если, конечно, вы не установите время ожидания чрезвычайно высоким. В этом случае вам просто нужно использовать pm = static + высокий pm.max_requests.

Несмотря на это, всё-таки режим dynamic и особенно ondemand может спасти вас когда у вас есть несколько PHP-FPM Pool-ов. Например, при хостинге нескольких cPanel-аккаунтов или нескольких сайтов под разными pool-ами. У меня есть сервер, к примеру, со 100+ аккаунтами cPanel и около 200+ доменами, и для режима static или даже dynamic было бы невозможно поддерживать хорошую производительность. И только режим ondemand справляется с этим хорошо, т. к. более двух третей из веб-сайтов имеют маленький трафик, а с ondemand это значит, что все «дети» будут завершены, экономя тонны серверной памяти! К счастью, разработчики cPanel это поняли и теперь по умолчанию стоит ondemand. Ранее с динамическим режимом по умолчанию, использовать PHP-FPM на общих серверах было не вариантом. Многие использовали suPHP , потому что dynamic-режим съедал память даже в простое PHP-FPM Pool-ов. Вероятно, если вы имеете хороший трафик, вы не будете размещены на сервере с большим количеством PHP-FPM Pool-ов (shared hosting).

Заключение

Когда дело доходит до PHP-FPM, раз вы начали получать серьезный трафик, то режимы ondemand и dynamic могут ограничить пропускную способность из-за свойственного оверхеда. Исследуйте вашу систему и установите количество процессов в наибольшее, с которым справится ваш сервер. Начните с pm.max_children, установленным на основе максимального использования режимов dynamic или ondemand, а затем увеличивайте до точки, где память и процессор остаются не перегруженными. Вы заметите, что с pm = static, т. к. вы держите всё в памяти, всплески трафика со временем будут меньше влиять на всплески загрузки процессора, а показатели загрузки и средней загрузки станут более сглаженными. Средний размер вашего PHP-FPM-процесса будет зависеть от конкретного веб-сервера, требующего ручной настройки, вот почему автоматизированные режимы – dynamic и ondemand – являются более популярными рекомендациями. Надеюсь, это была полезная статья.

Источник: https://phpprofi.ru/blogs/post/70