В журнале сервера пачками шло предупреждение: пул 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:

  1. свободных процессов стало меньше pm.min_spare_servers — FPM запускает 8 новых;
  2. новый процесс готов примерно через секунду, и счётчик свободных не успевает подрасти;
  3. через секунду 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?

Пришлите их — разберусь, нагрузка это или пул разгоняет себя сам.

· полезно?
АЧ
Алексей Чикун веб-разработчик, команда TRIOHEAD

Делаю сайты и сервисы целиком: от вёрстки до сервера. Пишу о том, что проверил на своих проектах.

Комментарии

// загружаю комментарии…

Комментарии публикуются после проверки. Ссылки и HTML не принимаются.