В журнале сервера пачками шло предупреждение: пул PHP-FPM «кажется занятым», запускаю ещё 8, 16, 32 процесса. Звучит как нагрузка, с которой сервер не справляется. Замер показал обратное: за 15 секунд такого всплеска через nginx прошло два запроса.
Это третья часть цикла «Сервер для WordPress».
Как выглядит
WARNING: [pool www] seems busy (you may need to increase pm.start_servers,
or pm.min/max_spare_servers), spawning 32 children, there are 0 idle, and 46 total children
На тестовой копии — 44 сообщения за 20 минут, на рабочем сервере — 63–74 за три часа, в пике 51 процесс.
Почему это не нагрузка
Решающий замер: за 15 секунд всплеска через nginx прошло два запроса, а пул вырос с 14 до 46 процессов. Разгоняет себя сам FPM:
- свободных процессов стало меньше
pm.min_spare_servers— FPM запускает 8 новых; - новый процесс готов примерно через секунду, и счётчик свободных не успевает подрасти;
- через секунду FPM видит ту же картину и удваивает: 16, потом 32.
Стандартный пул нашего образа держал наготове 6–8 процессов: start_servers 6, min_spare 4, max_spare 8, по 43 МБ каждый. Чтобы свободных стало меньше четырёх, хватает пары медленных запросов — дальше лавина идёт сама.
Откуда медленные запросы при двух посетителях
От прогрева кэша. Когда страница в кэше устарела, nginx отдаёт старую копию сразу, а новую собирает в фоне. Обходчик получает ответ за миллисекунды и тут же шлёт следующий, а за ним копится хвост фоновых сборок по 1–2 секунды. «Последовательный» обход создаёт десяток параллельных рендеров. Как это лечится со стороны обходчика — в 31-й части цикла про кэш страниц.
Грабля: поднять min_spare — сделать хуже
Первая мысль — дать больше запасных процессов через pm.min_spare_servers. Подняли с 4 до 12 — предупреждения пошли чаще, теперь при 9–10 свободных. Это именно тот порог, по которому FPM печатает предупреждение и запускает новые процессы. Чем он выше, тем раньше начинается лавина.
Как правильно: тёплый запас
[www]
pm = dynamic
pm.start_servers = 16 ; сразу после старта
pm.min_spare_servers = 4 ; порог тревоги — оставить низким
pm.max_spare_servers = 20 ; сколько свободных можно держать, не убивая
; pm.max_children не трогали
Запас держится через start_servers и max_spare_servers: процессов наготове много, а порог предупреждения остаётся низким.
Результат
- на рабочем сервере — 0 предупреждений за 11 минут против 22 за полчаса до правки, причём в окне сразу после выката с полным сбросом кэша, когда нагрузка выше обычной;
- на тесте пул стоит на 17–21 процессе: 730–900 МБ против 390 МБ раньше. Это цена тёплого запаса;
- одной паузы в обходчике без настройки пула не хватило: ручные прогоны давали ноль, а обход по крону — всплеск.
Насколько это срочно
Честно: не срочно. За сутки наблюдения потолок max_children не достигался ни разу, ошибок 5xx ноль — посетители ничего не замечали. Это шум в журнале и до 2 ГБ лишней памяти на пиках. Но шум опасен тем, что к нему привыкают и пропускают настоящее предупреждение.
Как мерить, чтобы не соврать: считать сообщения за окно времени (journalctl --since … | grep -c "seems busy"), а не «есть или нет»; не мерить сразу после пересоздания контейнера — кэш байткода холодный, запросы вдвое дольше; смотреть журнал nginx за секунды всплеска — если запросов единицы, дело не в посетителях.
В журнале сервера постоянные предупреждения от PHP-FPM?
Пришлите их — разберусь, нагрузка это или пул разгоняет себя сам.
// загружаю комментарии…