Беглый обзор и примеры для настройки PHP-FPM Process Manager —
dynamic
, ondemand
и static
.
Я не выполнял нагрузочного тестирования при использовании различных конфигураций, так что все выводы о применимости того или иного подхода чисто умозрительные.
Тем не менее — тестирование проводиться, думаю, будет, по возможности — добавлю результаты отдельным постом.
pm = dynamic
Большинство HowTo по настройке PHP-FPM (и этот блог — не исключение) рекомендуют использование Process Manager для PHP-FPM в dynamic
конфигурации, при которой кол-во процессов PHP-FPM меняется динамически, в зависимости от количества запросов, при этом имеется минимальное кол-во процессов, которые будут запущены при старте FPM-сервиса.
Пример такой конфигурации как правило выглядит так:
[pool_name] ... pm = dynamic pm.max_children = 5 pm.start_servers = 3 pm.min_spare_servers = 2 pm.max_spare_servers = 4 pm.max_requests = 200
Тут:
pm.max_children
: кол-во дочерних процессов, которое будет создано для пула при использованииstatic
, или максимальное кол-во дочерних процессов, которое будет создано для пула при использованииdynamic
, задаёт максималное кол-во запросов, которое может обслужить пул, аналогApacheMaxClients
для Apache2 илиPHP_FCGI_CHILDREN
для PHP FastCGIpm.start_servers
: кол-во дочерних процессов, которые будут созданы при старте пула, используется только приdynamic
конфигурацииpm.min_spare_servers
: минимальное кол-во дочерних процессов в статусе ожидания (idle), только дляdynamic
конфигурацииpm.max_spare_servers
: максимальное кол-во дочерних процессов в статусе ожидания (idle), только дляdynamic
конфигурацииpm.max_requests
: максимальное кол-во запросов, которое должен обработать процесс FPM перед его перезапуском (помогает избежать утечек памяти)
После применения такой конфигурации процессы будут выглядеть следующим образом:
Каждый такой процесс, включая дочерние, будет кушать память, независимо от того — требуется он сейчас или нет.
pm = ondemand
Кроме dynamic
конфигурации так же можно использовать ondemand
, при котором FPM не будет создавать дочерних процессов вообще, пока не появится реальный запрос для обработки, а запустит только мастер-процесс самого php-fpm
.
Выглядеть такой конфиг может так:
... pm = ondemand pm.max_children = 5 pm.process_idle_timeout = 10s pm.max_requests = 200 ...
Тут в pm.process_idle_timeout
задаётся время, через которое Process Manager уничтожит дочерний процесс, который не обслуживает запросы.
Перезапускаем сервис, и проверяем процессы:
Однако, если сделать вызов к сайту, который использует этот пул — FPM запустит дочерний процесс для его обслуживания, и продержит его 10 секунд, если не будет других запросов:
Процессы PHP-FPM:
И через 10 секунд — опять ни одного дочернего процесса не будет.
Такой вариант отлично подойдёт для сервера с небольшим объёмом RAM и который обслуживает нечастые запросы клиентов, что бы не тратить попусту память.
pm = static
И третий вариант конфигурации Process Manager — static
, когда указывается количество дочерних процессов, которые будут созданы при запуске пула независимо от количества запросов:
[pool-name] listen = /var/run/php/php-fpm-pool-name.sock user = www-data group = www-data listen.owner = www-data listen.group = www-data pm = static pm.max_children = 5 pm.max_requests = 200 ...
Эти процессы будут находится в режиме ожидания новых запросов.
Преимущество такого подхода в том, что при активном использовании пула (часто посещаемый вебсайт, например) — системе не придётся постоянно создавать/убивать дочерние процессы для PHP, тем самым будет экономиться и время процессора, и потребуется меньшее врмея для обслуживания нового запроса, т.к. FPM уже будет иметь готовый дочерний процесс, а не начнёт его создавать.
Единственной сложностью тут может быть вопрос — как определить кол-во процессов для создания.
Рассмотрим на нашем production сервере, где сейчас pm = dynamic
.
Используя ps_mem
— проверяем объём занятой памяти:
5.5 ГБ занято всего, из них 1.6 — это процессы php-fpm
, 129 штук.
Значит, если не учитывать процессы FPM — сервер кушает 3.9 ГБ памяти на остальные сервисы из 15 доступных:
Само собой — тут необходимо учитывать корреляцию между временем суток и кол-вом пользователей, так что мониторинг нам в помощь.
Тем не менее идея такова.
Из 15 ГБ занято под другие процеcсы и систему 4 ГБ, значит свободно 11. Из них можно выделить 70-80% под процессы FPM.
Используя значение из ps_mem
:
1.5 GiB + 65.9 MiB = 1.6 GiB php-fpm7.2 (129)
Считаем, что в среднем один дочерний процесс php-fpm
потребляет 12 мб:
Если выделять 8 ГБ чисто под FPM — значит получается:
(какая замечательная циферка)
Очевидно, что такое кол-во процессов будет чрезмерным.
Кроме того — 12 мб является средним значением, тогда как самые «толстые» процессы fpm
кушают почти 100 мб:
Исходя из предположения, что все процессы будут потреблять максимальное кол-во памяти — мы получаем уже всего лишь 80 процессов.
Как обычно — «Истина обычно лежит посередине».
Значение в pm.max_children = 300
для начала было бы вполне подходящим.
Далее — тестируем нагрузки с apache bench или его аналогами, и при необходимости меняем значение.