Администрирование PHP-FPM: выбор режима pm и оптимизация под нагрузку

Раздел: Администрирование сервера -> Настройка PHP-FPM

Основные подходы к настройке PHP-FPM

Как настроить базовый пул PHP-FPM для обработки HTTP запросов?

PHP-FPM (FastCGI Process Manager) является реализацией FastCGI для PHP. Основная конфигурация сосредоточена в файлах пулов, обычно расположенных в /etc/php/<version>/fpm/pool.d/. По умолчанию используется файл www.conf. Ниже приведен пример минимальной рабочей конфигурации.


[www]
user = www-data
group = www-data
listen = /run/php/php8.3-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35
pm.max_requests = 200

Php fpm server (настройка сервера php-fpm)

В данной конфигурации:

  • listen указывает на Unix-сокет. Можно использовать TCP (например, 127.0.0.1:9000).
  • pm выбран dynamic - количество процессов меняется в зависимости от нагрузки.
  • pm.max_children - максимальное количество дочерних процессов (ограничивает общее потребление памяти).
  • pm.start_servers, pm.min_spare_servers, pm.max_spare_servers управляют начальным и резервным количеством процессов.
  • pm.max_requests - максимальное число запросов до перезапуска процесса (помогает избежать утечек памяти).

Запуск и проверка:

sudo systemctl start php8.3-fpm
sudo systemctl status php8.3-fpm

Типичные ошибки:

  • 502 Bad Gateway - сокет или порт не доступен. Проверить, что PHP-FPM запущен и listen соответствует конфигурации веб-сервера.
  • Неверные права на сокет - веб-сервер не имеет доступа. Убедиться в совпадении пользователя и группы или использовать listen.mode.
  • Нехватка процессов - при пиковой нагрузке запросы встают в очередь. Увеличить pm.max_children (с учетом доступной памяти).

Как настроить динамическое управление процессами (pm = dynamic) для изменяющейся нагрузки?

Динамический режим позволяет пулу увеличивать и уменьшать количество процессов в заданных пределах. Параметры pm.min_spare_servers и pm.max_spare_servers определяют резерв простаивающих процессов. Пример конфигурации для средне-нагруженного сайта:

pm = dynamic
pm.max_children = 100
pm.start_servers = 10
pm.min_spare_servers = 10
pm.max_spare_servers = 30
pm.max_requests = 500

При резком увеличении нагрузки возможна задержка на создание новых процессов. Рекомендуется установить pm.process_idle_timeout (по умолчанию 10 секунд) для быстрого освобождения ресурсов неактивными процессами.

Ошибка: процессы не успевают создаваться при спайке трафика. Решение: временно увеличить pm.start_servers или перейти на статический режим.

Когда и как использовать статический режим (pm = static) для стабильной нагрузки?

Статический режим фиксирует число процессов. Он подходит для высоконагруженных проектов с предсказуемым трафиком. Пример:

pm = static
pm.max_children = 50

Важно точно рассчитать максимальное количество процессов, исходя из среднего потребления памяти на один процесс. Перерасход приведет к нехватке оперативной памяти и падению сервера.

Частая ошибка: установка слишком большого pm.max_children при ограниченной памяти. Рекомендуется вычислять лимит по формуле: (общая память - память ОС)/память на один PHP процесс. Пример: 16ГБ ОЗУ, 40МБ на процесс -> (16000-1024)/40 ≈ 374, но с запасом 300.

Как настроить PHP-FPM для экономии ресурсов с pm = ondemand?

Режим ondemand создает процесс только при поступлении запроса и завершает его после простоя. Подходит для окружений с малой нагрузкой. Пример:

pm = ondemand
pm.max_children = 20
pm.process_idle_timeout = 10

Недостаток: задержка при первом запросе после простоя (холодный старт). Рекомендуется для серверов разработки или микро-сервисов.

Проблема: при внезапном всплеске запросов создание процессов может не успевать. Решение: увеличить pm.max_children или использовать динамический режим.

Как создать изолированные пулы для разных приложений и пользователей?

Каждый пул может запускаться от своего пользователя и группы, иметь собственный сокет/порт и лимиты. Это повышает безопасность. Пример двух пулов:


[site1]
user = site1
group = site1
listen = /run/php/php8.3-site1.sock
pm = dynamic
pm.max_children = 20

[site2]
user = site2
group = site2
listen = /run/php/php8.3-site2.sock
pm = dynamic
pm.max_children = 30

Конфигурация веб-сервера (например, Nginx) должна указывать на соответствующий сокет:

location ~ \.php$ {
    fastcgi_pass unix:/run/php/php8.3-site1.sock;
    ...
}

Ошибка: пул не запускается из-за отсутствия пользователя. Необходимо создать системного пользователя: sudo useradd -r -s /bin/false site1.

Как выбрать между unix сокетом и TCP соединением?

Unix-сокет быстрее и безопаснее (недоступен из сети). TCP полезен для распределенной архитектуры (веб-сервер и PHP-FPM на разных хостах). Пример TCP:

listen = 192.168.1.10:9000
listen.allowed_clients = 192.168.1.20

На веб-сервере:

fastcgi_pass 192.168.1.10:9000;

Проблема: использование TCP может увеличить задержки из-за сетевых накладных расходов. Рекомендуется использовать Unix-сокет для локальных установок.

Как ограничить время выполнения и память для процессов, и выявить медленные запросы?

Параметры request_terminate_timeout, request_slowlog_timeout и slowlog помогают контролировать производительность.

request_terminate_timeout = 30s
request_slowlog_timeout = 5s
slowlog = /var/log/php/slow.log

Также можно ограничить память через php_admin_value[memory_limit] в пуле:

php_admin_value[memory_limit] = 128M
php_admin_value[max_execution_time] = 30

Ошибка: медленные запросы не записываются в лог. Проверить, что каталог для slowlog существует и доступен для записи пользователем пула.

Как настроить chroot для дополнительной изоляции пула?

Chroot ограничивает доступ пула к файловой системе. Пример:

chroot = /var/www/site1
chdir = /

Необходимо, чтобы все необходимые файлы (PHP, библиотеки, /dev/urandom) были внутри chroot. Это сложная конфигурация, используется редко, в основном для shared hosting.

Проблема: многие функции PHP могут сломаться (например, работа с сокетами, временными файлами). Требуется тщательное тестирование.

Расширенные примеры настройки PHP-FPM

Пример 1: Высоконагруженный пул с pm static и мониторингом

Пример
[highload]
user = app
group = app
listen = /run/php/highload.sock
pm = static
pm.max_children = 200
pm.status_path = /status
ping.path = /ping
ping.response = pong
access.log = /var/log/php/access.log
access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%"

Статус мониторинга доступен через /status (нужно настроить location в Nginx). Пример результата:

pool:                 highload
process manager:      static
start time:           10/Nov/2024:12:00:00 +0000
start since:          3600
accepted conn:        50000
listen queue:         0
max listen queue:     5
listen queue len:     0
idle processes:       50
active processes:     150
total processes:      200
max active processes: 180
max children reached: 0
slow requests:        2

Пример 2: Настройка пула с ограничением по памяти и использовании переменных окружения

Пример
[envpool]
user = deploy
group = deploy
listen = 127.0.0.1:9001
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 10
pm.max_requests = 1000
request_terminate_timeout = 60s
php_admin_value[memory_limit] = 256M
php_admin_value[max_execution_time] = 60
php_admin_value[upload_max_filesize] = 64M
php_admin_value[post_max_size] = 128M
env[APP_ENV] = production
env[DB_HOST] = localhost

Переменные окружения доступны в PHP через getenv(). Это удобно для конфигурации приложений.

Пример 3: Конфигурация для работы с несколькими версиями PHP на одном сервере

Устанавливаются разные версии PHP-FPM, каждая со своим портом/сокетом. Пример для PHP 7.4 и 8.3:

Пример
# Пулы для PHP 7.4
[www74]
listen = /run/php/php7.4-fpm.sock
...

# Пулы для PHP 8.3
[www83]
listen = /run/php/php8.3-fpm.sock
...

В Nginx используем разные location для разных расширений или каталогов:

Пример
location ~ \.php$ {
    if ($request_filename ~* /legacy/) {
        fastcgi_pass unix:/run/php/php7.4-fpm.sock;
    }
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    ...
}

Пример 4: Логирование медленных запросов и ошибок

Пример
request_slowlog_timeout = 2s
slowlog = /var/log/php/slow-$pool.log
php_admin_flag[log_errors] = on
php_admin_value[error_log] = /var/log/php/error.log

Анализ slowlog:

[10-Nov-2024 14:30:00]  [pool www] pid 1234
script_filename = /var/www/site/index.php
[0x00007f1234567890] function() /var/www/site/index.php:10
[0x00007f1234567890] ...

Пример 5: Команды управления службой и тестирование конфигурации

Пример
sudo php-fpm8.3 -t   # проверка синтаксиса конфигурации
sudo systemctl reload php8.3-fpm  # перезагрузка без прерывания текущих соединений
sudo systemctl restart php8.3-fpm # полный перезапуск
sudo systemctl enable php8.3-fpm # автозапуск при загрузке

Настройка сервера PHP-FPM - comments

En
Php fpm server (php)