Skip to content

Запуск в продакшене ​

Продакшен должен запускать Rapira после перезагрузки и восстанавливать сервер после сбоев. Он также должен обновлять код без потери запросов и сохранять логи. Эта страница описывает юнит systemd, обратный прокси, проверки работоспособности, метрики и постоянные настройки воркеров.

Rapira не задаёт структуру развёртывания. Она не требует конкретного пути конфигурации или супервизора процессов. Эта страница задаёт соглашение для остальной документации. Сначала установите бинарник согласно разделу Установка.

Rapira также доступна как образ ghcr.io/rapira-rs/rapira. Скопируйте его файлы в образ приложения через COPY --from. В контейнере используйте политику перезапуска среды выполнения вместо systemd. Остальные настройки не изменяются. См. раздел Docker.

Юнит systemd ​

Rapira может заменить php-fpm. Мастер-процесс создаёт, отслеживает и заменяет воркеры. Каждый пул запускает фиксированное число воркеров, которое задаёт processes. Systemd должен отслеживать только мастер-процесс. Отдельный менеджер процессов не нужен.

Пакеты .deb и .rpm устанавливают бинарник и встроенный PHP. Они не устанавливают юнит службы или php.ini. Эти файлы содержат настройки конкретного сайта. Обновления пакетов не должны заменять их. Установленные файлы перечислены в разделе Установка.

Создайте /etc/systemd/system/rapira.service:

ini
[Unit]
Description=Rapira PHP application server
After=network.target

[Service]
Type=exec
WorkingDirectory=/srv/app
ExecStart=/usr/bin/rapira serve /etc/rapira/rapira.toml
ExecReload=/bin/kill -USR2 $MAINPID
KillMode=mixed
Restart=on-failure
RuntimeDirectory=rapira
Environment=PHPRC=/etc/rapira

[Install]
WantedBy=multi-user.target

Перезагрузите конфигурацию systemd:

bash
sudo systemctl daemon-reload

Включите Rapira с параметром --now:

bash
sudo systemctl enable --now rapira

Юнит использует следующие настройки:

  • Type=exec: Rapira работает на переднем плане. Процесс, который запускает systemd, является мастер-процессом, поэтому $MAINPID указывает на него.
  • ExecReload: команда systemctl reload rapira отправляет SIGUSR2 мастер-процессу. Этот сигнал запускает описанную ниже перезагрузку.
  • KillMode=mixed: systemd отправляет сигнал остановки только мастер-процессу. Затем мастер отправляет SIGQUIT воркерам и ожидает их. После TimeoutStopSec systemd отправляет SIGKILL всей группе. Без KillMode=mixed остановка может прервать текущие запросы.
  • Restart=on-failure: systemd перезапускает Rapira после сбоя. После обычной остановки перезапуск не выполняется.
  • RuntimeDirectory=rapira: systemd создаёт /run/rapira при запуске и удаляет его при остановке. Приведённые ниже примеры хранят pid-файл и Unix-сокет в этом каталоге.
  • Environment=PHPRC: PHP использует этот каталог для поиска php.ini.

Запуск не от root

Добавьте User= и Group= в блок [Service]. Systemd передаст этой учётной записи право владения RuntimeDirectory. После этого учётная запись сможет создать pid-файл и Unix-сокет в /run/rapira/. Обычно она не может создавать файлы непосредственно в /run.

Для двух приложений на одном хосте нужны отдельные файлы конфигурации, юниты и адреса прослушивания. Их можно задать шаблонным юнитом systemd, например rapira@.service. Каждый экземпляр инициализирует PHP и создаёт отдельный пул воркеров.

Пути конфигурации ​

Это руководство использует /etc/rapira/rapira.toml для настроек Rapira. Оно хранит php.ini в том же каталоге и задаёт PHPRC=/etc/rapira. Rapira не содержит эти пути в бинарнике. Аргумент CONFIG принимает любой путь. PHP использует PHPRC для поиска конфигурации. При необходимости используйте другие пути.

Rapira может работать без php.ini. Значения по умолчанию отправляют диагностику PHP в лог, а не в HTTP-ответ. Создайте /etc/rapira/php.ini для настройки OPcache, ограничения памяти или часового пояса. Настройки диагностики описаны в разделе Логирование.

В PHP 8.4 OPcache - это отдельный файл opcache.so. Загрузите его строкой zend_extension в php.ini, как описано в разделе php.ini. В PHP 8.5 OPcache входит в libphp.

Относительный http.pool.entrypoint использует каталог файла конфигурации как базовый. Поэтому в этой структуре entrypoint = "index.php" означает /etc/rapira/index.php. Используйте абсолютный путь входного скрипта в продакшене. supervisor.pidfile использует то же правило.

Каждый воркер перед запуском PHP делает каталог входного скрипта рабочим каталогом. Операции PHP с файлами по относительным путям используют этот каталог. PHP не ищет php.ini в рабочем каталоге, поэтому задайте PHPRC. Настройка WorkingDirectory= действует только для мастер-процесса. Мастер использует её как базовый каталог для относительного пути CONFIG и относительного пути unix: в адресе прослушивания. Все ключи и значения по умолчанию описаны в разделе Конфигурация.

Обратный прокси ​

Rapira принимает открытый HTTP и не содержит настроек TLS. Прокси завершения TLS принимает HTTPS от клиента, расшифровывает соединение и передаёт Rapira открытый HTTP. Используйте для этой задачи nginx, Caddy, HAProxy или облачный балансировщик. Подключите прокси к Rapira через петлевой интерфейс или Unix-сокет. Публичный адрес Rapira также использует открытый HTTP.

toml
[http]
listen = "127.0.0.1:8000"
# listen = "unix:/run/rapira/rapira.sock"

Rapira создаёт Unix-сокет с режимом 0666. К сокету может подключиться любой процесс с доступом к каталогу времени выполнения. Rapira не настраивает режим сокета. Ограничьте доступ с помощью прав каталога. Для этого юнита задайте RuntimeDirectoryMode=0750. В Group= укажите группу, в которую входит учётная запись прокси.

Передавайте поля с дефисами, например X-Forwarded-For. Не используйте имена вида X_Forwarded_For. В режимах Classic и Worker имя с _ или . может соответствовать тому же ключу $_SERVER, что и имя с дефисами. В этих режимах Rapira по умолчанию удаляет каждое имя, которое содержит символ, отличный от латинской буквы ASCII, цифры или дефиса. Преобразование имён и параметр http.unsafe_field_names описаны в разделе HTTP.

Rapira может обслуживать статические ресурсы с помощью middleware статических файлов. Прокси не требуется вторая копия корневого каталога документов. Вместо этого ресурсы может обслуживать прокси или CDN.

Деплой без простоя ​

Разверните новый код. Затем перезагрузите Rapira:

bash
sudo systemctl reload rapira

Команда отправляет SIGUSR2 мастер-процессу. Мастер заменяет по одному воркеру и позволяет текущим запросам завершиться. Если воркер превышает process_control_timeout_secs, мастер отправляет SIGTERM, а затем SIGKILL. Это завершает текущий запрос. Последовательность замены описана в разделе Модель процессов.

Если systemd не управляет процессом, отправьте сигнал мастер-процессу. Задайте supervisor.pidfile для хранения идентификатора процесса. Создайте каталог pid-файла перед запуском Rapira. Также можно выбрать существующий каталог. Мастер не запускается, если он не может записать файл.

toml
[supervisor]
pidfile = "/run/rapira/rapira.pid"
process_control_timeout_secs = 30
bash
kill -USR2 "$(cat /run/rapira/rapira.pid)"

Только мастер-процесс записывает pid-файл. Он удаляет файл во время управляемого завершения. Оставшийся файл может указывать на SIGKILL, сбой процесса или сбой системы.

process_control_timeout_secs ограничивает начальное ожидание при остановке и каждое ожидание готовности нового воркера. После ожидания при остановке мастер отправляет SIGTERM. Через одну секунду он отправляет SIGKILL. Задайте TimeoutStopSec systemd больше этого полного интервала.

Соединения получают меньше времени на завершение: тайм-аут управления минус меньшее из двух значений: пять секунд или половина тайм-аута. По умолчанию это 25 секунд. Ответы, которые превышают этот срок, могут быть прерваны при остановке или перезагрузке.

Что не изменяет перезагрузка

Перезагрузка заменяет воркеры, но не мастер-процесс. Мастер сохраняет бинарник Rapira и настройки из rapira.toml и php.ini. Он также сохраняет набор дескрипторов gRPC, файл токенов [grpc.auth] и разделяемую память OPcache. Перезапустите Rapira после изменения одного из этих файлов. Также перезапустите Rapira при opcache.validate_timestamps = 0. При этой конфигурации перезагрузка не заменяет кешированные опкоды.

Логи ​

Rapira пишет отфильтрованные записи лога в stderr. Systemd передаёт stderr в журнал. В продакшене используйте JSON для stderr:

toml
[log]
level = "info"
format = "json"

Каждая строка содержит один объект с timestamp, level, target и fields. Объект fields содержит message и другие поля события. Временная метка использует UTC по RFC 3339. Rapira экранирует символы новой строки в сообщениях. Journald передаёт объект сборщикам логов без изменений.

bash
journalctl -u rapira -f

Настройте сборщик логов для чтения журнала юнита. Также можно передать stderr Rapira непосредственно сборщику. Сборщик может разобрать каждую запись как JSON без регулярных выражений. Уровни для целей и переопределение RUST_LOG описаны в разделе Логирование.

Проверки работоспособности и метрики ​

Добавьте таблицу [observability], чтобы обслуживать пробы работоспособности и метрики Prometheus. Тогда Rapira запускает ещё один процесс, который не выполняет PHP. GET /livez показывает, что мастер-процесс работает. GET /readyz показывает, что в каждом пуле PHP есть воркер в состоянии idle или active. GET /metrics возвращает метрики в текстовом формате Prometheus.

toml
[observability]
listen = "127.0.0.1:9180"

[observability.metrics]

[observability.probes]

Эндпоинты не используют аутентификацию и TLS. Используйте петлевой адрес или Unix-сокет. Коды состояния, примеры проб и справочник метрик описаны в разделе Метрики и проверки работоспособности.

Обновление воркеров и таймауты запросов ​

В режимах Worker и Dispatcher воркер сохраняет состояние приложения между запросами. Поэтому утечка памяти может постепенно увеличивать память воркера. Используйте следующие две настройки:

toml
[http.pool]
max_requests = 500
request_terminate_timeout_secs = 30

Таблица [grpc.pool] принимает те же ключи.

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

request_terminate_timeout_secs ограничивает время выполнения одного запроса. Rapira завершает и заменяет воркер, который превышает лимит. Значение обеих настроек по умолчанию равно нулю, и это отключает их. Включите их для продакшена.

Раздел Модель процессов описывает размер пула, задержки замены и обработку сбоев воркеров.