Долгое время я развертывал свои приложения на Symfony традиционным способом: PHP установлен прямо на сервере, веб-сервер вроде Nginx или Apache, Composer установлен на машине, а база данных настроена отдельно.
Это работало хорошо. На самом деле, именно так я деплоил многие из своих проектов.
Затем, в профессиональной среде, Docker стал частью обязательного рабочего процесса. Мне пришлось переосмыслить подход к деплою. Вместо того чтобы относиться к серверу как к среде, которую нужно настраивать вручную, я мог описать эту среду прямо в проекте.
Именно тогда Docker стал для меня гораздо более понятным.
В этой статье я покажу, как развертываю приложение на Symfony с использованием Docker, FrankenPHP, MariaDB и Docker Compose.
Цель состоит не в том, чтобы представить единственный правильный способ деплоя Symfony. Это просто практический подход, который можно адаптировать под ваши собственные проекты. В этом примере я использую PHP 8.4 и MariaDB, но вы, конечно, можете использовать другую поддерживаемую версию PHP или СУБД в зависимости от требований вашего приложения.
1. Почему стоит использовать Docker для Symfony?
Без Docker сервер для Symfony может выглядеть примерно так:
Server
├── PHP
├── Composer
├── MariaDB
├── Nginx
└── Symfony applicationКаждый компонент необходимо устанавливать и настраивать прямо на сервере.
Это может стать проблемой, когда ваша среда разработки и продакшн-среда не идентичны.
Например, вы можете вести разработку на PHP 8.5, в то время как на сервере используется PHP 8.2. Какое-нибудь PHP-расширение может быть установлено локально, но отсутствовать на продакшне. Конфигурация базы данных также может отличаться в разных средах.
С помощью Docker мы можем описать среду с помощью файлов конфигурации:
Server
└── Docker
├── FrankenPHP
└── MariaDBСреда становится воспроизводимой.
Пожалуй, это одно из главных преимуществ, ради которых я использую Docker: мне больше не нужно так сильно полагаться на серверы, настроенные вручную.
2. Почему именно FrankenPHP?
Самое интересное в этой установке то, что мы не будем использовать традиционную архитектуру:
Nginx → PHP-FPM → SymfonyВместо этого мы задействуем FrankenPHP.
FrankenPHP — это современный сервер приложений, созданный поверх Caddy, который может напрямую обслуживать PHP-приложения.
Это позволяет нам упростить архитектуру:
Browser
│
▼
FrankenPHP
│
▼
Symfony
│
▼
MariaDBНам больше не нужен дополнительный контейнер Nginx только для того, чтобы перенаправлять запросы в PHP-FPM.
Для приложения на Symfony это может быть очень удобным подходом, особенно если вы хотите получить относительно простую контейнеризированную архитектуру.
3. Архитектура проекта
Для этого примера мы будем использовать два основных сервиса:
- FrankenPHP для запуска Symfony;
- MariaDB для базы данных.
Docker Compose будет оркестрировать оба сервиса.
Итоговая архитектура будет выглядеть следующим образом:
Internet
│
▼
FrankenPHP
│
▼
Symfony
│
▼
MariaDBВозможная структура проекта:
my-project/
├── docker/
│ └── php/
│ └── Dockerfile
├── public/
├── src/
├── config/
├── migrations/
├── composer.json
├── compose.yaml
└── ...Файл Dockerfile определяет нашу среду PHP/FrankenPHP, в то время как compose.yaml описывает сервисы, из которых состоит наше приложение.
4. Создание образа FrankenPHP
Начнем с Dockerfile .
Мы можем использовать официальный образ FrankenPHP:
FROM dunglas/frankenphp:php8.4Я использую здесь PHP 8.4 просто потому, что эта версия применяется в моем примере. Вы можете заменить ее на другую версию PHP, поддерживаемую вашим приложением Symfony.
Затем мы можем установить расширения PHP, необходимые для проекта:
RUN install-php-extensions \
pdo_mysql \
intl \
zip \
opcacheТочный набор расширений будет зависеть от вашего приложения.
Для приложения Symfony, использующего Doctrine с MariaDB, pdo_mysql является одним из важнейших расширений.
Мы также можем установить Composer:
COPY --from=composer:2 /usr/bin/composer /usr/bin/composerТеперь у нас есть образ, содержащий PHP, FrankenPHP и Composer.
5. Добавление приложения Symfony
Нам нужно скопировать наше приложение Symfony внутрь контейнера.
В Dockerfile :
WORKDIR /appCOPY . .После этого наше приложение будет доступно по адресу:
/appМы можем установить зависимости Composer в процессе сборки образа:
RUN composer install \
--no-dev \
--optimize-autoloader \
--no-interactionДля продакшн-образа это предотвращает установку зависимостей, предназначенных только для разработки.
6. Настройка FrankenPHP
FrankenPHP использует Caddy в качестве веб-сервера, поэтому мы можем использовать Caddyfile для настройки обслуживания Symfony.
Например:
{
frankenphp
}:80 {
root * /app/public encode zstd gzip php_server
}
Важная часть здесь:
root * /app/publicSymfony использует директорию public/ в качестве точки входа в приложение.
Директива:
php_serverуказывает FrankenPHP обслуживать PHP-приложение. Вы можете узнать об этом подробнее на странице: Configuring a Web Server (Symfony Docs).
Следовательно, нам не нужно вручную настраивать Nginx и PHP-FPM.
7. Добавление MariaDB с помощью Docker Compose
Теперь давайте опишем нашу базу данных в compose.yaml .
Упрощенная конфигурация может выглядеть следующим образом:
services:
php:
build:
context: .
dockerfile: docker/php/Dockerfile
ports:
- "80:80"
depends_on:
- databasedatabase:
image: mariadb:11
environment:
MARIADB_DATABASE: app
MARIADB_USER: app
MARIADB_PASSWORD: password
MARIADB_ROOT_PASSWORD: root
volumes:
- database_data:/var/lib/mysqlvolumes:
database_data:
Теперь у нас есть два сервиса:
php
databaseПервый содержит Symfony и FrankenPHP.
Второй содержит MariaDB.
Я использую MariaDB 11 здесь в качестве примера. В этом выборе нет ничего специфичного для Docker: вы можете использовать другую версию MariaDB или другую СУБД, если это лучше подходит для вашего проекта.
8. Зачем использовать волюм для MariaDB?
Контейнер спроектирован так, чтобы его можно было легко удалить и воссоздать.
Это нормально для контейнера с приложением, но очевидно обстоит иначе для базы данных.
Если мы удалим контейнер MariaDB без персистентного хранилища, мы можем потерять его данные.
Именно поэтому у нас есть:
volumes:
- database_data:/var/lib/mysqlи:
volumes:
database_data:Docker сохраняет данные базы данных в волюм (volume), который не зависит от жизненного цикла контейнера.
Важное правило, которое следует запомнить:
Контейнеры можно воссоздавать. Данные должны быть персистентными.
9. Подключение Symfony к MariaDB
Это одна из первых вещей, которая может вызывать путаницу при начале работы с Docker.
Вне Docker у нас может быть:
DATABASE_URL="mysql://app:password@127.0.0.1:3306/app"Внутри Docker 127.0.0.1 ссылается на текущий контейнер, а не на контейнер MariaDB.
Поэтому мы используем имя сервиса Docker:
DATABASE_URL="mysql://app:password@database:3306/app"Здесь:
database— это сервис, определенный в compose.yaml .
Docker автоматически обеспечивает разрешение DNS между сервисами в одной сети.
Следовательно, Symfony может подключаться к MariaDB с помощью:
database:3306Это мелкая деталь, но понимание ее значительно упрощает работу с сетью Docker.
10. Запуск среды
Как только конфигурация готова, мы можем собрать образ:
docker compose buildЗатем запустить сервисы:
docker compose up -dЧтобы проверить их статус:
docker compose psМы должны увидеть что-то вроде:
NAME STATUS
my-project-php running
my-project-db runningТеперь наше приложение Symfony обслуживается FrankenPHP.
11. Выполнение команд Symfony внутри Docker
Еще одно преимущество такого сетапа заключается в том, что нам не нужно устанавливать PHP или Composer прямо на продакшн-сервере.
Мы можем выполнять команды внутри контейнера.
Например:
docker compose exec php php bin/console aboutДля установки зависимостей:
docker compose exec php composer installДля запуска миграций:
docker compose exec php php bin/console doctrine:migrations:migrateИ для очистки кэша:
docker compose exec php php bin/console cache:clearСледовательно, на хост-сервере не нужно устанавливать полноценную среду PHP.
Все, что требуется для работы приложения, находится внутри контейнера.
12. Подготовка Symfony к продакшну
Конфигурация для продакшна, очевидно, должна отличаться от конфигурации для разработки.
Например:
APP_ENV=prod
APP_DEBUG=0Нам также следует избегать коммита паролей и других секретных данных в Git.
Например:
DATABASE_URL=...
APP_SECRET=...должны передаваться через переменные окружения или с помощью подходящего решения для управления секретами.
Принцип прост:
Код можно версионировать. Секреты — нет.
13. Деплой новой версии
Когда приложение контейнеризировано, деплой новой версии становится гораздо более предсказуемым.
После получения последней версии:
git pullмы можем пересобрать образ:
docker compose buildи перезапустить сервисы:
docker compose up -dЗатем запустить все ожидающие миграции:
docker compose exec php \
php bin/console doctrine:migrations:migrate --no-interactionИ очистить кэш Symfony:
docker compose exec php \
php bin/console cache:clearМы также можем установить продакшн-зависимости с помощью:
docker compose exec php composer install \
--no-dev \
--optimize-autoloader \
--no-interactionКак только этот ручной рабочий процесс заработает правильно, его можно автоматизировать с помощью CI/CD-решения, такого как GitHub Actions.
14. А как насчет HTTPS?
В продакшене приложение, очевидно, не должно быть доступно только по протоколу HTTP.
Одно из преимуществ FrankenPHP заключается в том, что он построен на базе Caddy, который умеет работать с HTTPS и TLS-сертификатами при правильной настройке для продакшна.
На практике вам нужно:
- указать свой домен для сервера;
- настроить домен в веб-сервере;
- убедиться, что необходимые порты доступны;
- позволить Caddy/FrankenPHP получить сертификат;
- проверить, что обновление сертификата работает корректно.
Тогда архитектура может выглядеть так:
https://example.com
│
▼
FrankenPHP
│
▼
SymfonyТочная конфигурация будет зависеть от вашей инфраструктуры, особенно если перед Docker у вас уже есть реверс-прокси или другой веб-сервер.
15. Частые проблемы
Docker не устраняет проблемы деплоя. Он меняет место, где их нужно искать.
could not find driver
Если Symfony выводит:
could not find driverпроверьте, установлено ли необходимое расширение PHP в вашем образе Docker:
RUN install-php-extensions pdo_mysqlSymfony не может подключиться к MariaDB
Проверьте ваш DATABASE_URL .
Как правило, вам не следует использовать:
127.0.0.1Вместо этого используйте имя сервиса Docker:
databaseНапример:
DATABASE_URL="mysql://app:password@database:3306/app"Приложение возвращает ошибку 502
Проверьте логи FrankenPHP:
docker compose logs phpЛоги Docker должны быть одним из первых мест, куда вы заглядываете, когда контейнер ведет себя не так, как ожидалось.
Данные MariaDB пропали
Убедитесь, что у вас настроен персистентный волюм:
volumes:
- database_data:/var/lib/mysqlБез правильно настроенного персистентного хранилища воссоздание контейнера базы данных может привести к потере данных.
16. Шаг дальше: CI/CD
Как только деплой через Docker заработал вручную, следующим логическим шагом становится автоматизация.
Возможный рабочий процесс:
Git push
│
▼
GitHub Actions
│
├── Run tests
├── Build Docker image
└── Deploy
│
▼
Production
│
├── FrankenPHP
└── MariaDBС этого момента деплой новой версии больше не означает ручной запуск нескольких команд на сервере.
Одна и та же конфигурация Docker может использоваться для сборки, тестирования и развертывания приложения.
Именно здесь Docker становится особенно полезным в рамках CI/CD-процесса.
Заключение
Поначалу Docker может казаться излишне сложным, когда вы привыкли устанавливать PHP, MariaDB и веб-сервер прямо на машину.
Именно так я и чувствовал себя до того, как начал им пользоваться.
Но его ценность становится гораздо очевиднее при работе с разными средами или несколькими проектами.
С Docker приложение не так сильно зависит от того, что именно установлено и настроено на сервере. Среда становится частью самого проекта.
А с FrankenPHP мы можем еще больше упростить традиционную PHP-архитектуру, объединив веб-сервер и сервер приложений PHP в один сервис.
Для приложения Symfony итоговый сетап может быть относительно простым:
┌─────────────────┐
│ Internet │
└────────┬────────┘
│
▼
┌─────────────────┐
│ FrankenPHP │
│ │
│ Symfony │
└────────┬────────┘
│
▼
┌─────────────────┐
│ MariaDB │
└─────────────────┘Конечно, FrankenPHP — не единственный способ запуска Symfony, точно так же как MariaDB — не единственная база данных, которую вы можете использовать. Вы можете адаптировать этот сетап под другую версию PHP, PostgreSQL, MySQL или другую инфраструктуру в зависимости от требований вашего проекта.
Важна суть принципа: опишите среду вашего приложения, сделайте ее воспроизводимой и максимально автоматизируйте деплой.
Docker не обязательно делает ваш первый деплой проще. Он делает последующие деплои гораздо более предсказуемыми.
Комментарии (0)
Пока нет комментариев — будьте первым.