В этом руководстве я покажу вам, как настроить полноценную локальную среду с Nginx, MySql и Symfony всего за несколько минут. Более того, файл, о котором здесь пойдет речь, я сам использую для каждого проекта, над которым я начинаю работать.
Начнем же с простого вопроса: сколько времени занимает создание сайта?
Чтобы ответить на этот вопрос, позвольте мне рассказать вам одну историю. Много лет назад я жил в маленькой деревушке на окраине Лондона (Великобритания). Почти каждый вечер после работы я поднимался на холмы за пределами центра города, чтобы посмотреть на свет лондонских небоскребов. Я мечтал работать веб-разработчиком в одном из этих зданий. Чтобы добиться этого я поставил себе две задачи:
улучшить свои навыки и, как следствие, мое резюме;
продемонстрировать свои навыки, создав новый сайт в качестве портфолио;
Этот план сработал очень хорошо, и ровно через 12 месяцев я уже жил и работал в самом центре Лондона.
С тех пор изменилось многое. Я получил новую работу, приобрел другие навыки и завел много связей в этой области. Однако все это время кое-что оставалось неизменным. Мой сайт не изменялся с тех самых пор. Прошло целых пять лет, и я, наконец, решил обновить свой личный сайт. Сделаю я это с применением Symfony 6 и Docker. Что, вы тоже хотите попробовать? Нет ничего проще! Я подробно все объясню в этой статье.
Что такое Symfony?
Сразу оговорюсь, что эта статья не о том, что из себя представляют PHP-фреймворки в целом и Symfony в частности. Я уже писал, что это такое и почему Symfony 6 идеально подходит для вашего следующего проекта. Но если в двух словах, Symfony — один из самых популярных PHP-фреймворков, доступных в настоящее время. Это набор компонентов, хорошо собранных вместе, нацеленных облегчить жизнь разработчика. Не говоря уже о том, что проект на нем работает быстрее и с меньшим количеством головной боли.
Что такое Докер?
Вторая важная составляющая проекта — наличие Docker. О нем я тоже уже писал в прошлом. Вот статья, которая показывает, как контейнеризировать ваше PHP-приложение с помощью Docker. В двух словах, Docker — это платформа, которая помогает разработчикам собирать и запускать приложения. Контейнеры представляют наборы процессов, изолированных от основной операционной системы. На основе этих контейнеровы вы можете создавать целые проекты. Это очень удобно. Почему? Потому что теперь ваш ноутбук дома и компьютер в офисе будут “загружать” один и тот же контейнер. Наличие такого контейнера избавит вас от множества хлопот и проблем при работе над вашим проектом.
Что вам нужно, чтобы начать?
Symfony — это PHP-фреймворк, так что нам нужен сервер, на котором может работать PHP.
Не будем забывать, что нам также нужен какой-либо способ хранить данные. Это означает, что нам нужна база данных.
Нужно ли нам устанавливать все это? Никак нет! Единственное, что нам нужно, это Docker. Он позаботится о том, чтобы подтянуть все остальное.
Если вы хотите сохранить ваш проект или обновить его в будущем, вы можете добавить к нему контроль версий. С этим отлично справится GIT.
Настало время настроить проект. Давайте же начнем!
Создание docker-compose
Docker — идеальный инструмент, который можно достать из рукава в любой ситуации, когда вам нужно задействовать какой-то конкретный процесс. При создании многоконтейнерного приложения, подобного тому, которое мы собираемся реализовать в этой статье, нам понадобится еще один инструмент. Compose — это инструмент, который определяет и запускает многоконтейнерные Docker приложения. Эти контейнеры определяются через YAML-файл с именем docker-compose. Вот ссылка на полную документацию по Compose, ну а я обращу ваше внимание на то, что этот файл может иметь один или несколько ключей. Мы собираемся использовать три из них, а именно:
version указывает версию файла docker-compose. Это позволяет Docker понять, что он должен делать;
networks определяет отношения между сервисами. Так мы указываем, какие из них связаны друг с другом;
service перечисляет все контейнеры, необходимые данному проекту. Здесь мы также предоставляем подробную информацию о том, как эти сервисы должны запускаться;
Для реализации нашего проекта нужно три разных сервиса. Первый — это сервер, на котором будет запущено наше приложение. Затем нам нужно добавить PHP. Указываем, какую версию мы хотим. Нам также нужна база данных, в которой хранятся наши данные. Все эти сервисы будут связаны друг с другом. Это означает, что я должен создать сеть, которая свяжет их вместе. Вот как будет выглядеть этот файл в итоге:
services:
nginx-service:
image: nginx:stable-alpine
container_name: nginx-container
ports:
- "8080:80"
volumes:
- ./app:/var/www/project
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
depends_on:
- php81-service
- mysql8-service
networks:
- nginx-php81-mysql8
php81-service:
build:
context: .
dockerfile: ./php/Dockerfile
container_name: php81-container
ports:
- "9000:9000"
volumes:
- ./app:/var/www/project
networks:
- nginx-php81-mysql8
mysql8-service:
image: mysql:8
container_name: mysql8-container
ports:
- "3306:3306"
volumes:
- ./mysql:/var/lib/mysql
command: --default-authentication-plugin=mysql_native_password --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
restart: always # always restart unless stopped manually
environment:
MYSQL_ROOT_PASSWORD: secret
MYSQL_PASSWORD: secret
networks:
- nginx-php81-mysql8
networks:
nginx-php81-mysql8
Учитывая количество элементов, которые могут быть в этом файле, здесь я не буду на них останавливаться.
Конфигурация Nginx
Учитывая, что высокоуровневые элементы наших сервисов уже установлены, пришло время углубиться в детали.
Начнем с конфигурации нашего сервера. Наш файл docker-compose указывает, что конфигурация будет находиться внутри папки Nginx. Давайте создадим default.conf
и добавим в него кое-что.
Я не являюсь экспертом по настройке серверов, к слову, мне это не и нужно, поскольку Symfony предоставляет несколько готовых конфигураций сервера в зависимости от наших потребностей. Заходим на официальную страницу конфигурации Nginx Symfony и копируем то, что нам нужно.
server {
listen 80;
index index.php;
server_name localhost;
root /var/www/project/public;
location / {
try_files $uri /index.php$is_args$args;
}
location ~ ^/index\.php(/|$) {
fastcgi_pass php82-service:9000;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
fastcgi_buffer_size 128k;
fastcgi_buffers 4 256k;
fastcgi_busy_buffers_size 256k;
internal;
}
location ~ \.php$ {
return 404;
}
error_log /var/log/nginx/project_error.log;
access_log /var/log/nginx/project_access.log;
}
Вы могли заметить, что я добавил несколько новых полей. Эти изменения связаны с файлом docker-composer, который мы обновили ранее. Мы хотим прослушивать порт 80, который связан с портом 8080 в нашем контейнере Nginx.
Кроме того, я изменил server_name на localhost, чтобы немного упростить наш пример. Мы используем имя сервиса PHP в качестве значения поля fastcgi_pass.
И последнее, что касается ошибок 502. У Nginx есть плохая привычка наслаиваться на Symfony, что усложняет отладку. Чтобы решить эту проблему, нам нужно увеличить размер буфера FastCGI.
Докерфайл PHP
Существует разница между тем, как мы извлекаем контейнер сервера, и тем, как мы обновляем контейнер PHP. Вместо извлечения образа из DockerHub PHP использует Dockerfile. В этом есть много преимуществ. Одним из наиболее важных является то, что мы можем персонализировать то, как мы хотим, чтобы PHP работал.
Давайте создадим папку PHP в корне нашего проекта и добавим в нее докерфайл.
FROM php:8.1-fpm
RUN apt-get update && apt-get install -y zlib1g-dev g++ git libicu-dev zip libzip-dev zip \
&& docker-php-ext-install intl opcache pdo pdo_mysql \
&& pecl install apcu \
&& docker-php-ext-enable apcu \
&& docker-php-ext-configure zip \
&& docker-php-ext-install zip
WORKDIR /var/www/project
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
RUN curl -sS https://get.symfony.com/cli/installer | bash
RUN mv /root/.symfony/bin/symfony /usr/local/bin/symfony
Первая задача, которую выполняет этот файл, — получение образа PHP 8.1. Если вы еще не очень хорошо в этом разбираетесь и хотите узнать больше, вот учебник по основам PHP. Затем этот файл запускает серию команд, которые устанавливают расширения, запускают базу данных, PDO и т.д. Он подготавливает рабочий каталог и устанавливает composer и интерфейс командной строки Symfony. Нам нужно всего 15 строк, чтобы все это реализовать.
База данных
Сервис базы данных работает аналогично PHP. Я показывал вам файл docker-compose выше. Давайте же теперь пройдемся по полям и разберем некоторые детали.
В нашем случае мы будем использовать готовый образ. Это будет MySQL с тегом 8.
Поле портов указывает, какой порт необходим для связи между сервисом и хостом. В нашем случае это будет порт 4306.
Официальная документация говорит нам, что нам нужно добавить еще три поля:
В первом файл перечисляет ряд сведений о базе данных.
Restart указывает, что Docker должен делать в случае сбоя. В данном случае мы хотим, чтобы MySQL всегда автоматически перезапускался.
Environment содержит массив элементов, которые мы используем для добавления учетных данных пользователя.
Наконец, том (volume) будет создан из var/lib/mysql
в каталог /mysql. Mysql является частью сети. Сейчас нам не нужно обновлять какую-либо конфигурацию базы данных.
Запуск сервера
Теперь, когда все настроено, пора приступать к интересной части. Я собираюсь показать вам в чем тут вся суть и как заставить этот проект работать.
Первым делом начнем с сервера. Как вы можете видеть, том проекта указывает путь /var/www/project
. В файле конфигурации Nginx в корне у нас есть папка public
, внутри которой находится индексный файл под названием index.php
. Что нам нужно сделать, так это добавить эту папку и этот файл в каталог нашего приложения. Там мы можем просто вывести сообщение ‘Hello world! ‘, показывающее, что сервер работает правильно.
Последний шаг — создать эти образы и запустить сервисы. Docker позволяет нам сделать это с помощью одной команды, которая выглядит следующим образом:
docker-compose up -d - build
Она развернет образы, извлечет все необходимое из Docker Hub и покажет URL локального хоста, который мы сможем использовать для просмотра нашего сообщения.
Установка Symfony
До сих пор мы лишь настраивали контейнеры, чтобы наш проект работал. С этого момента мы начинаем фактическую работу над веб-частью проекта. Настало время устанавливать Symfony на сервер и запускать нашу IDE.
Для начала мы собираемся удалить папку. В настоящее время внутри нашего каталога приложений есть папка public. Единственной целью этой папки было проверить, работает ли наш сервер. Теперь, когда мы убедились, что все работает, нам больше не нужен index.php.
С помощью нашей первой команды нам нужно попасть внутрь контейнера PHP. Мы можем сделать это, набрав в терминале следующее:
docker exec -it php81-container bash
Эта команда позволяет попасть в терминал или в наш PHP-контейнер. Оказавшись внутри, мы можем подтянуть и создать наш проект Symfony.
composer create-project symfony/skeleton:"6.2." .
Теперь все новые файлы должны быть в папке app. Если вы зайдете в свой браузер, вы сможете увидеть главную страницу Symfony.
Генерация Gitignore
Теперь, когда мы установили наше веб-приложение, мы можем приступить к важынм деталям.
Прежде всего, я хочу показать вам gitignore.io. Это приложение позволяет создавать файлы gitignore, вводя немного информации для контекста. Речь идет о такой информации, как над какой SO вы работаете, язык программирования по вашему выбору и используемая IDE.
При нажатии кнопки “Create” будет создан файл gitignore. Вот его пример:
# Created by https://www.toptal.com/developers/gitignore/api/symfony,phpunit,macos
# Edit at https://www.toptal.com/developers/gitignore?templates=symfony,phpunit,macos### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### macOS Patch ###
# iCloud generated files
*.icloud
### PHPUnit ###
# Covers PHPUnit
# Reference: https://phpunit.de/
# Generated files
.phpunit.result.cache
.phpunit.cache
# PHPUnit
/app/phpunit.xml
/phpunit.xml
# Build data
/build/
### Symfony ###
!app/cache/.gitkeep
!app/logs/.gitkeep
# Email spool folder
/app/spool/
# Cache, session files and logs (Symfony3)
/var/cache/
/var/logs/
/var/sessions/
!var/cache/.gitkeep
!var/logs/.gitkeep
!var/sessions/.gitkeep
# Logs (Symfony4)
/var/log/
!var/log/.gitkeep
# Parameters
/app/config/parameters.yml
/app/config/parameters.ini
# Managed by Composer
/app/bootstrap.php.cache
/var/bootstrap.php.cache
/bin/
!bin/console
!bin/symfony_requirements
/vendor/
# Assets and user uploads
/web/bundles/
/web/uploads/
# PHPUnit
# Build data
# Composer PHAR
/composer.phar
# Backup entities generated with doctrine:generate:entities command
**/Entity/*~
# Embedded web-server pid file
/.web-server-pid
### Symfony Patch ###
/web/css/
/web/js/Installing Doctrine
Последнее, что нам осталось сделать перед тем, как я закончу эту статью, — настроить базу данных и установить ORM.
ORM позволяет вам делать запросы и извлекать записи в виде объектов ООП. Это идеально нам подходит, потому что мы получаем возможность манипулировать результатом так, как мы хотим. Чтобы добавить доктрину в наш проект, нам нужно запросить ее через composer. В командной строке PHP введите следующее:
composer require doctrine
Мы можем использовать ключевое слово doctrine только потому, что используем Symfony flex, чтобы скачать рецепты. На самом деле, что Symfony делает под капотом, так это извлекает пакет с именем symfony/orm-pack
, который содержит весь необходимый код.
Еще один шаг, который на осталось сделать, — это детали подключения к базе данных. Мы делаем это, обновляя env-файл в корне нашего PHP-проекта. Поле, которое нам нужно обновить, это DATABASE_URL. Это строка, которая должна содержать все учетные данные нашей базы данных.
DATABASE_URL="mysql://db_user:db_password@127.0.0.1:3306/db_name?serverVersion=5.7"
Пришло время создать нашу базу данных. И это тоже можно сделать с помощью всего одной простой команды. Введите в терминале exit, чтобы остановить bash, и запустите команду доктрины.
docker-compose run - rm php81-service php bin/console doctrine:database:create
Заключение
Symfony — отличный инструмент для создания веб-приложений. В сочетании с мощью Docker разработка любого проекта станет намного проще и приятнее. Это может казаться сложным поначалу, но если вы будете следовать моему пошаговому руководству, я уверен, что вы не столкнетесь с какими-либо трудностями. Вы на своем опыте убедитесь, как легко запустить этот проект.
На днях прошел открытый урок «Обновление до php 8.1 с помощью rector», в рамках которого мы:
применили rector к коду на php 8 для автоматического использования возможностей 8.1,
обсудили наюнсы перехода с myclabs/php-enum на built-in enum,
определили readonly-свойства и их влияние на тесты.
Запись урока можно посмотреть на странице онлайн-курса "Symfony Framework".
Комментарии (7)
pliashkou
00.00.0000 00:00Единственная вещь, которая мне тут не понравилась - это необходимость каждый раз подключаться к контейнеру через bash чтобы поставить зависимости. То же самое придётся делать каждый раз, когда команду нужно будет выполнить. Может есть другое варианты без установки php/composer на хост ?
Спасибо за статью!
olku
00.00.0000 00:00Есть иной подход - включать vendors в репозиторий. Повышает секьюрность и ускоряет развертывание.
SiGGthror
00.00.0000 00:00Запускать нужные тулзы внутри контейнера, и маунтить хостовую систему внутрь него.
Например, чтобы выполнить composer update достаточно просто запустить контейнер композера с флагом --rm и смаунтить внутрь него хостовую fs.
Можно подрубить Makefile, тогда становится жить чуть проще.
SiGGthror
00.00.0000 00:00+4Вообще статья достаточно слабая.
Это означает, что я должен создать сеть
Нет, не означает, потому что по дефолту команда docker-compose up создает отдельную сеть при запуске. Вообще кейсы когда надо указать сеть достаточно редкие, я с таким встречался только один раз, когда надо было чтобы тулза в одном контейнере могла достучаться до minikube.
Еще не ясно зачем пробрасывать 9000 порт у php, вряд ли мы захотим с хоста к нему присоединиться, а порт это действие занимает.
А вообще, раз уж мы говорим о докере, то локальная разработка только часть проблемы, ведь дальше мы захотим все это как-то деплоить, и делать это желательно не руками, а значит нам надо:
Настроить в ci/cd билд контейнеров
Настроить деплой этих контейнеров в какое-то окружение
И вот под эти цели все что описано выше слабо подходит.
kik_krsk
Спасибо за статью. У вас в конфиге nginx php82, а в остальных местах 81. Ещё бы можно было про xdebug написать.