Site icon 8HOST.COM

Контейнеризация приложения Laravel для среды разработки с помощью Docker Compose в Ubuntu 18.04

Контейнеризация – это процесс адаптации компонентов приложения для работы в так называемых контейнерах. Контейнеры – это одноразовые изолированные среды, предназначенные для разработки, тестирования и развертывания приложений в производстве.

Данный мануал научит вас разрабатывать приложение Laravel в контейнерах с помощью Docker Compose. В итоге у вас будет тестовое приложение Laravel, состоящее из трех отдельных контейнеров:

Для простоты разработки и отладки приложения мы синхронизируем файлы с помощью совместно используемых томов. Также вы научитесь запускать Composer и Artisan в контейнере app с помощью команд docker-compose exec.

Требования

1: Загрузка тестового приложения

Для начала нужно загрузить тестовое приложение Laravel из этого репозитория на Github. Вам понадобится ветка tutorial-01, в которой хранится базовое приложение Laravel (такое мы разработали в мануале Установка и настройка Laravel на стеке LEMP в Ubuntu 18.04).

Чтобы загрузить необходимую версию кода данного приложения в свой домашний каталог, введите эти команды:

cd ~
curl -L https://github.com/do-community/travellist-laravel-demo/archive/tutorial-1.0.1.zip -o travellist.zip

Также для работы нам понадобится команда unzip – утилита для распаковки архивов. Если вы не установили этот инструмент ранее, сделайте это сейчас:

sudo apt update
sudo apt install unzip

Теперь извлеките содержимое загруженного архива и переименуйте полученный каталог, чтобы его было проще найти:

unzip travellist.zip
mv travellist-laravel-demo-tutorial-1.0.1 travellist-demo

Перейдите в каталог travellist-demo:

cd travellist-demo

Теперь нужно создать конфигурационный файл .env для настройки нашего тестового приложения.

2: Создание конфигурационного файла .env

Конфигурационные файлы Laravel хранятся в каталоге config в корневом каталоге приложения. Файл .env дополнительно используется для  зависимой от среды конфигурации (в том числе для хранения учетных данных и другой информации, которая может меняться от одного развертывания к другому). Этот файл не управляется системой контроля версий.

Важно!Конфигурационный файл среды содержит важную информацию о вашем сервере, в том числе учетные данные БД и ключи безопасности, а потому этот файл не стоит выкладывать в открытый доступ.

Приоритет файла .env  выше, чем у обычных конфигурационных файлов в каталоге config. Каждой отдельной установке в новую среду нужен индивидуальный файл среды, в котором определяются такие настройки, как параметры отладки и подключения к базе данных, URL приложения и некоторые другие опции (в зависимости от среды).

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

cp .env.example .env

Откройте этот файл с помощью текстового редактора:

nano .env

Конфигурационный файл .env для тестового приложения travellist содержит параметры локальной базы данных MySQL с адресом хоста 127.0.0.1. Обновите переменную DB_HOST и укажите в ней сервис базы данных, который мы создадим в среде Docker немного позже. В этом мануале мы назовем этот сервис db. Замените указанное значение DB_HOST именем вашего сервиса БД.

APP_NAME=Travellist
APP_ENV=dev
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost:8000
LOG_CHANNEL=stack
DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=travellist
DB_USERNAME=travellist_user
DB_PASSWORD=password
...

При желании можно также изменить имя БД, имя пользователя и пароль. Эти переменные будут использоваться в дальнейшем, при создании файла docker-compose.yml для настройки сервисов.

Сохраните и закройте файл.

3: Создание файла Dockerfile

Сервисы MySQL и Nginx будут основаны на стандартных образах из Docker Hub, однако для их работы нам все равно нужно создать индивидуальный образ контейнера приложения. Для этого нужно создать новый Dockerfile.

Образ приложения travellist будет основан на образе php:7.4-fpm (это официальный образ PHP). Мы установим в базовой среде PHP-FPM несколько дополнительных модулей PHP и Composer (инструмент для управления зависимостями).

Кроме того, мы создадим нового пользователя системы – это необходимо, чтобы иметь при разработке приложения возможность выполнять команды artisan и composer. Параметр uid отвечает за соответствие uid пользователя в контейнере и пользователя хоста, на котором работает Docker. Следовательно, все файлы, созданные этими командами в контейнере, будут с соответствующими правами созданы на хосте. Также это позволяет вам использовать на хосте любой удобный редактор кода для разработки приложения внутри контейнеров.

Итак, создайте новый Dockerfile:

nano Dockerfile

Вставьте в ваш Dockerfile следующие строки:

FROM php:7.4-fpm
# Arguments defined in docker-compose.yml
ARG user
ARG uid
# Install system dependencies
RUN apt-get update && apt-get install -y \
git \
curl \
libpng-dev \
libonig-dev \
libxml2-dev \
zip \
unzip
# Clear cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
# Install PHP extensions
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd
# Get latest Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# Create system user to run Composer and Artisan Commands
RUN useradd -G www-data,root -u $uid -d /home/$user $user
RUN mkdir -p /home/$user/.composer && \
chown -R $user:$user /home/$user
# Set working directory
WORKDIR /var/www
USER $user

Сохраните и закройте файл.

В самом начале Dockerfile определяется базовый образ, который мы используем, это php:7.4-fpm.

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

Затем создается новый системный пользователь, а аргументы user и uid в начале Dockerfile помогают настроить его параметры (эти значения Docker Compose вставляет во время сборки).

В конце файла определяется рабочий каталог по умолчанию (/var/www) и выполняется переход в сессию нового пользователя. Благодаря этому вы сможете подключаться как обычный пользователь и будете находиться в правильном каталоге при запуске команд composer и artisan в контейнере приложения.

4: Настройка Nginx и дампа базы данных

Создавая среды разработки с помощью Docker Compose, разработчики часто сталкиваются с необходимостью совместно использовать конфигурации или файлы инициализации с контейнерами сервисов (чтобы настроить или включить эти сервисы в процессе загрузки). Благодаря этому отладка среды через конфигурационные файлы во время разработки приложения становится проще.

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

Для Nginx мы создадим файл travellist.conf, он будет отвечать за обслуживание приложения. Сначала создайте каталог docker-compose/nginx:

mkdir -p docker-compose/nginx

Теперь откройте в этом каталоге файл по имени travellist.conf.

nano docker-compose/nginx/travellist.conf

Скопируйте и вставьте в этот файл такие строки:

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;
}
}

Теперь Nginx будет прослушивать порт 80 и использовать index.php как индекс страницу по умолчанию. Также этот файл определяет корневой каталог /var/www/public, а затем настраивает использование сервиса app по порту 9000 для обработки файлов *.php.

Сохраните и закройте файл.

Чтобы настроить MySQL, создайте общедоступный дамп, который будет импортироваться во время инициализации контейнера. Эта функция доступна в образе MySQL 5.7, который мы используем в этом контейнере.

Создайте новый каталог для файлов MySQL в каталоге docker-compose:

mkdir docker-compose/mysql

Теперь откройте файл .sql:

nano docker-compose/mysql/init_db.sql

Этот дамп MySQL в качестве основы использует БД, которую мы настроили в первом мануале этой серии, Установка и настройка Laravel на стеке LEMP в Ubuntu 18.04. MySQL создаст новую таблицу по имени places, которая затем будет заполнена названиями городов.

Добавьте в файл следующий код:

DROP TABLE IF EXISTS `places`;
CREATE TABLE `places` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`visited` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO `places` (name, visited) VALUES ('Berlin',0),('Budapest',0),('Cincinnati',1),('Denver',0),('Helsinki',0),('Lisbon',0),('Moscow',1),('Nairobi',0),('Oslo',1),('Rio',0),('Tokyo',0);

Таблица places состоит из трех полей: id, name и visited. В поле visited хранится флаг, который будет отмечать еще не посещенные города. Вы можете самостоятельно добавить другие города в тестовую таблицу. Затем сохраните и закройте файл.

5: Настройка мультиконтейнерной среды с помощью Docker Compose

С помощью Docker Compose можно создавать мультиконтейнерные среды для работающих на Docker приложений. Для создания настраиваемых сред с применением нескольких контейнеров инструмент Docker Compose использует определения сервисов. При этом контейнеры в среде смогут совместно использовать сети и тома для хранения данных. Благодаря этому обеспечивается полная интеграция компонентов приложения.

Для определений сервисов нужно создать новый файл по имени docker-compose.yml. Как правило, этот файл хранится в корневом каталоге и определяет контейнеризованную среду, включая базовые образы для построения контейнеров и способы взаимодействия между сервисами.

В docker-compose.yml мы определим три разных сервиса: app, db и nginx.

Сервис app соберет образ travellist на основе нашего файла Dockerfile. Определяемый этим сервисом контейнер запустит сервер php-fpm, который обработает код PHP и вернет результат сервису nginx (он будет работать в отдельном контейнере). Сервис mysql определяет контейнер, в котором будет запущен сервер БД MySQL 5.7. Все сервисы будут использовать общую соединительную сеть по имени travellist.

Файлы приложения будут синхронизироваться сервисами app и nginx с помощью монтирования привязок. Монтирование привязок — это инструмент среды разработки, который позволяет настроить производительную двустороннюю синхронизацию между хостом и контейнерами.

Создайте файл docker-compose.yml в корневом каталоге приложения:

nano docker-compose.yml

Обычно docker-compose.yml начинается с определения версии, после чего следует блок services, который определяет все сервисы. Совместно используемые сети, как правило, определяются в конце этого файла.

Скопируйте этот шаблон и вставьте его в файл docker-compose.yml:

version: "3.7"
services:
networks:
travellist:
driver: bridge

Теперь давайте расширим блок services, добавив в него сервисы app, db и nginx.

Сервис app

Сервис app настраивает контейнер по имени travellist-app. Он собирает новый образ Docker на основе Dockerfile в том же каталоге, что и файл docker-compose.yml. Новый образ по имени travellist сохраняется на локальном компьютере.

Корневой каталог приложения находится в контейнере nginx, но при этом файлы приложения должны находиться в контейнере app (чтобы мы могли выполнять задачи командной строки с помощью инструмента Artisan).

Скопируйте следующее определение сервиса и вставьте его в блок services в файле docker-compose.yml:

app:
build:
args:
user: 8host
uid: 1000
context: ./
dockerfile: Dockerfile
image: travellist
container_name: travellist-app
restart: unless-stopped
working_dir: /var/www/
volumes:
- ./:/var/www
networks:
- travellist

Эти параметры делают следующее:

Сервис db

Сервис db будет использовать готовый образ MySQL 5.7 с Docker Hub. Docker Compose автоматически загружает файлы .env, находящиеся в том же каталоге, что и файл docker-compose.yml, а потому мы можем получить параметры БД из файла .env, который мы создали ранее.

Добавьте следующее определение сервиса в блок services сразу же после определения сервиса app:

db:
image: mysql:5.7
container_name: travellist-db
restart: unless-stopped
environment:
MYSQL_DATABASE: ${DB_DATABASE}
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
MYSQL_PASSWORD: ${DB_PASSWORD}
MYSQL_USER: ${DB_USERNAME}
SERVICE_TAGS: dev
SERVICE_NAME: mysql
volumes:
- ./docker-compose/mysql:/docker-entrypoint-initdb.d
networks:
- travellist
Вот что делают эти параметры:

Сервис nginx

Сервис nginx использует готовый образ Nginx, основанный на облегченном дистрибутиве Linux под названием Alpine. Он создает контейнер по имени travellist-nginx и использует определение ports для переадресации с порта 8000 системы хоста на порт 80  контейнера.

Добавьте следующее определение сервиса в раздел services сразу после сервиса db:

nginx:
image: nginx:1.17-alpine
container_name: travellist-nginx
restart: unless-stopped
ports:
- 8000:80
volumes:
- ./:/var/www
- ./docker-compose/nginx:/etc/nginx/conf.d
networks:
- travellist

Вот что делают эти строки:

Полный файл docker-compose.yml

Готовый файл docker-compose.yml будет иметь такой вид:

version: "3.7"
services:
app:
build:
args:
user: 8host
uid: 1000
context: ./
dockerfile: Dockerfile
image: travellist
container_name: travellist-app
restart: unless-stopped
working_dir: /var/www/
volumes:
- ./:/var/www
networks:
- travellist
db:
image: mysql:5.7
container_name: travellist-db
restart: unless-stopped
environment:
MYSQL_DATABASE: ${DB_DATABASE}
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
MYSQL_PASSWORD: ${DB_PASSWORD}
MYSQL_USER: ${DB_USERNAME}
SERVICE_TAGS: dev
SERVICE_NAME: mysql
volumes:
- ./docker-compose/mysql:/docker-entrypoint-initdb.d
networks:
- travellist
nginx:
image: nginx:alpine
container_name: travellist-nginx
restart: unless-stopped
ports:
- 8000:80
volumes:
- ./:/var/www
- ./docker-compose/nginx:/etc/nginx/conf.d/
networks:
- travellist
networks:
travellist:
driver: bridge

Сохраните и закройте файл, когда закончите работу.

6: Запуск приложения с помощью Docker Compose

Теперь с помощью команд docker-compose  мы можем собрать образ приложения и запустить сервисы, которые мы только что определили.

Запустите сборку образа app с помощью команды:

docker-compose build app

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

Building app
Step 1/11 : FROM php:7.4-fpm
---> fa37bd6db22a
Step 2/11 : ARG user
---> Running in f71eb33b7459
Removing intermediate container f71eb33b7459
---> 533c30216f34
Step 3/11 : ARG uid
---> Running in 60d2d2a84cda
Removing intermediate container 60d2d2a84cda
---> 497fbf904605
Step 4/11 : RUN apt-get update && apt-get install -y     git     curl     libpng-dev     libonig-dev     ...
Step 7/11 : COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
---> e499f74896e3
Step 8/11 : RUN useradd -G www-data,root -u $uid -d /home/$user $user
---> Running in 232ef9c7dbd1
Removing intermediate container 232ef9c7dbd1
---> 870fa3220ffa
Step 9/11 : RUN mkdir -p /home/$user/.composer &&     chown -R $user:$user /home/$user
---> Running in 7ca8c0cb7f09
Removing intermediate container 7ca8c0cb7f09
---> 3d2ef9519a8e
Step 10/11 : WORKDIR /var/www
---> Running in 4a964f91edfa
Removing intermediate container 4a964f91edfa
---> 00ada639da21
Step 11/11 : USER $user
---> Running in 9f8e874fede9
Removing intermediate container 9f8e874fede9
---> fe176ff4702b
Successfully built fe176ff4702b
Successfully tagged travellist:latest

Когда сборка закончится, можно запустить среду в фоновом режиме:

docker-compose up -d
Creating travellist-db    ... done
Creating travellist-app   ... done
Creating travellist-nginx ... done

Теперь контейнеры будут работать в фоновом режиме. Чтобы узнать состояние активных сервисов, запустите:

docker-compose ps

Вывод будет выглядеть так:

Name                    Command              State          Ports
-------------------------------------------------------------------------------
travellist-app     docker-php-entrypoint php-fpm   Up      9000/tcp
travellist-db      docker-entrypoint.sh mysqld     Up      3306/tcp, 33060/tcp
travellist-nginx   nginx -g daemon off;            Up      0.0.0.0:8000->80/tcp

Среда работает, но настройка приложения еще не завершена. Нужно выполнить еще несколько команд. Используйте команду docker-compose exec для запуска команд в контейнерах ваших сервисов. Например, команда ls -l может отображать подробную информацию о файлах в каталоге приложения:

docker-compose exec app ls -l
total 256
-rw-rw-r-- 1 8host 1001    738 Jan 15 16:46 Dockerfile
-rw-rw-r-- 1 8host 1001    101 Jan  7 08:05 README.md
drwxrwxr-x 6 8host 1001   4096 Jan  7 08:05 app
-rwxr-xr-x 1 8host 1001   1686 Jan  7 08:05 artisan
drwxrwxr-x 3 8host 1001   4096 Jan  7 08:05 bootstrap
-rw-rw-r-- 1 8host 1001   1501 Jan  7 08:05 composer.json
-rw-rw-r-- 1 8host 1001 179071 Jan  7 08:05 composer.lock
drwxrwxr-x 2 8host 1001   4096 Jan  7 08:05 config
drwxrwxr-x 5 8host 1001   4096 Jan  7 08:05 database
drwxrwxr-x 4 8host 1001   4096 Jan 15 16:46 docker-compose
-rw-rw-r-- 1 8host 1001   1015 Jan 15 16:45 docker-compose.yml
-rw-rw-r-- 1 8host 1001   1013 Jan  7 08:05 package.json
-rw-rw-r-- 1 8host 1001   1405 Jan  7 08:05 phpunit.xml
drwxrwxr-x 2 8host 1001   4096 Jan  7 08:05 public
-rw-rw-r-- 1 8host 1001    273 Jan  7 08:05 readme.md
drwxrwxr-x 6 8host 1001   4096 Jan  7 08:05 resources
drwxrwxr-x 2 8host 1001   4096 Jan  7 08:05 routes
-rw-rw-r-- 1 8host 1001    563 Jan  7 08:05 server.php
drwxrwxr-x 5 8host 1001   4096 Jan  7 08:05 storage
drwxrwxr-x 4 8host 1001   4096 Jan  7 08:05 tests
-rw-rw-r-- 1 8host 1001    538 Jan  7 08:05 webpack.mix.js

Теперь запустите команду composer install, чтобы установить зависимости приложения:

docker-compose exec app composer install

Эта команда вернет:

Loading composer repositories with package information
Installing dependencies (including require-dev) from lock file
Package operations: 85 installs, 0 updates, 0 removals
- Installing doctrine/inflector (1.3.1): Downloading (100%)
- Installing doctrine/lexer (1.2.0): Downloading (100%)
- Installing dragonmantank/cron-expression (v2.3.0): Downloading (100%)
- Installing erusev/parsedown (1.7.4): Downloading (100%)
- Installing symfony/polyfill-ctype (v1.13.1): Downloading (100%)
- Installing phpoption/phpoption (1.7.2): Downloading (100%)
- Installing vlucas/phpdotenv (v3.6.0): Downloading (100%)
- Installing symfony/css-selector (v5.0.2): Downloading (100%)

Generating optimized autoload files
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover --ansi
Discovered Package: facade/ignition
Discovered Package: fideloper/proxy
Discovered Package: laravel/tinker
Discovered Package: nesbot/carbon
Discovered Package: nunomaduro/collision
Package manifest generated successfully.

Прежде чем протестировать приложение, нужно сгенерировать уникальный ключ с помощью artisan (этот инструмент командной строки Laravel мы уже упоминали ранее). Этот ключ нужен для шифрования пользовательских сессий и других конфиденциальных данных:

docker-compose exec app php artisan key:generate
Application key set successfully.

Откройте браузер и введите домен или IP-адрес сервера, добавив порт 8000:

http://server_domain_or_IP:8000

На странице появится список городов.

Команда logs позволяет вам проверить логи, сгенерированные вашими сервисами:

docker-compose logs nginx
Attaching to travellist-nginx
travellist-nginx | 192.168.160.1 - - [23/Jan/2020:13:57:25 +0000] "GET / HTTP/1.1" 200 626 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36"
travellist-nginx | 192.168.160.1 - - [23/Jan/2020:13:57:26 +0000] "GET /favicon.ico HTTP/1.1" 200 0 "http://localhost:8000/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36"
travellist-nginx | 192.168.160.1 - - [23/Jan/2020:13:57:42 +0000] "GET / HTTP/1.1" 200 626 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36"

Чтобы приостановить среду Docker Compose, сохранив при этом состояние сервисов, введите:

docker-compose pause
Pausing travellist-db    ... done
Pausing travellist-nginx ... done
Pausing travellist-app   ... done

Восстановить сервисы можно с помощью этой команды:

docker-compose unpause
Unpausing travellist-app   ... done
Unpausing travellist-nginx ... done
Unpausing travellist-db    ... done

Чтобы отключить среду Docker Compose и удалить все контейнеры, сети и тома, введите:

docker-compose down
Stopping travellist-nginx ... done
Stopping travellist-db    ... done
Stopping travellist-app   ... done
Removing travellist-nginx ... done
Removing travellist-db    ... done
Removing travellist-app   ... done
Removing network travellist-laravel-demo_travellist

Больше команд Docker Compose вы найдете здесь.

Заключение

Теперь у вас есть состоящая из 3 контейнеров среда разработки Docker, которую вы создали с помощью Docker Compose.

Теперь вы можете работать над своим приложением и вам не нужно будет устанавливать и настраивать веб-сервер для разработки и тестирования. Более того, вы будете использовать одноразовую среду, которую можно легко реплицировать или распространить, – это очень полезно как в самой разработке, так и при переходе в производство.