Laravel Octane — это пакет, который запускает приложение поверх высокопроизводительных серверов Swoole, Open Swoole, RoadRunner или FrankenPHP. Вместо того чтобы загружать фреймворк на каждый HTTP-запрос (как это делает связка nginx + php-fpm), Octane один раз поднимает приложение в память и обслуживает все последующие запросы из этой же копии. На реальных проектах это даёт ускорение в 3–5 раз и заметно снижает потребление CPU.
В этой статье разберём, как поставить Octane на боевой проект, какие подводные камни ждут разработчика при переходе с классического php-fpm и как корректно настроить деплой в Docker.
Когда Octane действительно нужен
Перед тем как ставить Octane, имеет смысл оценить, даст ли он профит именно вашему приложению. Octane решает одну конкретную проблему — холодный старт фреймворка. Если у вас:
- тяжёлый bootstrap (много сервис-провайдеров, ENV, конфигов);
- API с большим числом запросов в секунду;
- микросервисы, где каждая миллисекунда на счету;
- длинная цепочка middleware и роутов,
— Octane окупится почти сразу. Если же приложение упирается в БД или внешние API, ускорение будет, но менее заметное. Сначала имеет смысл профилировать запросы через laravel/telescope или clockwork, и только потом подключать Octane.
Установка и базовая настройка
Octane ставится одной командой Composer, после чего нужно опубликовать конфигурацию:
composer require laravel/octane
php artisan octane:install
Установщик спросит, какой сервер использовать. На практике выбор сводится к двум вариантам:
- Swoole — даёт максимальную производительность и поддержку тиков, таймеров, корутин. Требует расширение PHP
swoole. - RoadRunner — написан на Go, проще в эксплуатации, не требует установки расширений и хорошо интегрируется с Kubernetes.
Для большинства Laravel-проектов я рекомендую начать с RoadRunner: он проще в Docker-окружении и не зависит от PECL-расширений.
Запуск через RoadRunner
После установки в корне проекта появится файл .rr.yaml. Минимальная рабочая конфигурация выглядит так:
version: "3"
server:
command: "php artisan octane:start --server=roadrunner"
http:
address: 0.0.0.0:8000
pool:
num_workers: 8
max_jobs: 500
supervisor:
max_worker_memory: 256
logs:
mode: production
level: warn
Параметр num_workers задаёт количество процессов воркеров. Хорошее эмпирическое правило — 2× число ядер CPU. max_jobs заставляет воркер перезапускаться после N запросов, что защищает от утечек памяти.
Главная ловушка: «грязное» состояние между запросами
Так как процесс приложения живёт между запросами, любое состояние, которое вы пишете в синглтонах или статических свойствах, остаётся жить. На php-fpm это работало, потому что после ответа процесс умирал. С Octane — нет. Типичные источники багов:
- Синглтоны, в которые вы по привычке кладёте текущего пользователя или request.
- Глобальные сервис-контейнеры, кеширующие данные пользователя.
- Сторонние пакеты, которые держат состояние в статических полях.
- Auth-фасады при работе вне HTTP-цикла (например, в очередях, поднятых тем же процессом).
Решение — использовать Octane::concurrently(), очищать сервисы через flushState() и тщательно ревизировать сервис-провайдеры. Laravel сам сбрасывает большинство стандартных сервисов между запросами, но кастомные синглтоны нужно регистрировать в массиве octane.flush:
// config/octane.php
'flush' => [
\App\Services\TenantContext::class,
\App\Services\CurrentUserResolver::class,
],
Кеш конфигов, маршрутов и событий
Octane загружает приложение один раз, поэтому кеширование на этапе деплоя даёт двойную выгоду — и при старте, и при работе. Обязательный набор команд перед стартом Octane:
php artisan config:cache
php artisan route:cache
php artisan event:cache
php artisan view:cache
Если в коде остаются env() вызовы вне конфигов — на Octane они вернут null, потому что переменные окружения недоступны после config:cache. Правильный паттерн — переносить все обращения к ENV в файлы config/*.php и далее читать через config().
Деплой в Docker
Минимальный Dockerfile для Octane на RoadRunner может выглядеть так:
FROM php:8.3-cli-alpine
RUN apk add --no-cache git unzip libpq-dev \
&& docker-php-ext-install pdo_pgsql opcache pcntl
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
WORKDIR /app
COPY . .
RUN composer install --no-dev --optimize-autoloader \
&& php artisan octane:install --server=roadrunner --no-interaction \
&& php artisan config:cache \
&& php artisan route:cache \
&& php artisan event:cache \
&& php artisan view:cache
EXPOSE 8000
CMD ["php", "artisan", "octane:start", "--server=roadrunner", "--host=0.0.0.0", "--port=8000"]
Обратите внимание на включённый opcache — он критичен для PHP-производительности. Для продакшена дополнительно настройте параметры:
opcache.enable=1
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0
opcache.preload=/app/preload.php
Параметр validate_timestamps=0 отключает проверку изменений файлов — идеально для иммутабельных Docker-образов, но требует пересборки контейнера на каждый деплой.
Graceful reload и zero-downtime
Если развернуть Octane за балансировщиком (nginx, Traefik, AWS ALB), деплой можно делать без даунтайма. Алгоритм:
- Балансировщик начинает направлять трафик на новый под;
- Старый под получает SIGTERM;
- RoadRunner / Swoole дорабатывают активные запросы и закрываются.
В Kubernetes для этого нужно правильно настроить terminationGracePeriodSeconds и preStop хук. Минимальный пример:
lifecycle:
preStop:
exec:
command: ["sleep", "10"]
terminationGracePeriodSeconds: 30
10 секунд sleep дают балансировщику время убрать под из апстрима, ещё 20 секунд — на докатывание открытых запросов.
Мониторинг
RoadRunner отдаёт метрики в формате Prometheus прямо из коробки — достаточно включить блок metrics в .rr.yaml:
metrics:
address: 0.0.0.0:2112
Дальше эндпоинт /metrics цепляется к Prometheus, поверх собирается Grafana-дашборд, и вы видите количество активных воркеров, время ответа, потребление памяти и частоту перезапусков. По этим графикам легко подбирать оптимальные значения num_workers и max_jobs.
Краткий чек-лист перед переходом на Octane
- Убрать все
env()из кода вне конфигов. - Проверить сервис-провайдеры и кастомные синглтоны на наличие состояния.
- Включить кеширование конфигов, маршрутов, событий и вью.
- Поднять opcache и при возможности preload.
- Настроить graceful shutdown в оркестраторе.
- Подключить Prometheus-метрики и Grafana.
После выполнения этого списка Octane выходит на боевой режим. На своих проектах я получал стабильное ускорение API в 3–4 раза и снижение нагрузки на CPU примерно вдвое — при том же количестве серверов и без переписывания кода. Для большинства Laravel-проектов это лучший способ выжать максимум производительности без перехода на другие языки.