almdev Разработка с ИИ

Очередь и планировщик на сервере с ispmanager
Панель даёт сайт, PHP и cron. Очередей она не даёт.

Как держать обработчик очереди живым на сервере с панелью управления: supervisor против cron, правильная версия PHP для воркера, перезапуск при выкладке и порядок диагностики застрявшей очереди.

Подкатегория: Инфраструктура

Чтение
5 мин
Технологии / версии
Laravel · supervisor · cron · ISPManager
22 сен 2026 · 5 мин · 22 просмотра · Архив
Контроль очереди и регулярных задач на сервере
Инфраструктура 09/2026

Панель управления делает то, для чего создана: сайт, домен, сертификат, база, версия PHP на выбор, cron. Всего, что связано с фоновой обработкой, в ней нет. Воркер, запущенный в SSH-сессии, живёт до закрытия терминала.

Продолжение темы, начатой в статьях про Composer с нужной версией PHP и про Redis на таком сервере. Здесь — про то, как заставить очередь работать постоянно.

Что даёт панель и чего в ней нет

Есть: несколько версий PHP одновременно, у каждого сайта своя; планировщик заданий с интерфейсом; отдельный системный пользователь на сайт.

Нет: менеджера процессов, интерфейса к очередям, мониторинга фоновых задач.

Отдельная особенность, которая влияет на всё дальнейшее: PHP для сайта и PHP в консоли — разные. В консоли у пользователя обычно системный PHP, часто старее того, на котором работает сайт, и с другим набором расширений.

Три способа держать воркер живым

Cron с повторным запуском. Каждую минуту cron пытается запустить воркер, а блокировка не даёт запустить второй экземпляр.

* * * * * cd /var/www/user/data/www/app && flock -n /tmp/worker.lock /opt/php84/bin/php artisan queue:work --stop-when-empty >> storage/logs/worker.log 2>&1

Работает без прав root. Минус: задержка до минуты после падения и на старте.

systemd. Правильный способ на своём сервере, требует root. На арендованном VPS с панелью root обычно есть, и я использую именно его в одном проекте.

Supervisor. Тоже требует root для установки, зато удобнее для нескольких воркеров и не завязан на конкретный дистрибутив.

Я остановился на supervisor. Причина прагматичная: та же конфигурация, что и в Docker локально, только пути другие.

Конфигурация под пользователя панели

[program:app-queue]
command=/opt/php84/bin/php /var/www/user/data/www/app/artisan queue:work redis --queue=default --sleep=3 --tries=3 --max-time=3600
directory=/var/www/user/data/www/app
user=user
numprocs=2
process_name=%(program_name)s_%(process_num)02d
autostart=true
autorestart=true
startsecs=5
stopwaitsecs=120
stdout_logfile=/var/www/user/data/www/app/storage/logs/worker.log
redirect_stderr=true

Три момента, специфичных для панели.

Полный путь к нужному PHP. Не php, а /opt/php84/bin/php. Системный PHP почти наверняка не тот, что нужен приложению, и воркер упадёт на несовместимости синтаксиса или на отсутствующем расширении.

Пользователь сайта, а не root. Иначе файлы кеша и логов создадутся от root, и веб-сервер потом не сможет в них писать. Симптом — сайт падает с ошибкой прав через несколько часов после того, как всё настроили.

Каталог сайта в directory. Относительные пути в приложении отсчитываются от него.

Конфигурационный файл может потребовать явного указания настроек PHP, если панель хранит их отдельно для каждой версии:

command=/opt/php84/bin/php -c /var/www/user/data/php-bin-isp-php84/php.ini /var/www/user/data/www/app/artisan queue:work

Без этого воркер получит настройки по умолчанию, а не те, что у сайта. Разница обычно в лимите памяти и в подключённых расширениях.

Планировщик

В Laravel одна запись cron заменяет весь планировщик:

* * * * * cd /var/www/user/data/www/app && /opt/php84/bin/php artisan schedule:run >> /dev/null 2>&1

Добавляется через интерфейс панели в разделе планировщика, а не через crontab -e — панель управляет своей копией и может перезаписать ручные правки.

Тот же полный путь к PHP. Ошибка здесь встречается чаще всего: команда добавлена, cron её выполняет, но системный PHP падает на синтаксисе, а вывод отправлен в никуда. Симптом — «планировщик не работает» без единой записи в логах.

При настройке я всегда сначала пишу вывод в файл и только потом, убедившись, что всё идёт, меняю на отбрасывание.

Перезапуск при выкладке

Воркеры держат код в памяти. Без перезапуска они продолжают работать со старой версией после обновления.

Скрипт обновления заканчивается тремя командами:

/opt/php84/bin/php artisan migrate --force
/opt/php84/bin/php artisan config:cache
/opt/php84/bin/php artisan queue:restart

Команда перезапуска не убивает процессы. Она ставит метку в кеш, воркеры проверяют её после каждой задачи и завершаются штатно. Supervisor поднимает новые.

config:cache эту метку не удаляет: конфигурационный кеш и application cache — разные вещи. Порядок выше нужен, чтобы команда стартовала уже с новой конфигурацией. Если при выкладке меняются cache store или prefix, старые воркеры могут не увидеть метку в новом кеше; один раз перезапускаю их напрямую через supervisorctl restart app-queue:*.

Диагностика: очередь стоит, ошибок нет

Порядок, к которому я пришёл после нескольких таких случаев.

Процессы живы?

supervisorctl status app-queue:*

Статус, отличный от RUNNING, — ответ найден. Смотрим лог, там причина падения.

Что в очереди?

Сначала беру database, prefix и имя очереди из конфигурации приложения. Фрагмент ручной проверки выглядит так:

redis-cli -n "$REDIS_DB" --scan --pattern "${REDIS_PREFIX}*queues:*"
redis-cli -n "$REDIS_DB" llen "$QUEUE_KEY"

QUEUE_KEY — точный ключ из результата первой команды, а не захардкоженный queues:default. Ноль при ожидающих задачах означает, что приложение пишет в другую базу, очередь или с другим префиксом, чем читает воркер. Классика при нескольких проектах на одном Redis.

Не завис ли воркер на задаче?

ps -o pid,etime,cmd -p $(pgrep -f "queue:work")

Время работы больше лимита --max-time означает, что процесс висит на чём-то, что не проверяет флаги — обычно на сетевом запросе без тайм-аута.

Что в проваленных?

/opt/php84/bin/php artisan queue:failed

Тот ли PHP?

supervisorctl status app-queue:* | head -1
cat /etc/supervisor/conf.d/app-queue.conf | grep command

Проверка занимает секунду и закрывает целый класс проблем, которые выглядят как что угодно, кроме версии интерпретатора.

Ограничения такой схемы

Описанная одноузловая схема масштабируется только вертикально: больше нагрузки — больше процессов на том же сервере. Сама Redis-очередь не мешает поднять воркеры на втором сервере, но это уже отдельный worker-host с общей конфигурацией, кодом и мониторингом.

Мониторинга нет. Длину очереди и возраст старейшей задачи я смотрю руками или простым скриптом, который шлёт письмо при превышении порога.

Нет изоляции. Воркер работает в том же окружении, что и сайт, и тяжёлая задача отъедает ресурсы у веб-запросов. Ограничить это на уровне панели нечем.

Для проекта с десятками тысяч задач в сутки этого достаточно. Для большего нужен либо отдельный сервер под обработку, либо переезд в контейнеры — и второе я обычно и предлагаю, когда доходит до такого объёма.

Итог

Настройка занимает полчаса, из которых двадцать минут — выяснение правильных путей к PHP и к каталогу сайта в конкретной панели.

Главное, что стоит запомнить: пользователь и путь к интерпретатору. Обе ошибки не дают внятной диагностики, обе выглядят как «очередь не работает», и обе решаются одной строкой в конфигурации.

#laravel #supervisor #cron #ispmanager