Переделываем Laravel Sail для production

2024.02.10
Рассмотрим Laravel Sail, его проблемы и совместимость с production, а затем попытаемся преобразовать этот удобный инструмент к production виду.

В прошлом году я активно начал изучать фреймворк Laravel. Использование началось с Laravel Sail и это оказалось действительно удобно для быстрого старта. По мере углубления оказалось что Sail не годиться для production, но не хочется терять столь удобный инструмент, поэтому попытаемся привести его к нужному виду.

Краткое знакомство с Laravel Sail

В документации Laravel есть инструкция по настройке окружения Laravel + Docker через пакет Sail. Это окружение в Docker действительно настраивается легко и просто.

Упрощенно Laravel Sail - это bash скрипт, предоставляющий удобный интерфейс для взаимодействия с приложениями в Docker контейнерах.

Конечно есть кодовая база на php, которая встраивается в приложение, генерация docker-compose.yml файла, и возможность без знаний Docker сконфигурировать среду разработки. Однако, большая часть использований для меня свелась к bash скрипту, потому что просто управлять приложениями в контейнерах (в рамках работы с фреймворком).

Рассмотрим некоторые детали.

Чтобы поднять новый проект нужно (example-app заменить на название будущего проекта):

$ curl -s https://laravel.build/example-app | bash

В книге "UNIX И LINUX РУКОВОДСТВО СИСТЕМНОГО АДМИНИСТРАТОРА" пишут:

Направление результатов работы команды curl в оболочку в среде системных администраторов рассматривается как типичный промах новичков, поэтому, если вы это сделаете, никому об этом не говорите. Исправить ситуацию легко: просто сохраните сценарий во временном файле, а затем запустите сценарий на отдельном этапе после успешного завершения загрузки.

А чтобы установить Sail в существующий проект, например в склонированный репозиторий с Laravel, то нужно:

$ docker compose run --rm \
    -u "$(id -u):$(id -g)" \
    laravelsail/php82-composer:latest \
    composer install --ignore-platform-reqs

Теперь добавим алиас sail в ~/.bashrc чтобы не писать длинный путь ./vendor/bin/sail каждый раз:

alias sail='[ -f sail ] && sh sail || sh vendor/bin/sail'

А теперь можно использовать магию Sail, вот некоторые примеры (остальное в документации):

$ sail up
$ sail composer require 
$ sail php script.php

Именно эта магия оказалась повседневной при разработке приложений на Laravel.

Проблемы штатного окружения

После инициализации приложения через Sail у нас в корне появляется docker-compose.yml, при помощи которого поднимается инфраструктура нашего приложения в docker. На ранних этапах прототипирования все устраивало, но когда речь зашла о переезде на сервер, встала необходимость думать о production окружении.

Мой прототип реализовывал web API, а также нужна отдача статики. И первая проблема это CORS.

Мне привычно использовать Nginx, тем более что проблемы с CORS решаются просто:

add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' '*' always;
add_header 'Access-Control-Allow-Headers' '*' always;

if ($request_method = 'OPTIONS') {
    return 204;
}

Вторая проблема была в производительности, точнее в привычном для меня production понимании организации инфраструктуры с заделом на будущее.

Если еще раз посмотреть в docker-compose.yml, который был создан после инициализации приложения, то можно увидеть что внутри единственного контейнера функционирует веб-сервер для разработки при помощи php artisan, который реализован на PHP.

Нам же для production нужен php-fpm, а значит контейнер с php придется менять, но здесь есть несколько нюансов:

Решение: Laravel Sail Production

В этом репозитории я решил вышеперечисленные проблемы удобным для меня способом:

Обязательно нужно создать .env и задать нужные параметры, чтобы окружение вело себя предсказуемо.

Для инициализации нового приложения нужно следовать документации Sail, а затем можно взять конфигурацию из репозитория. Либо можно заморочиться с инициализацией нового проекта на новом окружении из репозитория примерно так:

# поднимаем инфраструктуру
$ docker compose up -d

# создаем проект
$ docker compose exec -u "$(id -u):$(id -g)" php composer create-project laravel/laravel example-app

# перемещаем содержимое директории example-app в корень
$ docker compose exec -u "$(id -u):$(id -g)" php mv ./example-app/* .

# все что не переместилось из директории example-app перемещаем в корень руками, совмещая файлы

Для инициализации существующего приложения с окружением как в репозитории достаточно:

$ docker compose run --rm -u "$(id -u):$(id -g)" php composer install

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

В телеграм канале DevOps от первого лица можно оставить комментарий или почитать интересные истории из практики DevOps