Docker, docker, docker... Было время, когда это слово звучало в каждой курилке разработчиков. Хайп вокруг докера уже подугас, однако он по-прежнему может быть полезен как для локальной среды, так и как production-ready решение. Но в посте речь пойдет именно о среде для локальной разработки.

Но для тех, кто не в курсе, на всякий случай:

Docker — программное обеспечение для автоматизации развёртывания и управления приложениями в среде виртуализации на уровне операционной системы. Позволяет «упаковать» приложение со всем его окружением и зависимостями в контейнер, который может быть перенесён на любую Linux-систему с поддержкой cgroups в ядре, а также предоставляет среду по управлению контейнерами. Изначально использовал возможности LXC, с 2015 года применял собственную библиотеку, абстрагирующую виртуализационные возможности ядра Linux — libcontainer. С появлением ​Open Container Initiative начался переход от монолитной к модульной архитектуре.

wikipedia

Углубляться в то, что такое docker и как он работает в этой статье я не буду, это тема скорее для отдельного разговора. Если кому-то интересно - пишите в комментариях.

Что будет уметь наша сборка?

Простейшая среда разработки на php включает в себя следующие компоненты:

  • Собственно сам PHP, последний стабильный релиз 7.1;
  • Composer
  • Mysql, последняя стабильная версия 8;
  • Nginx

Также наша конфигурация будет поддерживать сколько угодно хостов nginx (см. проектов). Добавление новых компонентов в стек обычно не составляет труда, если это не заморская диковина конечно, но об этом позже.

Установка docker

Заострять внимание на процессе установки docker'а нет никакого смысла, на официальном сайте есть подробные мануалы по установке для всех популярных ОС:

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

Файловая структура

Переходим к организации папок и файлов нашей сборки. Создадим на диске какую-нибудь директорию, которая будет корневой для нашей сборки и в ней по порядку создаем следующие директории:

  • www - в этой папке будут лежать файлы наших проектов, по директории на каждый проект;
  • mysql - в этой папке будут храниться файлы наших баз данных;
  • logs - здесь будет собриать логи из разных образов;
  • hosts - здесь будут храниться файлы конфигурации nginx для наших проектов;
  • images - папка с нашими образами - компонентами нашей системы.

Еще не помешает создать дефолтный проект, чтобы проверить работоспособность нашей сборки когда все запустится. В директории www создадим директорию тестового проекта - hello.dev с одим единственным файлом index.php. Содердимое файла index.php классическое:

<?php phpinfo();

Также в корне будет лежать наш docker-compose.yml - сердце любой docker-конфигурации :)

Собираем образ PHP

Стандартный официальный образ PHP не включает в себя никаких модулей, поэтому чтобы включить их нужно собрать свой образ на основе официального. Звучит немного страшновато, но на деле все просто. Создаем директорию для нашего образа images/php и в ней создаем файл Dockerfile следующего содержания:

# Для начала указываем исходный образ, он будет использован как основа
FROM php:7.1-fpm
# Необязательная строка с указанием автора образа
MAINTAINER PHPtoday.ru <info@phptoday.ru>

# RUN выполняет идущую за ней команду в контексте нашего образа.
# В данном случае мы установим некоторые зависимости и модули PHP.
# Для установки модулей используем команду docker-php-ext-install.
# На каждый RUN создается новый слой в образе, поэтому рекомендуется объединять команды.
RUN apt-get update && apt-get install -y \
        curl \
        wget \
        git \
        libfreetype6-dev \
        libjpeg62-turbo-dev \
        libmcrypt-dev \
        libpng12-dev \
    && docker-php-ext-install -j$(nproc) iconv mcrypt mbstring mysqli pdo_mysql zip \
    && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
    && docker-php-ext-install -j$(nproc) gd
    
# Куда же без composer'а.
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

# Добавим свой php.ini, можем в нем определять свои значения конфига
ADD php.ini /usr/local/etc/php/conf.d/40-custom.ini

# Указываем рабочую директорию для PHP
WORKDIR /var/www

# Запускаем контейнер
# Из документации: The main purpose of a CMD is to provide defaults for an executing container. These defaults can include an executable, 
# or they can omit the executable, in which case you must specify an ENTRYPOINT instruction as well.
CMD ["php-fpm"]

Также в этой папке создадим пока пустой php.ini, чтобы не было ошибки при сборке образа. Можете добавить в него нужные вам настройки.

Docker Compose

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

# Версия docker-compose
version: '2'
# Список наших сервисов (контейнеров)
services:
    nginx:
    	# используем последний стабильный образ nginx
        image: nginx:latest
        # маршрутизируем порты
        ports:
            - "80:80"
            - "443:443"
        # монтируем директории, слева директории на основной машине, справа - куда они монтируются в контейнере
        volumes:
            - ./hosts:/etc/nginx/conf.d
            - ./www:/var/www
            - ./logs:/var/log/nginx
        # nginx должен общаться с php контейнером
        links:
            - php
    php:
        # у нас свой образ для PHP, указываем путь к нему и говорим что его надо собрать
        build: ./images/php
        # этот образ будет общаться с mysql
        links:
            - mysql
        # монтируем директорию с проектами
        volumes:
            - ./www:/var/www
    mysql:
        image: mysql:8
        ports:
            - "3306:3306"
        volumes:
            - ./mysql:/var/lib/mysql
        # задаем пароль для root пользователя
        environment:
            MYSQL_ROOT_PASSWORD: secret

Структура docker-compose.yml файла предельно простая, если будут вопросы - задавайте в комментариях.

Конфигурация nginx для проектов

Раньше мы уже создали тестовый проект hello.dev, давайте добавим для него конфиг nginx. В папке hosts создадим файл с названием hello-dev.conf:

server {
    index index.php;
    server_name hello.dev;
    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    root /var/www/hello.dev;

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
}

Конфиг nginx для докер-контейнера ничем не отличается от обычного конфига для сайта. Стоит лишь обратить внимание на директиву fastcgi_pass, где мы используем не путь к unix-сокету, а адрес php:9000. Здесь присутствует немного магии docker'а: php - это хост по которому доступен наш php контейнер внутри контейнера nginx, ну а 9000 - порт, по которому можно достучаться до fpm-сокета.

Run the magic!

Мы набросали минимальную конфигурацию для локальной среды разработки и можем ее смело запускать. Для этого из корня нашей сборки, где лежит docker-compose.yml файл нужно выполнить команду docker-compose up -d и немного подождать. Первый запуск будет дольше, потому что docker'у нужно скачать образы и собрать образ для php.

В конце концов мы увидим заветные строчки:

Starting source_mysql_1 ... 
Starting source_mysql_1 ... done
Starting source_php_1 ... 
Starting source_php_1 ... done
Starting source_nginx_1 ... 
Starting source_nginx_1 ... done

Они говорят нам что наши три контейнера запущены и готовы к работе. Проверимс... Для этого откроем браузер и перейдем по адресу http://hello.dev/, но сперва добавим одну строку в hosts файл.

127.0.0.1 hello.dev

Важно для windows и mac адрес 127.0.0.1 нужно заменить на адрес виртуальной машины, в которой запускается докер, потому что нативной поддержки пока нет или она очень унылая.

Итак, окрываем браузер и видим:

phpinfo

Наша среда готова для работы. Можно легко добавлять новые проекты - просто создаем новую папку для проекта в www и добавляем новый конфиг для nginx, перезаускаем контейнеры и вперед! Удачи в разработке и пишите вопросы в комментариях.

Репозиторий на github.

Использованные образы: