Старт PHP-демона: systemd unit и альтернативы
Подходы к запуску PHP как системной службы
Как настроить запуск PHP-FPM через systemd для продакшен-среды?
PHP-FPM (FastCGI Process Manager) является стандартным способом запуска PHP в связке с веб-сервером nginx или apache. Для управления им используется systemd – менеджер служб в современных дистрибутивах Linux. Ниже описан процесс настройки и запуска.
Шаг 1: Установка пакета php-fpm
sudo apt update && sudo apt install php8.1-fpmService php start (запуск php-сервиса)
После установки создаётся юнит systemd с именем php8.1-fpm.service (версия может отличаться).
Шаг 2: Проверка статуса и запуск
sudo systemctl status php8.1-fpm
sudo systemctl start php8.1-fpm
sudo systemctl enable php8.1-fpm # автозапуск при загрузке
Шаг 3: Конфигурация пула – файл /etc/php/8.1/fpm/pool.d/www.conf. Настройки listen, pm (менеджер процессов), max_children и т.д.
listen = /run/php/php8.1-fpm.sock
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35
После изменений требуется перезагрузка:
sudo systemctl reload php8.1-fpm
Типичные проблемы:
- Ошибка "Unit php8.1-fpm.service not found" – не установлен пакет php-fpm или версия не совпадает.
- Права доступа на сокет – веб-сервер не может подключиться к сокету. Решение: проверить пользователя в конфиге пула (listen.owner, listen.group) и добавить пользователя веб-сервера в группу.
- Порт уже занят – если используется TCP-порт (listen = 127.0.0.1:9000), убедиться, что порт свободен.
Как запустить встроенный PHP-сервер (php -S) в качестве службы с помощью systemd?
Встроенный сервер подходит для разработки и тестирования, но не рекомендуется для продакшена из-за однопоточности и отсутствия управления процессами.
Создаётся юнит systemd:
[Unit]
Description=PHP built-in web server for MyApp
After=network.target
[Service]
Type=simple
User=www-data
Group=www-data
ExecStart=/usr/bin/php -S 0.0.0.0:8080 -t /var/www/myapp
StandardOutput=journal
StandardError=journal
Restart=on-failure
[Install]
WantedBy=multi-user.target
Поместить в /etc/systemd/system/php-builtin.service, затем:
sudo systemctl daemon-reload
sudo systemctl start php-builtin
sudo systemctl status php-builtin
Проблемы:
- Сервер останавливается при ошибке в приложении – помогает Restart=on-failure.
- Не поддерживает параллельные запросы – легко приводит к зависанию.
- Для изменения порта нужно редактировать юнит.
Как использовать init.d скрипт для управления PHP-FPM на старых системах?
В дистрибутивах без systemd (или с совместимостью) применяются скрипты /etc/init.d/. Некоторые пакеты php-fpm содержат такой скрипт.
sudo service php8.1-fpm start
sudo service php8.1-fpm stop
sudo service php8.1-fpm restart
Скрипт можно найти после установки пакета или создать вручную.
Проблемы:
- На современных системах init.d может быть заменён на systemd-совместимый wrapper.
- Команда service без скрипта выдаст "unrecognized service".
Как настроить Supervisor для демонизации PHP-скриптов (воркеров очередей)?
Supervisor позволяет управлять долго работающими процессами, автоматически перезапускать их и вести логи. Подходит для воркеров Laravel, Symfony, скриптов обработки данных.
Установка:
sudo apt install supervisor
Создание конфигурации /etc/supervisor/conf.d/php-worker.conf:
[program:php-worker]
command=php /var/www/app/artisan queue:work --sleep=3 --tries=3
process_name=%(program_name)s_%(process_num)02d
numprocs=2
autostart=true
autorestart=true
user=www-data
redirect_stderr=true
stdout_logfile=/var/log/supervisor/php-worker.log
Обновление supervisor и запуск:
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start php-worker:*
Проблемы:
- Ошибка "FATAL Exited too quickly" – скрипт завершается сразу. Проверить команду, пути, окружение.
- Логи не пишутся – проверить права на директорию /var/log/supervisor.
Как запустить PHP-FPM в контейнере Docker и управлять им как службой?
Использование Docker для изоляции PHP-окружения популярно в микросервисах. Можно управлять контейнером через systemd, docker-compose.
Пример docker-compose.yml:
version: '3'
services:
php-fpm:
image: php:8.1-fpm
volumes:
- ./app:/var/www/html
ports:
- "9000:9000"
restart: unless-stopped
Запуск: docker-compose up -d. Для интеграции с systemd создаётся юнит, который вызывает docker-compose.
Проблемы:
- Контейнер не видит файлы – неправильно настроен volume.
- Порт 9000 занят другим процессом.
- При ошибках в entrypoint контейнер постоянно перезапускается.
Расширенные примеры настройки PHP службы
Пример 1: Детальная конфигурация пула PHP-FPM
Файл /etc/php/8.1/fpm/pool.d/www.conf может содержать множество параметров для точной настройки. Ниже показаны ключевые директивы с пояснениями.
; Пример конфигурации пула для высоконагруженного сайта
[www]
user = www-data
group = www-data
listen = /run/php/php8.1-fpm.sock
; Использование сокета вместо TCP для производительности
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
; Динамическое управление процессами
pm = dynamic
pm.max_children = 100
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 30
; Ограничение времени выполнения запроса
request_terminate_timeout = 300
; Максимальное количество запросов до перезапуска процесса
pm.max_requests = 1000
После изменения конфигурации проверяется синтаксис и перезагружается служба:
sudo php-fpm8.1 -t
sudo systemctl reload php8.1-fpm
Результат проверки статуса пулов:
$ sudo systemctl status php8.1-fpm
● php8.1-fpm.service - PHP 8.1 FastCGI Process Manager
Loaded: loaded (/lib/systemd/system/php8.1-fpm.service; enabled; vendor preset: enabled)
Active: active (running) since ...
Docs: man:php-fpm8.1(8)
Main PID: 12345 (php-fpm8.1)
Status: "Processes active: 3, idle: 7, Requests: 23456, slow: 0, Traffic: 0.00req/s"
Tasks: 12 (limit: 2345)
Memory: 48.3M
CGroup: /system.slice/php8.1-fpm.service
├─12345 php-fpm: master process (/etc/php/8.1/fpm/php-fpm.conf)
├─12346 php-fpm: pool www
├─12347 php-fpm: pool www
└─12348 php-fpm: pool www
Пример 2: Systemd unit для встроенного сервера с PHP.ini и роутером
Для более гибкого запуска встроенного сервера можно указать путь к php.ini и файл-роутер (router.php).
[Unit]
Description=PHP Built-in Server for App
After=network.target
[Service]
Type=simple
User=deploy
ExecStart=/usr/bin/php -c /etc/php/8.1/cli/php.ini -S 0.0.0.0:8080 -t /var/www/app /var/www/app/router.php
Restart=always
RestartSec=5
StandardOutput=append:/var/log/php-builtin/access.log
StandardError=append:/var/log/php-builtin/error.log
[Install]
WantedBy=multi-user.target
Создание файла роутера router.php:
<?
$uri = $_SERVER['REQUEST_URI'];
if (file_exists(__DIR__ . $uri) && !is_dir(__DIR__ . $uri)) {
return false; // отдавать статические файлы напрямую
}
require __DIR__ . '/index.php';
Запуск и проверка:
sudo systemctl start php-builtin
curl http://localhost:8080/
sudo systemctl status php-builtin
● php-builtin.service - PHP Built-in Server for App
Loaded: loaded (/etc/systemd/system/php-builtin.service; enabled; vendor preset: enabled)
Active: active (running) since ...
Main PID: 23456 (php)
Tasks: 1 (limit: 12345)
Memory: 12.3M
CGroup: /system.slice/php-builtin.service
└─23456 /usr/bin/php -c /etc/php/8.1/cli/php.ini -S 0.0.0.0:8080 -t /var/www/app /var/www/app/router.php
Пример 3: Supervisor для нескольких воркеров Laravel с разными параметрами
В конфигурации supervisor можно задать несколько программ с различными настройками.
[program:laravel-queue-high]
command=php /var/www/app/artisan queue:work --queue=high --sleep=3 --tries=3
process_name=%(program_name)s_%(process_num)s
numprocs=2
autostart=true
autorestart=true
user=forge
stdout_logfile=/var/log/supervisor/queue-high.log
[program:laravel-queue-low]
command=php /var/www/app/artisan queue:work --queue=low --sleep=5 --tries=5
process_name=%(program_name)s_%(process_num)s
numprocs=1
autostart=true
autorestart=true
user=forge
stdout_logfile=/var/log/supervisor/queue-low.log
Перезагрузка конфигурации и проверка статуса:
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl status
laravel-queue-high:laravel-queue-high_00 RUNNING pid 34567, uptime 0:02:34 laravel-queue-high:laravel-queue-high_01 RUNNING pid 34568, uptime 0:02:34 laravel-queue-low:laravel-queue-low_00 RUNNING pid 34569, uptime 0:02:34
Пример 4: Docker Compose стек с PHP-FPM, Nginx, Redis
Полноценный стек для веб-приложения включает nginx как фронтенд и php-fpm для обработки скриптов, а также redis для кеширования.
version: '3.8'
services:
php-fpm:
build:
context: .
dockerfile: Dockerfile.php
volumes:
- ./src:/var/www/html
environment:
- REDIS_HOST=redis
depends_on:
- redis
restart: unless-stopped
nginx:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./src:/var/www/html
- ./nginx.conf:/etc/nginx/conf.d/default.conf
depends_on:
- php-fpm
restart: unless-stopped
redis:
image: redis:alpine
restart: unless-stopped
volumes:
src:
Dockerfile.php:
FROM php:8.1-fpm
RUN docker-php-ext-install pdo_mysql redis
WORKDIR /var/www/html
Запуск стека и проверка:
docker-compose up -d
docker-compose ps
Name Command State Ports -------------------------------------------------------------- app_nginx_1 /docker-entrypoint.sh ngin ... Up 0.0.0.0:8080->80/tcp app_php-fpm_1 docker-php-entrypoint php ... Up 9000/tcp app_redis_1 docker-entrypoint.sh redis ... Up 6379/tcp
Пример 5: Init.d скрипт для PHP-FPM (устаревший, для справки)
На старых системах (Debian 7, CentOS 6) использовались скрипты в /etc/init.d/. Ниже приведён сокращённый пример скрипта php-fpm.
#!/bin/sh
### BEGIN INIT INFO
# Provides: php-fpm
# Required-Start: $remote_fs $network
# Required-Stop: $remote_fs $network
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: PHP FastCGI Process Manager
# Description: PHP-FPM is a PHP FastCGI implementation
### END INIT INFO
NAME=php-fpm
DAEMON=/usr/sbin/php-fpm
case "$1" in
start)
echo -n "Starting $NAME: "
$DAEMON && echo "OK" || echo "FAIL"
;;
stop)
echo -n "Stopping $NAME: "
killall $NAME && echo "OK" || echo "FAIL"
;;
restart)
$0 stop
sleep 2
$0 start
;;
status)
status_of_proc $DAEMON $NAME
;;
*)
echo "Usage: $0 {start|stop|restart|status}"
exit 1
;;
esac
exit 0
Использование: service php-fpm start. При ошибках следует проверять наличие исполняемого файла и права на его запуск.