[https://habr.com/ru/post/519500/](https://habr.com/ru/post/519500/) Решение, которое позволит создать на локальном компьютере универсальную среду разработки на **PHP** за **30 — 40 минут**. ## Почему Docker? - Docker не является VM-системой, он не моделирует аппаратное обеспечение компьютера. Используя Docker вы получите минимальное потребление ресурсов. Docker-контейнеры взаимодействуют напрямую с ядром вашего компьютера (или ядром хоста), но при этом изолируют программу на уровне процесса. - Высокая скорость развертывания. Вы можете использовать готовые docker-образы, которые устанавливаются и запускаются за секунды. - Приложения, находящееся внутри docker-контейнеров, можно запустить на любом компьютере с установленным Docker, при этом окружение будет одинаковым. - Возможность простой сегрегации пользовательских данных и контейнеров-сервисов. Если вы сломаете или удалите docker-контейнер, то данные не потеряются, так как они не будут принадлежать контейнеру. Контейнер выполняет лишь функцию сервиса, и не хранит данные, которые нельзя потерять между запусками. - Вы можете очень быстро добавлять новые контейнеры, изменять их конфигурацию, запускать различные версии баз данных на одной машине. ## Требования - Git. - Docker engine 19.x и выше. ## Возможности и особенности среды разработки - Несколько версий **PHP — 7.3 и 7.1** с набором наиболее востребованных расширений. - Возможность использовать для web-проектов разные версии **PHP**. - Готовый к работе монитор процессов **Supervisor**. - Предварительно сконфигурированный веб-сервер **Nginx**. - Базы данных: **MySQL 5.7**, **MySQL 8**, **PostgreSQL** (latest), **MongoDB 4.2**, **Redis** (latest). - Настройка основных параметров окружения через файл **.env**. - Возможность модификации сервисов через **docker-compose.yml**. - Последняя версия **docker-compose.yml**. - Все docker-контейнеры базируются на официальных образах. - Структурированный **Dockerfile** для создания образов **PHP**. - Каталоги большинства docker-контейнеров, в которых хранятся пользовательские данные и параметры конфигурации смонтированы на локальную машину. В целом, среда разработки удовлетворяет требованию — **при использовании Docker каждый контейнер должен содержать в себе только один сервис**. **Репозиторий проекта** ## Структура проекта Рассмотрим структуру проекта. ``` ├── .env-example ├── .gitignore ├── .ssh ├── README.md ├── docker-compose.yml ├── mongo ├── mysql-5.7 ├── mysql-8 ├── nginx ├── php-ini ├── php-workers ├── php-workspace ├── postgres ├── projects └── redis ``` **Примечание** В некоторых каталогах можно встретить пустой файл .**gitkeep**. Он нужен лишь для того, чтобы была возможность добавить каталог под наблюдение Git. **.gitkeep** — является заполнением каталога, это фиктивный файл, на который не следует обращать внимание. ### .env-example Пример файла с основными настройками среды разработки. ``` # Временная зона WORKSPACE_TIMEZONE='Europe/Moscow' # XDEBUG DOCKER_PHP_ENABLE_XDEBUG='on' # Настройки Nginx # Порт, который следует использовать # для соединения с локального компьютера NGINX_PORT=80 # Настройки Redis # Порт, который следует использовать # для соединения с локального компьютера REDIS_PORT=6379 # Настройки Postgres POSTGRES_DB=test POSTGRES_USER=pg_user POSTGRES_PASSWORD=secret POSTGRES_PORT=54322 # Настройки общие для MySQL 8.x и MySQL 5.7.x MYSQL_ROOT_PASSWORD=secret MYSQL_DATABASE=test # Настройки MySQL 8.x # Порт, который следует использовать # для соединения с локального компьютера MYSQL_8_PORT=4308 # Настройки MySQL 5.7.x # Порт, который следует использовать # для соединения с локального компьютера MYSQL_5_7_PORT=4307 # Настройки MongoDB # Порт, который следует использовать # для соединения с локального компьютера MONGO_PORT=27017 # Настройки PHP 7.3 # Внешний порт, доступен с локального компьютера PHP_7_3_PORT=9003 # Настройки PHP 7.1 # Внешний порт, доступен с локального компьютера PHP_7_1_PORT=9001 ``` ### .gitignore Каталоги и файлы, в которых хранятся пользовательские данные, код ваших проектов и ssh-ключи внесены в. gitignore. ### .ssh Этот каталог предназначен для хранения ssh-ключей. ### readme.md Документация. ### docker-compose.yml Документ в формате **YML**, в котором определены правила создания и запуска многоконтейнерных приложений **Docker**. В этом файле описана структура среды разработки и некоторые параметры необходимые для корректной работы web-приложений. ### mongo Каталог базы данных MongoDB. ``` ├── configdb │ └── mongo.conf ├── db └── dump ``` **mongo.conf** — Файл конфигурации **MongoDB**. В этот файл можно добавлять параметры, которые при перезапуске MongoDB будут применены. **db** — эта папка предназначена для хранения пользовательских данных MongoDB. **dump** — каталог для хранения дампов. ### mysql-5.7 Каталог базы данных MySQL 5.7. ``` ├── conf.d │ └── config-file.cnf ├── data ├── dump └── logs ``` **config-file.cnf** — файл конфигурации. В этот файл можно добавлять параметры, которые при перезапуске MySQL 5.7 будут применены. **data** — эта папка предназначена для хранения пользовательских данных MySQL 5.7. **dump** — каталог для хранения дампов. **logs** — каталог для хранения логов. ### mysql-8 Каталог базы данных MySQL 8. ``` ├── conf.d │ └── config-file.cnf ├── data ├── dump └── logs ``` **config-file.cnf** — файл конфигурации. В этот файл можно добавлять параметры, которые при перезапуске MySQL 8 будут применены. **data** — эта папка предназначена для хранения пользовательских данных MySQL 8. **dump** — каталог для хранения дампов. **logs** — каталог для хранения логов. ### nginx Эта папка предназначена для хранения файлов конфигурации Nginx и логов. ``` ├── conf.d │ ├── default.conf │ └── vhost.conf └── logs ``` **default.conf** — файл конфигурации, который будет применён ко всем виртуальным хостам. **vhost.conf** — здесь хранятся настройки виртуальных хостов web-проектов. Рассмотрим **vhost.conf** подробнее: ``` server { listen 80; index index.php index.html; server_name project-1.localhost; error_log /var/log/nginx/project-1.error.log; access_log /var/log/nginx/project-1.access.log combined if=$loggable; root /var/www/project-1.ru; location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass php-7.3:9000; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_script_name; } } server { listen 80; index index.php index.html; server_name project-2.localhost; error_log /var/log/nginx/project-2.error.log; access_log /var/log/nginx/project-2.access.log combined if=$loggable; root /var/www/project-2.ru; location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass php-7.1:9000; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_script_name; } } ``` В файле конфигурации описаны настройки для **двух** web-проектов — **project-1.localhost** и **project-2.localhost**. Здесь следует обратить внимание на то, как производится перенаправление запросов к нужному docker-контейнеру. Например, для проекта **project-1.localhost** указано: ``` fastcgi_pass php-7.3:9000; ``` **php-7.3** — название docker-контейнера, а **9000** — порт внутренней сети. Контейнеры между собой связаны через внутреннюю сеть, которая определена в файле docker-compose.yml. ### php-ini В этом каталоге находятся файлы конфигурации PHP. ``` ├── 7.1 │ └── php.ini └── 7.3 └── php.ini ``` Для каждой версии **PHP** — свой файл конфигурации. ### php-workers Место для хранения файлов конфигурации **Supervisor**. ``` ├── 7.1 │ └── supervisor.d │ └── 7.3 └── supervisor.d ``` Для каждой версии PHP — могут быть добавлены свои файлы с настройками. ### php-workspace Здесь хранится файл, в котором описаны действия, выполняемые при создании образов docker-контейнеров PHP. ``` └── Dockerfile ``` **Dockerfile** — это текстовый документ, содержащий все команды, которые следует выполнить для сборки образов PHP. ### postgres Каталог для системы управления базами данных **PostgreSQL**. ``` ├── .gitkeep ├── data └── dump ``` **data** — эта папка предназначена для хранения пользовательских данных PostgreSQL. **dump** — каталог для хранения дампов. ### projects Каталог предназначен для хранения web-проектов. Вы можете создать в это каталоге папки, и поместить в них ваши web-проекты. Например: ``` project-1.ru project-2.ru ... ``` Содержимое каталога projects доступно из контейнеров **php-7.1** и **php-7.3.** Если зайти в контейнер php-7.1 или php-7.3, то в каталоге /var/www будут доступны проекты, которые расположены в projects на локальной машине. ### redis Каталог key-value хранилища Redis. ``` ├── conf └── data ``` **conf** — папка для хранения специфических параметров конфигурации. **data** — если настройки конфигурации предполагают сохранения данных на диске, то Redis будет использовать именно этот каталог. ## Программы в docker-контейнерах PHP Полный перечень приложений, которые установлены в контейнерах **php-7.x**можно посмотреть в **php-workspace/Dockerfile**. Здесь перечислим лишь некоторые, наиболее важные: - bash - htop - curl - Git - Сomposer - make - wget - NodeJS - Supervisor - npm ## Начало работы **1.** Выполните клонирование данного репозитория в любое место на вашем компьютере. ``` git clone https://github.com/drandin/docker-php-workspa ``` Перейдите в директорию, в которую вы клонировали репозиторий. Все дальнейшие команды следует выполнять именно в этой директории. **2**. Скопируйте файл **.env-example** в **.env** ``` cp .env-example .env ``` Если это необходимо, то внесите изменения в файл **.env**. Измените настройки среды разработки в соответствии с вашими требованиями. **3**. Выполните клонирование web-проектов в каталог projects. Для примера, далее мы будем исходить из предположения, что у вас есть **2** проекта: ``` project-1.ru project-2.ru ``` **project-1.ru** — будет работать на версии **PHP 7.3**, а **project-2.ru** — на **PHP 7.1**. **4**. Отредактируйте настройки виртуальных хостов **Nginx**. Файл конфигурации виртуальных хостов находится в каталоге **./nginx/conf.d/**. **5**. Настройте хосты (доменные имена) web-проектов на локальной машине. Необходимо добавить названия хостов web-проектов в файл **hosts** на вашем компьютере. В файле **hosts** следует описать связь доменных имён ваших web-проектов в среде разработки на локальном компьютере и **IP** docker-контейнера **Nginx**. На **Mac** и **Linux** этот файл расположен в **/etc/hosts**. На **Windows** он находится в **C:\Windows\System32\drivers\etc\hosts**. Строки, которые вы добавляете в этот файл, будут выглядеть так: ``` 127.0.0.1 project-1.localhost 127.0.0.1 project-2.localhost ``` В данном случае, мы исходим из того, что **Nginx**, запущенный в docker-контейнере, доступен по адресу **127.0.0.1** и web-сервер слушает порт **80**. Не рекомендуем использовать имя хоста с .**dev** на конце в среде разработки. Лучшей практикой является применение других названий — **.localhost** или **.test**. **6**. _[опционально, если это необходимо]_ Настройте маршрутизацию внутри контейнеров web-проектов. Web-проекты должны иметь возможность отправлять http-запросы друг другу и использовать для этого название хостов. Из одного запущенного docker-контейнера **php-7.1** web-приложение **№ X** должно иметь возможность отправить запрос к другому web-приложению **№ Y**, которое работает внутри docker-контейнера **php-7.3**. При этом адресом запроса может быть название хоста, которое указано в файле **/etc/hosts** локального компьютера. Чтобы это стало возможным нужно внутри контейнеров так же внести соответствующие записи в файл **/etc/hosts**. Самый простой способ решить данную задачу — добавить секцию **extra_hosts**в описание сервисов **php-7.1** и **php-7.3** в **docker-compose.yml**. Пример: ``` ... php-7.1: ... extra_hosts: - 'project-1.localhost:IP_HOST_MACHINE' - 'project-2.localhost:IP_HOST_MACHINE' ... ``` **IP_HOST_machine** — IP адрес, по которому из docker-контейнера доступен ваш локальный компьютер. Если вы разворачиваете среду разработки на **Mac**, то внутри docker-контейнера вам доступен хост **docker.for.mac.localhost**. Узнать **IP** адрес вашего **Mac** можно при помощи команды, который нужно выполнить на локальной машине: ``` docker run -it alpine ping docker.for.mac.localhost ``` В результате вы получите, что-то подобное: ``` PING docker.for.mac.localhost (192.168.65.2): 56 data bytes 64 bytes from 192.168.65.2: seq=0 ttl=37 time=0.286 ms 64 bytes from 192.168.65.2: seq=1 ttl=37 time=0.504 ms 64 bytes from 192.168.65.2: seq=2 ttl=37 time=0.801 ms ``` После того, как вам станет известен IP-адрес, укажите его в секции **extra_hosts**в описание сервисов **php-7.1** и **php-7.3** в **docker-compose.yml**. ``` ... php-7.1: ... extra_hosts: - 'project-1.localhost:192.168.65.2' - 'project-2.localhost:192.168.65.2' ... ``` **8**. Настройте параметры соединения с системами хранения данных. **Хосты и порты сервисов** Для того, чтобы настроить соединения с базами данных из docker-контейнеров **php-7.1** и **php-7.3** следует использовать следующие названия хостов и порты: | **Сервис** | **Название хоста** | **Порт** | | ---- | ---- | ---- | | MySQL 5.7 | mysql-5.7 | 3308 | | MySQL 8 | mysql-8 | 3308 | | PostgreSQL | postgres | 5432 | | MongoDB | mongo | 27017 | | Redis | redis | 6379 | Именно эти параметры следует использовать для конфигурации web-проектов. **Для соединения с базами данных с локальной машины:** - Хост для всех баз данных — **127.0.0.1**. - Порты — значения указанные в **.env**. **7**. Создайте контейнеры и запустите их. Выполните команду: ``` docker-compose build && docker-compose up -d ``` Создание контейнеров займёт некоторое время. Обычно от 10 до 30 минут. Дождитесь окончания процесса. Ваш компьютер не должен потерять доступ в интернет. Если вы всё сделали правильно, то контейнеры будут созданы и запущены. Откройте **Docker Dashboard** и убедитесь в этом: ![[2c9be6f6952a8ce221c86bdd595602a9.png]] **8**. Создайте SSH-ключи. Для работы web-проектов могут потребоваться SSH-ключи, например для того, чтобы из контейнера при помощи **Composer** можно было установить пакет из приватного репозитория. Создать SSH-ключи можно при помощи следующей команды: ``` ssh-keygen -f ./.ssh/id_rsa -t rsa -b 2048 -C "your-name@example.com" ``` Вместо your-name@example.com укажите свой **Email**. В папку **.ssh/** будут сохранены **2 файла** — публичный и приватный ключ. **9**. Проверьте созданные docker-контейнеры. Выполните команду: ``` docker ps ``` Если создание контейнеров прошло успешно, то вы увидите примерно такой результат: ``` CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8d348959c475 docker-php-workspace_php-7.1 "docker-php-entrypoi…" 6 minuts ago Up 54 seconds 0.0.0.0:9001->9000/tcp php-7.1 a93399727ff6 docker-php-workspace_php-7.3 "docker-php-entrypoi…" 6 minuts ago Up 53 seconds 0.0.0.0:9003->9000/tcp php-7.3 5cd80ac95388 nginx:stable-alpine "/docker-entrypoint.…" 6 minuts ago Up 51 seconds 0.0.0.0:80->80/tcp nginx 70182bc9e44c mysql:5.7 "docker-entrypoint.s…" 6 minuts ago Up 54 seconds 33060/tcp, 0.0.0.0:4307->3306/tcp mysql-5.7 46f2766ec0b9 mysql:8.0.21 "docker-entrypoint.s…" 6 minuts ago Up 53 seconds 33060/tcp, 0.0.0.0:4308->3306/tcp mysql-8 a59e7f4b3c61 mongo:4.2 "docker-entrypoint.s…" 6 minuts ago Up 54 seconds 0.0.0.0:27017->27017/tcp mongo eae8d62ac66e postgres:alpine "docker-entrypoint.s…" 6 minuts ago Up 53 seconds 0.0.0.0:54322->5432/tcp postgres bba24e86778a redis:latest "docker-entrypoint.s…" 6 minuts ago Up 54 seconds 0.0.0.0:6379->6379/tcp redis ``` **10**. Установка зависимостей для web-приложений. Если для работы web-приложений необходимо установить зависимости, например через менеджер пакетов **Composer** или **NPM**, то сейчас самое время сделать это. В контейнерах **php-7.1** и **php-7.3** уже установлен и **Composer** и **NPM**. Войдите в контейнер **php-7.1**: ``` docker exec -it php-7.1 bash ``` или **php-7.3**: ``` docker exec -it php-7.3 bash ``` Перейдите в рабочий каталог необходимого web-проекта и выполните требуемые действия. Например, установите зависимости через **Composer** при помощи команды: ``` composer install ``` ## Вопросы и ответы Несколько важных вопросов и ответов. **Как зайти в работающий docker-контейнер?** Выполните команду: `docker exec -it container_name bash` **container_name** — имя контейнера. **Как останавливать и удалить контейнеры и другие ресурсы среды разработки, которые были созданы?** `docker-compose down` **Как получить список всех контейнеров?** `docker ps -a` **Как получить подробную информацию о docker-контейнере?** `docker inspect containername` _**container**_**name** — имя контейнера. **Как получить полный список расширений PHP, которые установлены в контейнере php-7.3?** Если контейнер **php-7.3** запущен, то выполните команду: `docker exec -it php-7.3 php -m` **Как удалить все контейнеры?** Удаление всех контейнеров: `docker rm -v $(docker ps -aq)` Удаление всех активных контейнеров: `docker rm -v $(docker ps -q)` Удаление всех неактивных контейнеров: `docker rm -v $(docker ps -aq -f status=exited)` ## Развёртывание дампов MySQL, PostgreSQL и MongoDB Если для работы web-проектов требуются перенести данные в хранилища, то следуйте описанным ниже инструкциям. Выполните следующую команду на локальной машине: ``` docker exec -i postgres psql --username user_name database_name < /path/to/dump/pgsql-backup.sql ``` Или зайдите в контейнер postgres и выполните: ``` psql --username user_name database_name < /path/to/dump/pgsql-backup.sql ``` **user_name** — имя пользователя. Значение postgres_USER (см. файл .env). **database_name** — название базы данных. Значение postgres_DB (см. файл .env). Существует два варианта. **Вариант 1** Если требуется создать дополнительных пользователей, то следует это сделать перед началом процедуры загрузки дампа. В файле **mysql/conf.d/config-file.cnf** отключите лог медленных запросов **slow_query_log=0** или установите большое значение **long_query_time**, например 1000. Если дамп сжат утилитой **gzip**, сначала следует распаковать архив: `gunzip databases-dump.sql.gz` Затем можно развернуть дамп, выполнив на локальном компьютере команду: ``` docker exec -i mysql mysql --user=root --password=secret --force < databases-dump.sql ``` Указывать пароль в командной строке — плохая практика, не делайте так в производственной среде. **MySQL** выдаст справедливое предупреждение: _mysql: [Warning] Using a password on the command line interface can be insecure_ Ключ **--force** говорит MySQL, что ошибки следует проигнорировать и продолжить развёртывание дампа. Этот ключ иногда может пригодится, но лучше его без необходимости не применять. **Вариант 2** Воспользоваться утилитой **Percona** **XtraBackup**. **Percona XtraBackup** — это утилита для горячего резервного копирования баз данных MySQL. О том, как работать с **XtraBackup** можно узнать по ссылке: [https://medium.com/@drandin/создание-резервной-копии-mysql-при-помощи-утилиты-xtrabackup-26bd3f843075](https://medium.com/@drandin/%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5-%D1%80%D0%B5%D0%B7%D0%B5%D1%80%D0%B2%D0%BD%D0%BE%D0%B9-%D0%BA%D0%BE%D0%BF%D0%B8%D0%B8-mysql-%D0%BF%D1%80%D0%B8-%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D0%B8-%D1%83%D1%82%D0%B8%D0%BB%D0%B8%D1%82%D1%8B-xtrabackup-26bd3f843075) - Скопируйте фалы дампа в каталог **mongo/dump**. - Войдите в контейнер **mongo**: ``` docker exec -it mongo sh ``` Выполните следующую команду, чтобы развернуть дамп базы **database_name**: ``` mongorestore -d database_name /dump/databases/database_nam ``` **Git-репозиторий проекта:** [https://github.com/drandin/docker-php-workspace](https://github.com/drandin/docker-php-workspace)