Администрирование PHP через FastCGI Process Manager
Основное решение: PHP-FPM с веб-сервером Nginx
PHP-FPM (FastCGI Process Manager) является альтернативой старому mod_php для Apache и предназначен для обработки PHP-скриптов через протокол FastCGI. Основное преимущество - гибкое управление пулами рабочих процессов, что позволяет эффективно распределять ресурсы сервера. Наиболее распространённая связка - Nginx в качестве фронтенд-сервера и PHP-FPM как бэкенд.
Установка и базовая настройка
На сервере с Ubuntu или Debian установка выполняется командами:
sudo apt update
sudo apt install php-fpmПосле установки служба PHP-FPM запускается автоматически. Конфигурационные файлы находятся в директории /etc/php/{version}/fpm/. Основной файл - pool.d/www.conf (или пул по умолчанию).
Типичная конфигурация пула для одного сайта:
[www]
user = www-data
group = www-data
listen = /run/php/php8.1-fpm.sock
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
pm.max_requests = 500Настройка Nginx для передачи запросов в PHP-FPM:
server {
listen 80;
server_name example.com;
root /var/www/html;
index index.php index.html;
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.1-fpm.sock;
}
}После изменения конфигурации требуется перезагрузка обеих служб:
sudo systemctl restart php8.1-fpm nginxТипичная проблема: 502 Bad Gateway
Ошибка 502 возникает, когда Nginx не может соединиться с PHP-FPM. Причины: неверный путь к сокету, не запущена служба, неправильные права доступа к сокету. Проверка статуса:
sudo systemctl status php8.1-fpmЕсли сокет не найден, проверьте файл /run/php/ (должен присутствовать .sock). Иногда требуется изменить listen.owner и listen.group в пуле на www-data.
Для повышения производительности рекомендуется использовать pm = static, если сервер выделен под PHP и количество запросов стабильно. Параметр pm.max_children устанавливается из расчёта доступной памяти: (Total RAM - RAM для ОС и других служб) / среднее потребление PHP-процесса.
Как настроить несколько пулов PHP-FPM для разных сайтов?
Каждый сайт может иметь собственный пул с индивидуальными параметрами. Это изолирует процессы и предотвращает влияние одного сайта на другой. Создайте файл конфигурации для второго пула, например /etc/php/8.1/fpm/pool.d/site2.conf:
[site2]
user = site2user
group = site2user
listen = /run/php/site2.sock
pm = ondemand
pm.max_children = 10
pm.process_idle_timeout = 10s
pm.max_requests = 200
chdir = /var/www/site2В Nginx для каждого виртуального хоста указывается свой сокет:
server {
listen 80;
server_name site2.example.com;
root /var/www/site2;
location ~ \.php$ {
fastcgi_pass unix:/run/php/site2.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}Проблема: ошибка доступа к сокету. Убедитесь, что пользователь Nginx (www-data) имеет права на чтение/запись сокета. Добавьте в пул параметр listen.mode = 0666 или измените группу.
Какие режимы управления процессами существуют и когда их применять?
PHP-FPM поддерживает три режима (pm):
- dynamic - количество процессов изменяется в зависимости от нагрузки. Подходит для серверов с переменной нагрузкой.
- static - фиксированное число процессов (pm.max_children). Рекомендуется для выделенных серверов с предсказуемой нагрузкой.
- ondemand - процессы создаются только при поступлении запроса и завершаются после простоя. Экономит память, но увеличивает задержку первого запроса. Подходит для сайтов с редкими посещениями.
Пример конфигурации для режима ondemand с таймаутом простоя:
[ondemand_pool]
pm = ondemand
pm.max_children = 20
pm.process_idle_timeout = 15s
pm.max_requests = 1000Как отследить активность процессов PHP-FPM?
Встроенный статус-страница. Включите в пуле:
pm.status_path = /statusВ Nginx добавьте location для доступа:
location /status {
fastcgi_pass unix:/run/php/php8.1-fpm.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}Откройте в браузере http://example.com/status. Вы увидите JSON или HTML с информацией о пуле: количество активных, idle, total процессов.
Ошибка: статусная страница возвращает 404
Проверьте, что pm.status_path задан в пуле, а Nginx передаёт запрос на PHP-FPM (а не ищет файл). Убедитесь, что в fastcgi-params есть SCRIPT_FILENAME.
Как ограничить время выполнения скриптов и объём памяти?
Параметры PHP для пула задаются через php_admin_value:
[www]
php_admin_value[memory_limit] = 128M
php_admin_value[max_execution_time] = 30
php_admin_value[max_input_time] = 60
php_admin_value[upload_max_filesize] = 10M
php_admin_value[post_max_size] = 12MЗначения переопределяют глобальные настройки php.ini для конкретного пула. Это позволяет разным сайтам иметь разные лимиты.
Проблема: PHP-скрипты не могут превысить заданные лимиты
Если скрипту требуется больше времени или памяти, увеличьте соответствующие значения в пуле или временно через ini_set, если пул разрешает переопределение (но это не рекомендуется из-за безопасности).
Как обеспечить логирование ошибок PHP-FPM?
Основной лог PHP-FPM (ошибки пула и общие) хранится в /var/log/php8.1-fpm.log. Для каждого пула можно задать отдельный лог через php_admin_value[error_log]. Также можно перенаправить stderr в syslog:
[www]
catch_workers_output = yes
php_admin_value[error_log] = /var/log/php/www-error.log
php_admin_flag[log_errors] = onПросмотр логов в реальном времени:
sudo tail -f /var/log/php8.1-fpm.logПроблема: логи не пишутся. Проверьте права на директорию /var/log/php/ и наличие файла. Убедитесь, что catch_workers_output включён.
Расширенные примеры настройки и диагностики PHP-FPM
1. Динамическое изменение пула без перезагрузки (reload)
При изменении конфигурации пула можно выполнить graceful reload без потери активных соединений:
sudo systemctl reload php8.1-fpmРезультат: служба перечитывает файлы конфигурации, текущие процессы завершают обработку запросов, новые процессы запускаются с новыми параметрами. Эквивалентная команда для init.d:
sudo /etc/init.d/php8.1-fpm reload2. Настройка PHP-FPM с использованием TCP-сокета вместо Unix-сокета
Иногда требуется делить пул между несколькими серверами. Используйте TCP-сокет:
[www]
listen = 127.0.0.1:9000
listen.allowed_clients = 127.0.0.1В Nginx:
fastcgi_pass 127.0.0.1:9000;Результат: запросы проходят через TCP loopback. Unix-сокет быстрее, TCP удобнее для сетевого разделения.
3. Ограничение количества одновременных запросов на воркер
Параметр pm.max_requests определяет, сколько запросов обработает один процесс до его перезапуска. Это помогает бороться с утечками памяти:
[www]
pm.max_requests = 1000Мониторинг используйте команду:
ps aux | grep php-fpmВы увидите, что каждый процесс завершает работу после 1000 запросов и заменяется новым.
4. Использование пула с разными версиями PHP
На одном сервере можно установить несколько версий PHP (например, 7.4 и 8.1) и запустить для каждого свой PHP-FPM с разными пулами. В Nginx для разных location укажите соответствующий сокет:
location ~ \.php$ {
# Основная версия PHP 8.1
fastcgi_pass unix:/run/php/php8.1-fpm.sock;
}
location /legacy/ {
location ~ \.php$ {
fastcgi_pass unix:/run/php/php7.4-fpm.sock;
}
}Такой подход используется при миграции проектов на новую версию PHP.
5. Профилирование медленных запросов
Включите логирование медленных запросов (slow log) для выявления узких мест в PHP-скриптах:
[www]
request_slowlog_timeout = 5s
slowlog = /var/log/php/slow.logПример записи в slow.log:
[12-Jun-2025 10:15:22] [pool www] pid 12345 script_filename = /var/www/html/index.php [0x00007f1234567890] sleep() /var/www/html/index.php:10 [0x00007f1234567891] main() /var/www/html/index.php:1
Результат показывает, что скрипт выполнялся дольше 5 секунд из-за вызова sleep() в строке 10.
6. Мониторинг пула через Unix-сокет с помощью socat
Используйте утилиту socat для отправки команды статуса в PHP-FPM:
echo "GET /status" | sudo socat - UNIX-CONNECT:/run/php/php8.1-fpm.sockРезультат (фрагмент):
pool: www process manager: dynamic start time: 12/Jun/2025:10:00:00 +0000 start since: 915 accepted conn: 1024 listen queue: 0 max listen queue: 0 listen queue len: 128 idle processes: 2 active processes: 1 total processes: 3 max active processes: 5 max children reached: 0 slow requests: 0
7. Настройка пула для использования переменных окружения
Передавайте переменные окружения из веб-сервера в PHP с помощью директивы env:
[www]
env[APP_ENV] = production
env[DB_HOST] = localhost
env[DB_NAME] = myappВ коде PHP доступ через getenv('APP_ENV'). Полезно для разделения конфигураций разработки и продакшна.
8. Решение проблемы “Unable to create child process”
Ошибка возникает при нехватке памяти или превышении лимита пользовательских процессов. Проверьте системные лимиты:
ulimit -u # лимит пользовательских процессов
cat /proc/sys/kernel/pid_maxУвеличьте лимит в /etc/security/limits.conf:
www-data soft nproc 1024
www-data hard nproc 2048После изменения перезапустите PHP-FPM. Если проблема сохраняется, уменьшите pm.max_children.
9. Использование pm.status_path для автоматического мониторинга (Prometheus)
Создайте exporter для сбора метрик. Включите статусную страницу в формате JSON:
pm.status_path = /status?jsonПример ответа:
{"pool":"www","process-manager":"dynamic","start-time":1718183200,"start-since":200,"accepted-conn":500,"listen-queue":0,"max-listen-queue":0,"listen-queue-len":128,"idle-processes":2,"active-processes":1,"total-processes":3,"max-active-processes":4,"max-children-reached":0,"slow-requests":0}С помощью скрипта (Python, bash) парсите JSON и передайте в Prometheus или любой другой мониторинг.
10. Изоляция пулов по сети namespace
Для усиления безопасности можно запустить каждый пул в отдельном namespace с помощью systemd. Создайте сервисный файл для каждого пула (например, /etc/systemd/system/php8.1-fpm@.service):
[Unit]
Description=PHP-FPM for %i
After=network.target
[Service]
Type=notify
ExecStart=/usr/sbin/php-fpm8.1 --nodaemonize --fpm-config /etc/php/8.1/fpm/pool.d/%i.conf
PrivateTmp=true
ProtectSystem=full
Затем запустите экземпляр для пула site2:
sudo systemctl start php8.1-fpm@site2Этот метод используется в контейнеризированных средах для максимальной изоляции.