Установка Laravel, Nginx и MySQL с помощью Docker Compose
Development, LEMP Stack, Ubuntu | 1 Comment
В последнее время Docker часто используется для развертывания приложений, поскольку он упрощает этот процесс с помощью виртуальных контейнеров. Например, если вы используете стек LEMP (который состоит из PHP, Nginx и MySQL) и Laravel, Docker может значительно ускорить процедуру настройки приложения.
Docker Compose упрощает разработку, поскольку дает разработчикам возможность определять инфраструктуру, – включая сервисы приложений, сети и тома, – в едином файле. Docker Compose легко заменяет собой сразу несколько команд, в том числе docker container create и docker container run.
В этом мануале вы научитесь создавать приложение в фреймворке Laravel, используя Nginx как веб-сервер и MySQL как БД. Все это будет помещено в контейнеры Docker. Вы напишете полную конфигурацию приложения в файле docker-compose вместе с конфигурационными файлами для PHP, MySQL и Nginx.
Требования
- Сервер Ubuntu 18.04 и пользователь с привилегиями sudo. Все инструкции вы найдете здесь.
- Docker, установленный по мануалу Установка и использование Docker в Ubuntu 18.04.
- Docker Compose, установленный по мануалу Установка Docker Compose в Ubuntu 18.04 (раздел 1).
1: Загрузка Laravel и установка зависимостей
Для начала нужно загрузить последнюю версию Laravel и установить зависимости программы, в том числе и Composer, менеджер пакетов PHP уровня приложения. Используйте Docker, чтобы не устанавливать Composer глобально.
Перейдите в домашний каталог и клонируйте последнюю версию Laravel в laravel-app:
cd ~
git clone https://github.com/laravel/laravel.git laravel-app
Затем перейдите в каталог laravel-app:
cd ~/laravel-app
Теперь через образ composer смонтируйте каталоги проекта Laravel, чтобы избежать накладок глобальной установки Composer:
docker run --rm -v $(pwd):/app composer install
Опции -v и –rm в команде docker run создают контейнер, который привязывается к текущему каталогу до тех пор, пока вы его не удалите. Содержимое каталога ~/laravel-app скопируется в контейнер, а содержимое папки vendor, которую Composer создает внутри контейнера, будет скопировано в текущий каталог.
Теперь отредактируйте привилегии каталога, все права на него нужно передать вашему пользователю sudo:
sudo chown -R $USER:$USER ~/laravel-app
Это пригодится вам позже, когда вы будете работать с Dockerfile для образа приложения, поскольку так вы сможете работать с кодом и запускать процессы в контейнере через своего пользователя sudo.
2: Создание конфигурационного файла Docker Compose
Сборка приложений с помощью Docker Compose упрощает настройку и контроль версий в инфраструктуре. Чтобы настроить приложение Laravel, давайте создадим файл docker-compose и определим в нем сервисы веб-сервера, базы данных и приложения.
Откройте файл:
nano ~/laravel-app/docker-compose.yml
В файле docker-compose нужно определить три сервиса: app, webserver и db. Вставьте в файл такой код (только укажите свой сложный пароль root в MYSQL_ROOT_PASSWORD, переменной среды сервиса db):
version: '3'
services:
#PHP Service
app:
build:
context: .
dockerfile: Dockerfile
image: digitalocean.com/php
container_name: app
restart: unless-stopped
tty: true
environment:
SERVICE_NAME: app
SERVICE_TAGS: dev
working_dir: /var/www
networks:
- app-network
#Nginx Service
webserver:
image: nginx:alpine
container_name: webserver
restart: unless-stopped
tty: true
ports:
- "80:80"
- "443:443"
networks:
- app-network
#MySQL Service
db:
image: mysql:5.7.22
container_name: db
restart: unless-stopped
tty: true
ports:
- "3306:3306"
environment:
MYSQL_DATABASE: laravel
MYSQL_ROOT_PASSWORD: your_mysql_root_password
SERVICE_TAGS: dev
SERVICE_NAME: mysql
networks:
- app-network
#Docker Networks
networks:
app-network:
driver: bridge
В файл входят следующие сервисы:
- app: определение сервиса, которое содержит приложение Laravel и запускает кастомный образ Docker, его мы определим позже. Также оно присваивает значение /var/www для параметру working_dir в контейнере.
- webserver: загружает образ nginx:alpine и открывает порты 80 и 443.
- db: извлекает образ mysql:5.7.22 и определяет новые переменные среды, в том числе БД laravel для приложения и пароль пользователя root этой БД. Вы можете использовать любое имя для базы данных. Также вам следует заменить your_mysql_root_password собственным паролем. Это определение также связывает порт хоста 3306 и такой же порт контейнера .
Свойство container_name определяет имя контейнера, которое должно совпадать с именем сервиса. Если вы не определите это свойство, Docker будет присваивать контейнерам случайные имена (по умолчанию он выбирает имя исторической личности и случайное слово, разделяя их символом подчеркивания).
Для простоты взаимодействия между контейнерами сервисы подключаются к соединительной сети app-network. Соединительная сеть использует программный мост, который позволяет подключенным к этой сети контейнерам взаимодействовать друг с другом. Драйвер устанавливает правила хоста автоматически, чтобы контейнеры из разных соединительных сетей не могли взаимодействовать напрямую. Это повышает безопасность приложений, поскольку взаимодействовать в таких условиях смогут только связанные сервисы. Кроме того, так вы сможете указать разные сети и сервисы, подключающиеся к функциям: например, клиентские сервисы приложениймогут использовать сеть frontend, а серверные — сеть backend.
3: Постоянное хранение данных
Docker предоставляет удобные средства для постоянного хранения данных. Для этого в данном тестовом приложении мы будем использовать тома и монтируемые образы. Мы будем хранить файлы базы данных, приложения и конфигурации. Тома очень гибкие в резервном копировании, их легко сохранять по истечении жизненного цикла контейнера, а монтируемые образы упрощают редактирование кода во время разработки и немедленно отражают изменения файлов или каталогов хоста в контейнерах. Мы используем оба варианта.
Важно! Монтируемые образы позволяют менять файловую систему хоста через работающие в контейнерах процессы, в том числе создавать, изменять и удалять важные системные файлы и каталоги. Это может повлиять на процессы в системе хоста, не связанные с Docker. Монтируемые образы следует использовать очень осторожно.
Для постоянного сохранения базы данных MySQL определите том dbdata в файле docker-compose, в определении сервиса db:
...
#MySQL Service
db:
...
volumes:
- dbdata:/var/lib/mysql
networks:
- app-network
...
Том dbdata используется для постоянного сохранения содержимого /var/lib/mysql в контейнере. Это позволяет останавливать и перезапускать сервис db, не теряя данных.
Вставьте в конец файла определение тома dbdata:
...
#Volumes
volumes:
dbdata:
driver: local
Теперь можно использовать этот том для разных сервисов.
Затем добавьте к сервису db привязку монтируемого образа для конфигурационных файлов MySQL, которые мы создадим позже.
...
#MySQL Service
db:
...
volumes:
- dbdata:/var/lib/mysql
- ./mysql/my.cnf:/etc/mysql/my.cnf
...
Это привяжет файл ~/laravel-app/mysql/my.cnf к каталогу /etc/mysql/my.cnf в контейнере.
А теперь добавьте монтируемые образы в сервис webserver. Образов будет два: один для кода приложения, а второй — для определения настроек Nginx.
#Nginx Service
webserver:
...
volumes:
- ./:/var/www
- ./nginx/conf.d/:/etc/nginx/conf.d/
networks:
- app-network
Первый образ привязывает код приложения из каталога ~/laravel-app к каталогу /var/www внутри контейнера. Конфигурационный файл, добавляемый в ~/laravel-app/nginx/conf.d/, также монтируется в папку /etc/nginx/conf.d/ в контейнере, что позволяет менять содержимое каталога по мере необходимости.
Теперь добавьте следующие привязки образов в сервис app для кода приложения и конфигурационных файлов:
#PHP Service
app:
...
volumes:
- ./:/var/www
- ./php/local.ini:/usr/local/etc/php/conf.d/local.ini
networks:
- app-network
Сервис app привязывает монтируемый образ каталога ~/laravel-app, который содержит код приложения, к /var/www. Это позволяет ускорить разработку, поскольку все изменения в локальном каталоге приложения сразу же отразятся в контейнере. Также конфигурационный файл PHP ~/laravel-app/php/local.ini привязывается к файлу /usr/local/etc/php/conf.d/local.ini в контейнере. Мы создадим локальный конфигурационный файл PHP в разделе 5.
Теперь файл docker-compose имеет такой вид:
version: '3'
services:
#PHP Service
app:
build:
context: .
dockerfile: Dockerfile
image: digitalocean.com/php
container_name: app
restart: unless-stopped
tty: true
environment:
SERVICE_NAME: app
SERVICE_TAGS: dev
working_dir: /var/www
volumes:
- ./:/var/www
- ./php/local.ini:/usr/local/etc/php/conf.d/local.ini
networks:
- app-network
#Nginx Service
webserver:
image: nginx:alpine
container_name: webserver
restart: unless-stopped
tty: true
ports:
- "80:80"
- "443:443"
volumes:
- ./:/var/www
- ./nginx/conf.d/:/etc/nginx/conf.d/
networks:
- app-network
#MySQL Service
db:
image: mysql:5.7.22
container_name: db
restart: unless-stopped
tty: true
ports:
- "3306:3306"
environment:
MYSQL_DATABASE: laravel
MYSQL_ROOT_PASSWORD: your_mysql_root_password
SERVICE_TAGS: dev
SERVICE_NAME: mysql
volumes:
- dbdata:/var/lib/mysql/
- ./mysql/my.cnf:/etc/mysql/my.cnf
networks:
- app-network
#Docker Networks
networks:
app-network:
driver: bridge
#Volumes
volumes:
dbdata:
driver: local
Сохраните и закройте файл.
Теперь пора создать пользовательский образ вашего приложения.
4: Создание Dockerfile
Docker позволяет настраивать среду внутри отдельных контейнеров с помощью файла Dockerfile. Файл Dockerfile создает пользовательские образы, которые затем можно использовать для установки зависимостей приложения и изменения настроек. Вы можете загружать созданные образы в Docker Hub или в частный реестр.
Файл Dockerfile должен находиться в каталоге ~/laravel-app. Создайте этот файл:
nano ~/laravel-app/Dockerfile
Этот Dockerfile будет определять базовый образ и необходимые команды для сборки образа приложения Laravel. Добавьте в файл такие строки:
FROM php:7.2-fpm
# Copy composer.lock and composer.json
COPY composer.lock composer.json /var/www/
# Set working directory
WORKDIR /var/www
# Install dependencies
RUN apt-get update && apt-get install -y \
build-essential \
mysql-client \
libpng-dev \
libjpeg62-turbo-dev \
libfreetype6-dev \
locales \
zip \
jpegoptim optipng pngquant gifsicle \
vim \
unzip \
git \
curl
# Clear cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
# Install extensions
RUN docker-php-ext-install pdo_mysql mbstring zip exif pcntl
RUN docker-php-ext-configure gd --with-gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ --with-png-dir=/usr/include/
RUN docker-php-ext-install gd
# Install composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# Add user for laravel application
RUN groupadd -g 1000 www
RUN useradd -u 1000 -ms /bin/bash -g www www
# Copy existing application directory contents
COPY . /var/www
# Copy existing application directory permissions
COPY --chown=www:www . /var/www
# Change current user to www
USER www
# Expose port 9000 and start php-fpm server
EXPOSE 9000
CMD ["php-fpm"]
Сначала Dockerfile создает образ на основе образа php:7.2-fpm. Это образ Debian с предустановленным экземпляром PHP FastCGI PHP-FPM. Также этот файл устанавливает необходимые пакеты для Laravel: mcrypt, pdo_mysql, mbstring, imagick и composer.
Директива RUN определяет команды для обновления, установки и настройки параметров внутри контейнера, включая выделенного пользователя и группу www. Директива WORKDIR указывает рабочий каталог приложения (в данном случае это каталог /var/www ).
Используя отдельного пользователя и группу с ограниченными правами доступа, вы снижаете уязвимости при запуске контейнеров Docker (по умолчанию они запускаются с правами root). Чтобы не запускать контейнер с привилегиями root, мы создали пользователя www с правами на чтение и запись для каталога /var/www.
Это делается с помощью команды COPY и флага –chown для копирования прав каталога приложения.
Команда EXPOSE открывает в контейнере порт 9000 для сервера php-fpm. CMD определяет команду, которая будет запускаться после создания контейнера. Здесь CMD содержит команду php-fpm, которая запускает сервер.
Сохраните и закройте файл.
Теперь пора определить конфигурации PHP.
5: Настройка PHP
Итак, мы определили инфраструктуру в файле docker-compose. Теперь можно настроить сервис PHP в качестве процессора для входящих запросов Nginx.
Для настройки PHP нужно создать файл local.ini в каталоге php. Это файл, который в разделе 2 мы привязали к файлу /usr/local/etc/php/conf.d/local.ini в контейнере. Имея этот файл, вы сможете игнорировать файл по умолчанию php.ini, который PHP считывает при запуске.
Создайте каталог php:
mkdir ~/laravel-app/php
Затем откройте файл local.ini:
nano ~/laravel-app/php/local.ini
Чтобы продемонстрировать настройку PHP, мы добавим следующий код для установки ограничений размера выгруженных файлов:
upload_max_filesize=40M
post_max_size=40M
Директивы upload_max_filesize и post_max_size задают максимальный допустимый размер выгружаемых файлов и показывают, как задавать конфигурации php.ini из local.ini. Все параметры конфигурации PHP, которые вы хотите игнорировать, можно поместить в файл local.ini.
Сохраните и закройте файл.
Теперь пора настроить веб-сервер.
6: Настройка Nginx
Теперь можно настроить Nginx на поддержку PHP-FPM в качестве сервера FastCGI для обслуживания динамического контента. Сервер FastCGI разработан на основе двоичного протокола для взаимодействия интерактивных программ с веб-сервером.
Для Nginx нужно создать в папке ~/laravel-app/nginx/conf.d/ файл app.conf с конфигурацией сервисов.
Создайте каталог nginx/conf.d/:
mkdir -p ~/laravel-app/nginx/conf.d
Затем создайте файл app.conf:
nano ~/laravel-app/nginx/conf.d/app.conf
Добавьте в файл такой код, чтобы определить конфигурацию Nginx:
server {
listen 80;
index index.php index.html;
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
root /var/www/public;
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass app:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
location / {
try_files $uri $uri/ /index.php?$query_string;
gzip_static on;
}
}
Блок server задает конфигурацию веб-сервера Nginx с помощью директив:
- listen: определяет порт, который сервер прослушивает для получения входящих запросов.
- error_log и access_log: определяют файлы для записи логов.
- root: определяет путь к корневому каталогу, формируя полный путь к любому запрошенному файлу в локальной файловой системе.
В блоке location для php директива fastcgi_pass указывает, что сервис app прослушивает сокет TCP по порту 9000. Благодаря этому сервер PHP-FPM прослушивает запросы через сеть, а не через сокет Unix. Сокет Unix имеет небольшое преимущество в скорости по сравнению с сокетом TCP, однако у него нет сетевого протокола и он пропускает сетевой стек. Если хосты находятся в одной системе, использование сокета Unix может иметь смысл, но если сервисы работают на разных хостах, сокет TCP гораздо лучше, поскольку позволяет подключаться к распределенным сервисам. Поскольку контейнеры app и webserver работают на разных хостах, в данной конфигурации сокет TCP будет эффективнее.
Сохраните и закройте файл.
Благодаря привязке, созданной в разделе 2, все изменения в каталоге nginx/conf.d/ прямо отразятся в контейнере webserver.
7: Настройка MySQL
Настроив PHP и Nginx, вы можете включить MySQL как базу данных для приложения.
Для MySQL нужно создать файл my.cnf в каталоге mysql. Этот файл мы привязали к файлу /etc/mysql/my.cnf внутри контейнера в разделе 2. Привязка монтируемого образа позволяет игнорировать все параметры my.cnf, когда это потребуется.
Чтобы посмотреть, как это работает, давайте добавим в файл my.cnf параметры, которые включают лог общих запросов и задают лог-файл.
Создайте каталог mysql:
mkdir ~/laravel-app/mysql
Создайте файл my.cnf:
nano ~/laravel-app/mysql/my.cnf
Поместите в файл следующий код, чтобы включить лог запросов и задать расположение лога:
[mysqld]
general_log = 1
general_log_file = /var/lib/mysql/general.log
Файл my.cnf включает лог, задавая параметру general_log значение 1. Параметр general_log_file указывает, где будут храниться логи.
Сохраните файл и закройте его.
Все готово, пора запускать контейнеры.
8: Запуск контейнеров и изменение настроек среды
Итак, мы определили все сервисы в файле docker-compose и создали конфигурации для всех сервисов. Теперь можно запустить контейнеры. В качестве последнего шага мы создадим копию файла .env.example, который Laravel включает по умолчанию, и назовем ее .env, поскольку именно такой файл Laravel использует для определения среды:
cp .env.example .env
После запуска контейнеров мы вставим в этот файл параметры.
Теперь все сервисы определены в файле docker-compose, и вам нужно просто запустить одну команду, которая запустит все контейнеры, создаст тома и настройки и подключит сети:
docker-compose up -d
При первом запуске команда docker-compose up загрузит все необходимые образы Docker, что может занять некоторое время. После загрузки образов на локальный компьютер Compose создаст контейнеры. Флаг -d преобразует процесс в демон, что позволяет поддерживать работу контейнеров в фоновом режиме.
После завершения этого процесса используйте следующую команду, чтобы запросить список всех запущенных контейнеров:
docker ps
Вы увидите следующий вывод с данными о контейнерах app, webserver и db:
CONTAINER ID NAMES IMAGE STATUS PORTS
c31b7b3251e0 db mysql:5.7.22 Up 2 seconds 0.0.0.0:3306->3306/tcp
ed5a69704580 app digitalocean.com/php Up 2 seconds 9000/tcp
5ce4ee31d7c0 webserver nginx:alpine Up 2 seconds 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp
В этом выводе CONTAINER ID — это уникальный идентификатор контейнера, а NAMES перечисляет имена сервисов. Вы можете использовать оба эти идентификатора для доступа к контейнерам. IMAGE определяет имя образа каждого контейнера, а STATUS предоставляет данные о состоянии.
Теперь вы можете отредактировать файл .env в контейнере app, чтобы добавить необходимые параметры.
Откройте файл с помощью docker-compose exec, что позволяет запускать определенные команды в контейнерах. В данном случае команда откроет файл для редактирования:
docker-compose exec app nano .env
Найдите блок DB_CONNECTION и определите в нем особенности настройки вашей системы. Нужно изменить следующие поля:
- DB_HOST – нужно указать контейнер базы данных db.
- DB_DATABASE – укажите здесь БД laravel.
- DB_USERNAME – укажите имя пользователя БД. В этом случае мы используем laraveluser.
- DB_PASSWORD – надежный пароль этого пользователя.
DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=laraveluser
DB_PASSWORD=your_laravel_db_password
Сохраните и закройте файл.
Затем настройте ключ для приложения Laravel с помощью команды php artisan key:generate. Она сгенерирует ключ и скопирует его в файл .env, что защитит сессии пользователя и шифрованные данные:
docker-compose exec app php artisan key:generate
Теперь для запуска приложения у вас есть все необходимые настройки среды. Чтобы кэшировать эти настройки для ускорения загрузки приложения, запустите команду:
docker-compose exec app php artisan config:cache
Конфигурации будут загружены в /var/www/bootstrap/cache/config.php в контейнере.
Теперь откройте в браузере сайт http://your_server_ip. На экране появится главная страница приложения Laravel.
Теперь можно перейти к настройке данных пользователя БД laravel в контейнере db.
9: Создание пользователя MySQL
Установка MySQL по умолчанию предоставляет только учетную запись root с неограниченными привилегиями доступа к серверу СУБД. Обычно при работе с базой данных лучше не использовать администратора, root. Вместо этого лучше создать специального пользователя для базы данных нашего приложения.
Чтобы создать его, запустите интерактивную оболочку bash в контейнере db с помощью команды docker-compose exec:
docker-compose exec db bash
Внутри контейнера войдите в MySQL как root:
mysql -u root -p
Вам будет предложено ввести root пароль MySQL, заданный в файле docker-compose.
Для начала проверьте наличие базы данных laravel, которую вы определили в файле docker-compose. Запустите show databases для проверки существующих баз данных:
show databases;
В выводе вы должны увидеть БД laravel:
+--------------------+
| Database |
+--------------------+
| information_schema |
| laravel |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.00 sec)
Затем создайте пользователя, у которого будет доступ к этой базе данных. Мы используем имя laraveluser, но вы можете выбрать другое. Просто убедитесь, что имя пользователя и пароль соответствуют учетным данным, указанным ранее в файле .env.
GRANT ALL ON laravel.* TO 'laraveluser'@'%' IDENTIFIED BY 'your_laravel_db_password';
Сбросьте привилегии, чтобы сообщить серверу MySQL об изменениях:
FLUSH PRIVILEGES;
Закройте MySQL:
EXIT;
Выйдите из контейнера:
exit
10: Миграция данных и знакомство с консолью Tinker
Теперь приложение запущено, вы можете выполнить миграцию данных и поэкспериментировать с командой tinker, которая запускает консоль PsySH с загруженным приложением Laravel. PsySH — это консоль среды выполнения и интерактивный отладчик PHP, а Tinker — это REPL для Laravel. Команда tinker позволяет взаимодействовать с приложением Laravel из командной строки в интерактивной оболочке.
Сначала протестируйте соединение с MySQL с помощью команды Laravel artisan migrate, которая создаст в БД таблицу migrations внутри контейнера:
docker-compose exec app php artisan migrate
Эта команда выполняет миграцию таблиц Laravel. В случае успешной миграции вы увидите такой вывод:
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_000000_create_users_table
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated: 2014_10_12_100000_create_password_resets_table
После завершения миграции вы можете отправить запрос для проверки подключения к БД:
docker-compose exec app php artisan tinker
Проверьте соединение MySQL:
\DB::table('migrations')->get();
Вы получите такой результат:
=> Illuminate\Support\Collection {#2856
all: [
{#2862
+"id": 1,
+"migration": "2014_10_12_000000_create_users_table",
+"batch": 1,
},
{#2865
+"id": 2,
+"migration": "2014_10_12_100000_create_password_resets_table",
+"batch": 1,
},
],
}
Вы можете использовать tinker для взаимодействия с базами данных и для экспериментов с сервисами и моделями.
Теперь приложение Laravel работает, а вы готовы к дальнейшей разработке и экспериментам.
Заключение
Теперь на вашем сервере есть рабочее приложение на основе стека LEMP.
Docker Compose упрощает установку, так как позволяет с помощью одной команды создавать группы контейнеров Docker, определенные в одном файле.
Читайте также:
- Тестирование приложения в непрерывной интеграции с Docker и Docker Compose в Ubuntu 16.04
- Автоматическое развертывание приложений Laravel с помощью Deployer в Ubuntu 16.04
1 комментарий
на 8м этапе выдает ошибку service must be a mapping, not a NoneType. ругается на docker-compose.yml.
в чем может быть беда?