Процессы php-cgi пожирают память размножаясь в геометрической прогрессии и не хотят умирать по истечении лимита FcgidMaxRequestsPerProcess, после чего php-cgi активно начинает сваливать всё в swap и система начинает выдавать "502 Bad Gateway".
Чтобы ограничить число форкнутых php-cgi процессов, не достаточно установить FcgidMaxRequestsPerProcess, после обработки которых процессы должны загибаться, но не всегда они делают это добровольно.
Ситуация до боли знакомая когда php-cgi процессы (чилды) плодятся выедая память, а умирать их ну никак не заставишь - жить хотят с.ки!:) Напоминает проблему с перенаселением земли "пиплами" - неправда ли?;)
Урегулировать извечные дисбаланс между предками и чилдами можно путем ограничения числа php-cgi чилдов, времени их жизни (геноцид) и контроля активности их размножения (контрацепция).
Ограничение числа php-cgi процессов для mod_fcgid
Приведённые ниже директивы играют наверное самую главную роль в ограничении числа php-cgi процессов и в большинстве случаев приведённые здесь значения по умолчанию являются ущербными для серверов с оперативной памятью ниже 5-10 ГБ:
- FcgidMaxProcesses 1000 - максимальное количество процессов, которые могут быть активны одновременно;
- FcgidMaxProcessesPerClass 100 - максимальное количество процессов в одном классе (сегменте), т.е. максимальное количество процессов которое разрешено порождать через один и тот же враппер (wrapper - обвертку);
- FcgidMinProcessesPerClass 3 - минимальное количество процессов в одном классе (сегменте), т.е. минимальное количество процессов запускаемые через один и тот же враппер (wrapper - обвертку), которое будет доступно после завершения всех запросов;
- FcgidMaxRequestsPerProcess 0 - FastCGI должен "сыграть в ящик" после выполнения этого количества запросов.
Какое число php-cgi процессов будет самым оптимальным? Для определения оптимального числа php-cgi процессов нужно Зарегистрироваться на нашем сайте!:)
Настройки тайм-аута жизни php-cgi процессов
При активном размножении php-cgi чилдов, поедая РАМу они могут жить чуть ли не вечно, а это чревато катаклизмами. Ниже есть перечень ГМО-директив, которые помогут урезать срок жизни для php-cgi процессов и своевременно освободить занимаемые ими ресурсы:
- FcgidIOTimeout 40 - время (в сек.) в течении которого модуль mod_fcgid будет пытаться выполнить скрипт.
- FcgidProcessLifeTime 3600 - если процесс существует дольше этого времени (в секундах), то он должен будет помечен для уничтожения во время следующего сканирования процессов, интервал которого задается в директиве FcgidIdleScanInterval;
- FcgidIdleTimeout 300 - если число процессов превышает FcgidMinProcessesPerClass, то процесс, который не обрабатывает запросы за это время (в сек.), во время следующего сканирования процессов, интервал которого задается в директиве FcgidIdleScanInterval, будет отмечен для убивания;
- FcgidIdleScanInterval 120 - интервал, через который mod_fcgid модуль будет искать процессы превысившие лимиты FcgidIdleTimeout или FcgidProcessLifeTime.
- FcgidBusyTimeout 300 - если процесс занят обработкой запросов свыше этого времени (в сек.), то во время следующего сканирования, интервал которого задается в FcgidBusyScanInterval, такой процесс будет отмечен для убивания;
- FcgidBusyScanInterval 120 - интервал, через который выполняется сканирование и поиск занятых процессов превысивших лимит FcgidBusyTimeout;
- FcgidErrorScanInterval 3 - интервал (в сек.), через который модуль mod_fcgid будет убивать процессы ожидающие завершения, в т.ч. и те которые превысили FcgidIdleTimeout or FcgidProcessLifeTime. Убивание происходит путем отправки процессу сигнала SIGTERM, а если процесс продолжает быть активным, то он убивается сигналом SIGKILL.
Нужно принимать во внимание, что процесс превысивший FcgidIdleTimeout или FcgidBusyTimeout может прожить + ещё время FcgidIdleScanInterval или FcgidBusyScanInterval, через которое он будет отмечен для уничтожения.
ScanInterval-ы лучше устанавливать с разницей в несколько сек., например если FcgidIdleScanInterval 120, то FcgidBusyScanInterval 117 - т.е. чтобы сканирование процессов не происходило в одно и тоже время.
Активность порождения php-cgi процессов
Если ничего из вышеприведённого не помогло, что удивительно, то можно ещё попробовать пошаманить с активностью порождения php-cgi процессов...
Помимо лимитов на количество запросов, процессов php-cgi и времени их жизни, есть ещё такая штука как активность порождения дочерних процессов, которую можно урегулировать такими директивами как FcgidSpawnScore, FcgidTerminationScore, FcgidTimeScore и FcgidSpawnScoreUpLimit, перевод которых с буржуйского думаю я дал правильный (указаны значения по умолчанию):
- FcgidSpawnScore 1 - Каждый потомок процесса добавляет это значение к общему счету активности родительского процесса;
- FcgidTerminationScore 2 - После завершения каждого порожденного процесса это значение добавляется к общему счету активности родительского процесса;
- FcgidTimeScore 1 - число, которое каждую секунду вычитается из общего счета активности родительского процесса;
- FcgidSpawnScoreUpLimit 10 - Если текущий счет (Score) активности родительского процесса выше, чем значение FcgidSpawnScoreUpLimit, никакие дочерние процессы приложения не будут порождены, а все запросы на порождение должны будут ожидать, пока существующий процесс не освободится или пока оценка (Score) не упадает ниже этого предела.
Если мой перевод описания и понимание указанных выше параметров верное, то для понижения активности порождения php-cgi процессов следует понизить значение директивы FcgidSpawnScoreUpLimit или увеличить значения FcgidSpawnScore и FcgidTerminationScore.
Итоги
Надеюсь я перечислил и детально разжевал большую часть директив модуля mod_fcgid, которые помогут ограничить число php-cgi и время их жизни, а также понизить потребление ресурсов. Ниже приводиться полная конфигурация mod_fcgid для успешно рабочего сервера с процессором 2500 Мгц и оперативной памятью 512 Мб: