# Запуск Laravel-приложения в Docker-Compose

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

Docker Compose — следующий шаг в развитии docker-контейнеров. Это приложение позволяет запустить одновременно несколько контейнеров и объединить их во внутреннюю сеть, которая будет поддерживаться Docker. Это бывает удобно, когда приложение использует не только фреймворк своего языка программирования, но и базу данных и веб-сервер для работы в сети.

В этой инструкции мы рассмотрим запуск Laravel-приложения c помощью Docker Compose.

Для начала работы нам понадобятся подготовленный к работе сервер под управлением Ubuntu, установленный Docker и установленный Docker Compose.

Мы будем запускать приложение, созданное Digital Ocean для своих образовательных нужд. Принцип запуска любого другого приложения, которое вы создадите и клонируете в один из общедоступных репозиториев, не будет отличаться.

# Скачивание приложения

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

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

Это приложение, которое хранит в базе список мест, которые мы посетили и которые хотели бы посетить.

Вышеуказанной командой мы скачали архив с приложением в текущую папку и сразу переименовали его в travellist.zip.

Теперь нам нужно установить программу для разархивирования:

sudo apt update
sudo apt install unzip

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

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

После этого можно переходить к настройке переменных среды нашего приложения.

# Настройка переменных среды

По умолчанию файлы с настройками переменных среды хранятся в папках Laravel-приложений в директории config. Мы создадим отдельный файл, который будет скрыт от просмотра и расположен в основной директории проекта. Наше скачанное приложение идёт с файлом настроек. Скопируем его, чтобы на его основе можно было создать новый файл с настройками:

cp .env.example .env

Теперь откроем его для редактирования в текстовом редакторе:

vim .env

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

APP_NAME=travel
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=travel_list
DB_USERNAME=travel_list_user
DB_PASSWORD=password
…

Здесь:

  • APP_URL — адрес, по которому будет доступно наше приложение в сети, когда будет запущено;

  • DB_HOST — адрес базы данных для внутреннего обращения — обратите внимание, что по умолчанию он указан 127.0.0.1, но поскольку мы будем запускать приложение через Compose, нужно указать здесь параметр db, как один из контейнеров Docker Compose;

  • DB_DATABASE, DB_USERNAME и DB_PASSWORD — их вы можете изменить по своему усмотрению, они будут переданы и назначены в указанном виде.

# Создание докерфайла

MySQL и Nginx для работы нашего приложения будут развёрнуты из стандартных образов, которые есть на DockerHub, но вот самому нашему приложению потребуются дополнительные зависимости и приложения. Их установку мы и опишем в докерфайле.

Создадим его:

vim dockerfile

В докерфайле мы обозначим, что устанавливать будем PHP:7.4-fpm, затем пропишем установку всех необходимых зависимостей для работы приложения, скопируем официальный образ Composer в качестве менеджера зависимостей для PHP и создадим рабочую директорию:

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

# Настройка веб-сервера и базы данных

Мы создадим конфигурационные файлы для веб-сервера и базы данных внутри директории docker-compose:

mkdir -p /docker-compose/nginx

Флаг -p здесь указывает на то, что будет создана не только конечная папка, но и все, которые указаны в команде, но ещё не созданы.

В папке веб-сервера создадим конфигурационный файл:

vim travel.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. Также мы указали, что для работы с файлами php Nginx должен использовать порт 9000.

Это основные параметры веб-сервера. Теперь конфигурационный файл можно закрыть и перейти к настройкам базы данных.

Файл с настройками базы также будет лежать в папке /docker-compose. Создадим нужную папку:

mkdir docker-compose/mysql

Поместим в неё конфигурационный файл базы:

vim docker-compose/mysql/init_db.sql

Сюда мы поместим набор команд, добавляющих в наше приложение таблицу с посещёнными местами:

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 ('Tokyo',0),('Budapest',0),('Verkhnyaa Sinyachikhai',1),('Strassbourg',0),('Penza',1),('Serov',1),('Nevyansk',1),('Nairobi',0),('Edinburg',1),('Kirovgrad',1),('Alapayevsk',0);

В таблице places в данном случае будет три столбца: порядковый номер, место и факт посещения места.

Когда вы заполните таблицу своими данными, можно будет переходить к созданию Docker Compose-файла.

# Создание Docker Compose — файла

Docker Compose настраивается быстро — вся конфигурация системы контейнеров (какие образы скачивать, по каким портам соединяться с сетью, какой контейнер от какого зависит и т.д.) указываются в одном файле. Этот файл — docker-compose.yml — мы сейчас и создадим:

vim docker-compose.yml

Сначала мы добавим в compose-файл первые и последние строки файла: в первых строках указывается версия Docker Compose, которая будет использована, в последних — описывается внутренняя сеть, которая будет создана для связи контейнеров между собой: указывается имя сети и её тип. В нашем случае начало и конец compose-файла будут выглядеть так:

version: "3.7"
services:


networks:
  travel:
    driver: bridge

Здесь мы указали, что будем использовать версию 3.7 Docker Compose, внутренняя сеть будет называться travel и будет использоваться для связи контейнеров — параметр bridge.

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

# Описание приложения

Чтобы описать приложение в compose-файле, после строки services: добавим блок описания нашего приложения:

app:
    build:
      args:
        user: User
        uid: User_id
      context: ./
      dockerfile: dockerfile
    image: travellist
    container_name: travel-app
    restart: unless-stopped
    working_dir: /var/www/
    volumes:
      - ./:/var/www
    networks:
      - travel

Важно проконтролировать значения в строках user и uid — здесь мы указываем пользователя, от чьего имени будет запускаться приложение. Это должен быть ваш системный пользователь. ID всех существующих на сервере пользователей можно посмотреть командой getent passwd.

В строке dockerfile указываем имя нашего докер-файла (обратите внимание — этот параметр чувствителен к регистру), а в networks — имя нашей внутренней сети, которое мы создали на первом шаге.

# Описание базы данных

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

…
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:
      - travel
…

Здесь в строке image мы указываем, какую конкретно версию MySQL должен будет скачать Docker Compose при старте. Параметр volumes создаёт общий том, к которому будет доступ у всех контейнеров — это важно, потому что доступ к базе данных должен быть как у самого приложения, так и у веб-сервера.

Остальные строки копируют переменные среды из конфигурационного файла базы данных и приложения (блок environment) и определяют внутреннюю сеть, в которой будет работать контейнер (networks).

В конце добавим блок, отвечающий за работу веб-сервера.

# Настройка веб-сервера

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

…
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
…

Здесь мы также указываем версию образа Nginx, которую хотим использовать, имя контейнера, порты, на которых будет работать веб-сервер, и описываем тома — общие папки, с которыми будут работать контейнеры.

Наш compose-файл готов, можно переходить к запуску приложения.

# Запуск приложения

Вводим команду:

docker compose build app

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

# Output

Build complete.
Don't forget to run 'make test'.
…
Successfully built 44a6f75243be
Successfully tagged travel:latest

После завершения создания можно запустить работу приложения в фоновом режиме:

docker-compose up -d

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

docker compose ps

# Output
NAME                COMMAND                  SERVICE             STATUS              PORTS
travellist-app      "docker-php-entrypoi…"   app                 running             9000/tcp
travellist-db       "docker-entrypoint.s…"   db                  running             33060/tcp
travellist-nginx    "/docker-entrypoint.…"   nginx               running             0.0.0.0:8000->80/tcp, :::8000->80/tcp

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

docker compose exec composer update laravel/framework
docker compose exec composer install

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

docker-compose exec app php artisan key:generate

Теперь можно заходить в браузер по адресу your_server_ip:8000 и проверять работу приложения.

Если ваш VPS расположен на Джино, и вы не используете выделенный IP, то в адресной строке браузера нужно указывать технический адрес вашего сервера. Порт для подключения нужно перенаправить. В разделе УправлениеПеренаправление портов создайте перенаправление для порта 8000 и укажите новый номер порта в адресной строке браузера после указания технического адреса.

Если всё было сделано правильно, вы увидите 2 списка мест — которые вы уже посетили и хотите посетить.

«laravel_list»

# Завершение работы Docker Compose

Приостановить работу вашего приложения можно командой pause, а возобновить её — обратной командой unpause:

docker compose pause
docker compose unpause

Если вы хотите полностью остановить запущенное приложение, введите команду down:

docker compose down

Мы изложили основы работы с Docker Compose при запуске приложений. Опираясь на эту инструкцию, вы можете переходить к самостоятельной разработке приложений и их запуску в Docker Compose.