tech-tips/Языки программирования/PHP/Среда разработки PHP на базе Docker - Хабр.md

687 lines
30 KiB
Markdown
Raw Permalink Normal View History

[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)