Пример docker-compose для развертывания веб-сервера с Laravel
Используемые термины: Docker, PHP, NGINX, MariaDB, Linux.
В инструкции мы рассмотрим простой пример сценария docker-compose, который позволит быстро развернуть инфраструктуру с Laravel. Ссылки на страницы с дополнительной информацией по работе с docker будут приведены в конце инструкции.
Laravel является фреймворком общего назначения, который разработан на PHP. Значит нам понадобится инфраструктура с веб-сервером, базой данных и интерпретатором PHP. В итоге мы получим систему, состоящую из:
- Веб-сервера NGINX.
- СУБД MariaDB.
- PHP версии 8.1.
- phpMyAdmin (опционально).
Нами будут выполнены следующие шаги:
Подготовка системы к работе
Создание docker-compose
Запуск веб-сервера
Развертывание Laravel
Получение сертификата от Let's Encrypt
Дополнительная информация
Также рекомендую к прочтению статью на хабре Laravel+Docker+Gitlab. С чего начать.
Готовим систему
Подготовим операционную систему к работе.
1. Установка Docker.
Для запуска сценария нам необходимо установить docker и docker-compose. Для каждого типа Linux необходимо выполнить свой набор команд.
Подробнее процесс описан в инструкции Установка Docker на Linux.
2. Каталог для работы.
Создадим и перейдем в каталог, где будут находиться файлы веб-сервера и сам сценарий docker-compose.
mkdir /opt/laravel
cd /opt/laravel
Команды ниже будут выполняться из расчета, что мы находимся в созданном каталоге.
Сценарий и настройка сервера
Создаем файл docker-compose:
vi docker-compose.yml
- ---
- services:
- laravel_nginx:
- image: nginx
- hostname: laravel_nginx
- container_name: laravel_nginx
- restart: unless-stopped
- environment:
- TZ: "Europe/Moscow"
- ports:
- - 80:80
- - 443:443
- volumes:
- - ./nginx/conf.d:/etc/nginx/conf.d
- - ./nginx/ssl:/etc/nginx/ssl
- - ./www_data:/usr/share/nginx/html
- laravel_php:
- build:
- context: ./php/
- hostname: laravel_php
- container_name: laravel_php
- restart: unless-stopped
- user: root
- environment:
- TZ: "Europe/Moscow"
- volumes:
- - ./www_data:/var/www/html
- - ./php/php.ini:/usr/local/etc/php/php.ini
- laravel_db:
- image: mariadb
- hostname: laravel_db
- container_name: laravel_db
- restart: unless-stopped
- environment:
- TZ: "Europe/Moscow"
- MARIADB_DATABASE: laravel
- MARIADB_USER: laravel
- MARIADB_PASSWORD: laravel123
- MARIADB_ROOT_PASSWORD: root_password123
- volumes:
- - ./mysql_data:/var/lib/mysql
- laravel_pma:
- image: phpmyadmin
- hostname: laravel_pma
- container_name: laravel_pma
- restart: unless-stopped
- environment:
- TZ: "Europe/Moscow"
- PMA_HOST: laravel_db
- UPLOAD_LIMIT: 50M
- ports:
- - 81:80
* сценарий создаст 4 контейнера:
- laravel_nginx — веб-сервер, на базе nginx.
- laravel_php — интерпретатор PHP.
- laravel_db — сервер баз данных MariaDB.
- laravel_pma — веб-инструмент для работы с базами данных MySQL/MariaDB.
5 - 18 | Создаем контейнер веб-сервера nginx. В качестве образа используем одноименный nginx. |
16 | Конфигурационные файлы будут находится в каталоге nginx/conf.d хостовой машины (в каталоге с docker-compose.yml) и /etc/nginx/conf.d внутри контейнера. |
17 | Каталоги для хранения сертификатов. |
18 | Файлы веб-приложения — www_data на хосте docker и /usr/share/nginx/html внутри контейнера. |
20 - 31 | Контейнер с PHP. |
21 - 22 | Мы не будем брать готовый образ и сами соберем контейнер с использованием Dockerfile. Создание и описание последнего будет ниже в инструкции. |
30 | Как и для веб-сервера мы используем каталог www_data с рабочими файлами сайта, но пробрасываем их в каталог /var/www/html внутри контейнера. |
31 | Файл с настройками PHP. |
33 - 45 | Контейнер с базой данных MariaDB. |
40 | При инициализации СУБД будет создана база с именем laravel. |
41 | При инициализации базы будет создан пользователь с именем laravel. |
42 | Пароль пользователя laravel будет laravel123. |
43 | Указываем пароль для суперпользователя root. |
47 - 57 | Контейнер с phpMyAdmin. |
54 | С помощью данной переменной окружения говорим, на каком контейнере запущена СУБД. |
55 | Разрешаем передачу файлов размером не более 50 Мб. По умолчанию ограничение в 2 Мб. |
57 | Обратите внимание, что данный контейнер мы вешаем на внешний 81 порт. |
Создадим каталог для хранения Dockerfile — он нам нужен для сборки контейнера с PHP:
mkdir php
Создадим в данном каталоге файл Dockerfile:
vi php/Dockerfile
- FROM php:8.1-fpm
- MAINTAINER Dmitriy Mosk <master@dmosk.ru>
- ENV TZ=Europe/Moscow
- RUN apt update && \
- apt -y --no-install-recommends install libonig-dev libxml2-dev zlib1g-dev libzip-dev libcurl4-openssl-dev && \
- docker-php-ext-install mysqli bcmath mbstring xml ctype zip pdo_mysql curl filter
* с помощью данного файла будет собран контейнер с интерпретатором php и компонентом php-fpm, принимающим запросы на обработку скриптов.
1 | В качестве базового образа берем php:8.1-fpm. Из его названия видно, что версия php будет 8.1. Меняем на версию, которая нужна, именно, вам, но не забываем сверить ее с документацией к Laravel. |
7 - 9 | Дополнительно установим php расширения, которые нужны для работы фреймворка. Данный список также можно найти на официальном сайте разработчика. |
Создадим конфигурационный файл php.ini:
vi php/php.ini
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT & ~E_NOTICE
short_open_tag = On
post_max_size = 1G
upload_max_filesize = 512M
date.timezone = "Europe/Moscow"
* где:
- error_reporting — для продуктива лучше не отображать информацию об устаревших функциях (E_DEPRECATED), плохой совместимости кода (E_STRICT) и информации о потенциальных проблемах (E_NOTICE).
- short_open_tag — позволяет использовать короткую нотацию для раскрытия блока код PHP.
- post_max_size — максимальный объем данных, которые можно передать на сервер методом POST.
- upload_max_filesize — максимальный объем загружаемого файла.
- date.timezone — часовой пояс.
Создаем каталог:
mkdir -p nginx/conf.d
И создаем конфигурационный файл nginx для виртуального домена:
vi nginx/conf.d/default.conf
- server {
- listen 80 default_server;
- listen 443 ssl default_server;
- server_name _;
- location ~ /.well-known {
- root /usr/share/nginx/html;
- allow all;
- }
- if ($scheme = 'http') {
- return 301 https://$host$request_uri;
- }
- ssl_certificate /etc/nginx/ssl/laravel.localnet.crt;
- ssl_certificate_key /etc/nginx/ssl/laravel.localnet.key;
- root /usr/share/nginx/html/public;
- error_log /var/log/nginx/error.log;
- index index.php;
- location / {
- try_files $uri $uri/ /index.php?$query_string;
- gzip_static on;
- }
- location ~ \.php$ {
- set $root_path /var/www/html/public;
- fastcgi_pass laravel_php:9000;
- fastcgi_index index.php;
- fastcgi_param SCRIPT_FILENAME $root_path$fastcgi_script_name;
- include fastcgi_params;
- fastcgi_param DOCUMENT_ROOT $root_path;
- }
- }
* данный файл описывает поведение и ответ веб-сервера nginx при обращении к nginx.
2 - 3 | Сервер будет слушать на портах 80 и 443. Обратите внимание, что описываемая конфигурация является конфигурацией по умолчанию, то есть, если обращение идет не на конкретный или несуществующий виртуальный домен, то будет применяться, именно, она. |
5 | Указывает, для какого домена будет действовать данная настройка. В нашем примере это домен по умолчанию, поэтому пишем просто _. |
7 - 10 | Данная настройка понадобится, если мы захотим получить бесплатный сертификат от Let's Encrypt. |
12 - 14 | Перенаправляет запросы с http на https. |
16 - 17 | Пути до файлов сертификатов. Необходимы для работы https. |
30 - 37 | Данный блок описывает поведение nginx при обработке файлов с расширением php. В нашем примере запросы будут передаваться контейнеру laravel_db. |
Создаем каталог для хранения сертификатов:
mkdir nginx/ssl
Генерируем самоподписанные сертификаты:
openssl req -new -x509 -days 1461 -nodes -out nginx/ssl/laravel.localnet.crt -keyout nginx/ssl/laravel.localnet.key -subj "/C=RU/ST=SPb/L=SPb/O=Global Security/OU=IT Department/CN=laravel.localnet/CN=www.laravel.localnet"
* данная команда сформирует ключи laravel.localnet.crt и laravel.localnet.key в каталоге nginx/ssl относительно нашей рабочей папки.
Для тестирования веб-сервера нам будет достаточно самозаверенного сертификата. На него будет ругаться браузер, но с игнорирование предупреждения, портал можно будет открыть.
Для продуктивной среды лучше получить сертификаты от доверенного центра (купить или получить бесплатно от Let's Encrypt).
Сценарий для развертывания веб-сервера готов.
Запуск и тестирование веб-сервера
Для создания и запуска контейнеров выполняем команду:
docker-compose up -d
Запуск контейнеров займет какой-то время. Ждем.
Создадим файл:
vi www_data/index.php
<?php
phpinfo();
?>
Можно проверить работу сервера из командной строки:
curl -k https://127.0.0.1
Мы должны получить ответ в виде html-страницы с параметрами PHP.
Того же самого эффекта можно добиться в браузере, перейдя по адресу https://<IP-адрес хоста docker>. При получении предупреждения безопасности, игнорируем его.
Установка и настройка Laravel
Мы подготовили сервер, теперь установим и запустим Laravel.
Смотрим образы с названием laravel, которые есть в системе:
docker images | grep laravel
Нам нужен образ для php. В нашем случае, название будет:
laravel-laravel_php
На основе найденного образа запустим контейнер:
docker run --rm -it -v ./www_data:/var/www/html laravel-laravel_php bash
* данный контейнер будет удален после работы с ним.
Скачаем php composer и установим его в каталог /usr/local/bin:
# curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
Проверим, что композер установлен:
# composer --version
Удалим ранее созданный файл index.php:
# rm -f index.php
С помощью php composer создадим новый проект laravel:
# composer create-project laravel/laravel ./
В текущую папку будут загружены необходимые скрипты и выполнены установки и настройки, необходимые для работы портала.
Сгенерируем ключ приложения Laravel:
# php artisan key:generate --ansi
* данный ключи будет использоваться для защиты сеансов пользователя.
Выставим владельца www-data для файлов в текущем каталоге:
# chown -R www-data:www-data ./
Выходим из контейнера:
# exit
Откроем теперь файл конфигурации:
vi www_data/.env
Найдем секцию настройки подключения к СУБД mysql и зададим нужные значения:
...
DB_CONNECTION=mysql
DB_HOST=laravel_db
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=laravel
DB_PASSWORD=laravel123
...
* где:
- DB_HOST — имя контейнера СУБД.
- DB_DATABASE — имя созданной базы данных.
- DB_USERNAME — логин для подключения к СУБД.
- DB_PASSWORD — пароль пользователя DB_USERNAME.
Почистим старый кэш и создадим новый:
docker-compose exec laravel_php php artisan config:cache
Мы можем увидеть что-то на подобие:
Configuration cache cleared!
Configuration cached successfully!
Проверяем, что фреймворк может работать с базой, выполнив незавершенные миграции:
docker-compose exec laravel_php php artisan migrate
Мы должны увидеть что-то на подобие:
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_000000_create_users_table (0.03 seconds)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated: 2014_10_12_100000_create_password_resets_table (0.03 seconds)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated: 2019_08_19_000000_create_failed_jobs_table (0.02 seconds)
Пробуем подключиться к порталу, перейдя по ссылке https://<IP-адрес хоста docker>. Если браузер выкинет предупреждение безопасности из-за самоподписанного сертификата, игнорируем, продолжая загрузку страницы.
* в нашем примере мы указали, что созданный виртуальный домен является конфигурацией по умолчанию. Если на нашем сервере много виртуальных доменов, то в URL необходимо указать доменное имя.
В итоге, мы должны увидеть страницу Laravel:
Laravel поднят.
Сертификат от Let's Encrypt
Ранее мы настроили веб-сервер с самоподписанным сертификатом, но в рабочей среде нам нужен сертификат, которому доверяют многие системы. Проще всего использовать бесплатный сертификат от Let's Encrypt. Для его получения сначала открываем наш файл docker-compose:
vi docker-compose.yml
И добавляем новый volume для контейнера laravel_nginx:
---
services:
laravel_nginx:
...
volumes:
...
- ./nginx/well-known/:/usr/share/nginx/html
* мы пробрасываем внутрь контейнера каталог nginx/well-known.
Теперь можно запустить команду:
docker run -it --rm --name certbot -v "/opt/laravel/nginx/ssl:/etc/letsencrypt" -v "/opt/laravel/nginx/well-known:/usr/share/nginx/html" certbot/certbot certonly --webroot --agree-tos --email postmaster@dmosk.ru --webroot-path /usr/share/nginx/html/ -d dmosk.ru -d www.dmosk.ru
* данная команда создаст временный контейнер, который запросит сертификат для домена dmosk.ru и его поддомена www.dmosk.ru. В качестве каталога, где будет размещен временный файл проверки будет использоваться /usr/share/nginx/html, который является каталогом /opt/laravel/nginx/well-known на хосте docker и также смотри в папку /usr/share/nginx/html внутри веб-контейнера laravel_nginx.
Если настройка выполнена верно, мы должны получить файлы сертификата, которые будут находиться в каталоге /opt/laravel/nginx/ssl/live/dmosk.ru. Пропишем новые сертификаты:
vi nginx/conf.d/dmosk.ru.conf
ssl_certificate /etc/nginx/ssl/live/dmosk.ru/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/live/dmosk.ru/privkey.pem;
Перечитаем конфигурацию веб-сервером. Сначала проверим ее:
docker exec laravel_nginx nginx -t
И если ошибок нет:
docker exec laravel_nginx nginx -s reload
Для продления сертификата используем команду:
docker run -it --rm --name certbot -v "/opt/laravel/nginx/ssl:/etc/letsencrypt" -v "/opt/laravel/nginx/well-known:/usr/share/nginx/html" certbot/certbot renew
Читайте также
Дополнительная информация по работе с docker и веб-серверами:
1. Создание собственного образа Docker.
2. Настройка веб-сервера в Docker (NGINX + PHP + MariaDB).