Разработка под Docker. Локальное окружение. Часть 2 — Nginx+PHP+MySql+phpMyAdmin

Website developmentPHP
Для лучшего понимания нижеследующего материала сначала рекомендуется ознакомится с Предыдушим постом

Рассмотрим пример развертки локального окружения состоящего из связки Nginx+PHP+MySql+phpMyAdmin. Данная связка очень популярна и может удовлетворить ряд стандартных потребностей рядового разработчика.

Как и в прошлом посте акцент будет смещен в сторону утилиты docker-compose, чем докера в чистом виде.

Итак, поехали!

Начнем вот с такого docker-compose.yml, который лежит в отдельной папке proxy:

docker-compose.yml для nginx-proxy
version: '3.0'

services:

  proxy:
    image: jwilder/nginx-proxy
    ports:
      - 80:80
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro
    networks:
      - proxy


networks:
  proxy:
    driver: bridge


В представленном файле описана конфигурация для создания одного контейнера с именем proxy на базе образа image: jwilder/nginx-proxy и создание сети с одноименным именем. Директива networks указывает к каким сетям подключен контейнер, в данном примере, это наша сеть proxy.

При создание сети директиву driver: bridge можно было бы и не указывать. Драйвер типа «мост» является драйвером по умолчанию. Данный контейнер будет связываться по сети с прочими контейнерами.

Образ jwilder/nginx-proxy является базовым и взят и Docker Hub там же представлено довольно обширное и подробное описание по его использованию. Принцип работы nginx-proxy довольно простой, он через пробрасываемый сокет докера получает доступ к информации о запущенных контейнерах, анализирует наличие переменной окружения с именем VIRTUAL_HOST и перенаправляет запросы с указанного хоста на контейнер, у которого данная переменная окружения задана.

Запускаем прокси уже известной командой docker-compose up -d наблюдаем следующий вывод:

Creating network "proxy_proxy" with driver "bridge"
Creating proxy_proxy_1 ... done

Данный вывод информирует нас о том, что в начале была создана сеть proxy_proxy, а затем был создан контейнер proxy_proxy_1. Имя сети получилось из названия папки, в которой размещался файл docker-compose.yml, у меня это proxy и одноименного имени сети.

Если ввести команду docker network ls, то мы увидим список сетей докера в нашей системе и одна из них должна быть proxy_proxy.

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

Создаем второй docker-compose.yml следующего содержания:

docker-compose.yml для прочих сервисов
version: '3.0'

services:

  nginx:
    image: nginx
    environment:
      - VIRTUAL_HOST=site.local
    depends_on:
      - php
    volumes:
      - ./docker/nginx/conf.d/default.nginx:/etc/nginx/conf.d/default.conf
      - ./html/:/var/www/html/
    networks:
      - frontend
      - backend

  php:
    build:
      context: ./docker/php
    volumes:
      - ./docker/php/php.ini:/usr/local/etc/php/php.ini
      - ./html/:/var/www/html/
    networks:
      - backend

  mysql:
    image: mysql:5.7
    volumes:
      - ./docker/mysql/data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: root
    networks:
      - backend


  phpmyadmin:
    image: phpmyadmin/phpmyadmin:latest
    environment:
      - VIRTUAL_HOST=phpmyadmin.local
      - PMA_HOST=mysql
      - PMA_USER=root
      - PMA_PASSWORD=root
    networks:
      - frontend
      - backend


networks:
  frontend:
    external:
      name: proxy_proxy
  backend:


Что тут у нас объявлено?

Перечислены четыре сервиса: nginx, php, mysql и phpmyadmin. И две сети. Одна сеть прокси с именем frontend, объявлена как внешняя сеть и новая внутренняя сеть backend. Драйвер для нее не указан, как и писал ранее, будет использоваться драйвер по умолчанию типа bridge.

nginx


Тут примерно должно быть все понятно. Используем базовый образ с докер хаб. Переменная окружения необходима для работы прокси и сообщает ему, по какому адресу должен быть доступен контейнер. Опция depends_on указывает, на зависимость данного контейнера от контейнера php. Это означает, что вперед будет запущен контейнер php, а после него будет выполнен запуск зависимого от него контейнера nginx. Далее пробрасываем конфигурацию для нашего nginx. Она будет чуть ниже и монтируем папку с html. Так же замечаем, что контейнер имеет доступ сразу к двум сетям. Он должен связываться и прокси из сети frontend и с php из сети backend. В принципе, можно было бы все контейнеры и в одну сеть frontend попихать, но я придерживаюсь, что подобное разделение более верное.

default.nginx
server {
    listen 80;
    server_name_in_redirect off;
    access_log  /var/log/nginx/host.access.log  main;

    root /var/www/html/;

    location / {
        try_files $uri /index.php$is_args$args;
    }

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

    location ~ /\.ht {
        deny  all;
    }
}


default.nginx — это конфиг для nginx, который пробрасывается в контейнер. Ключевой момент тут директива fastcgi_pass php:9000. Она задает адрес FastCGI-сервера. Адрес может быть указан в виде доменного имени или IP-адреса, и порта.

php:9000 — имя сервиса это и есть адрес FastCGI-сервера. Nginx обращаясь по адресу php будет получать IP-адрес контейнера, в котором работает php. Порт 9000 это стандартный порт, он объявлен при создание базового контейнера. Данный порт доступен для nginx по сети, но не доступен на хостовой машине, так как не был проброшен.

php


Тут необычно то, что не указан образ. Вместо этого происходит сборка собственного образа прямо из compose-файла. Директива context указывает на папку, в которой находится Dockerfile.

Dockerfile
FROM php:7.3.2-fpm

RUN apt-get update && apt-get install -y \
        libzip-dev \
        zip \
	&& docker-php-ext-configure zip --with-libzip \
	&& docker-php-ext-install zip \
	&& docker-php-ext-install mysqli

COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

WORKDIR /var/www/html


В Dockerfile указано, что для сборки используется базовый образ php:7.3.2-fpm, далее выполняется запуск команд для установки php-расширений. Далее копируется composer из другого базового образа и устанавливается рабочая директория для проекта. Детальнее вопросы сборки рассмотрю в других постах.

Также во внутрь контейнера пробрасывается файл php.ini и папка html с нашим проектом.

Заметим, что php находится в сети backend и к примеру прокси к нему доступ получить уже не может.

mysql


Берется базовый образ mysql с тегом 5.7, который отвечает за версию mysql. Папка ./docker/mysql/data используется для хранения файлов базы данных (ее даже создавать не надо, сама создасться при запуске). И через переменные окружения задается пароль для пользователя root, тоже root.

База находится в сети backend, что позволяет ей держать связь с php. В базовом образе используется стандартный порт 3306. Он доступен по сети докера для php, но не доступен на хостовой машине. Если выполнить проброс для данного порта, то можно к нему коннектиться к примеру из того же PHPSTORM. Но если вам достаточно интерфейса phpmyadmin, то этого можно и не делать.

phpmyadmin


Официальный образ phpmyadmin. В переменных окружения используется VIRTUAL_HOST для взаимодействия с прокси, аналогично nginx. PMA_USER и PMA_PASSWORD доступ к базе. И PMA_HOST сам хост базы. Но это не localhost, как обычно бывает, а mysql. Т.е. связь с базой доступна по имени ее сервиса, т.е. mysql. Контейнер phpmyadmin может связаться с базой, т.к имеет подключение к сети backend.

Запускаем сервисы привычной командой: docker-compose -d.

Видим следующий вывод:

Запуск сервисов
Creating network "lesson2_backend" with the default driver
Building php
Step 1/4 : FROM php:7.3.2-fpm
 ---> 9343626a0f09
Step 2/4 : RUN apt-get update && apt-get install -y         libzip-dev         zip      && docker-php-ext-configure zip --with-libzip   && docker-php-ext-install zip   && docker-php-ext-install mysqli
 ---> Using cache
 ---> 5e4687b5381f
Step 3/4 : COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
 ---> Using cache
 ---> 81b9c665be08
Step 4/4 : WORKDIR /var/www/html
 ---> Using cache
 ---> 3fe8397e92e6
Successfully built 3fe8397e92e6
Successfully tagged lesson2_php:latest
Pulling mysql (mysql:5.7)...
5.7: Pulling from library/mysql
fc7181108d40: Already exists
787a24c80112: Already exists
a08cb039d3cd: Already exists
4f7d35eb5394: Already exists
5aa21f895d95: Already exists
a742e211b7a2: Already exists
0163805ad937: Already exists
62d0ebcbfc71: Pull complete
559856d01c93: Pull complete
c849d5f46e83: Pull complete
f114c210789a: Pull complete
Digest: sha256:c3594c6528b31c6222ba426d836600abd45f554d078ef661d3c882604c70ad0a
Status: Downloaded newer image for mysql:5.7
Creating lesson2_php_1        ... done
Creating lesson2_mysql_1      ... done
Creating lesson2_phpmyadmin_1 ... done
Creating lesson2_nginx_1      ... done


Видим, что в начале происходит создание сети lesson2_backend, затем сборка образа php, потом может происходить скачивание образов, которых еще нет в системе (pull) и собственно запуск описанных сервисов.

Последний штрих, чтобы все заработало это добавление в hosts или доменов site.local и phpmyadmin.local.

Содержимое index.php может быть следующим:

index.php
<?php

//phpinfo();

$link = mysqli_connect('mysql', 'root', 'root');
if (!$link) {
    die('Ошибка соединения: ' . mysqli_error());
}
echo 'Успешно соединились';
mysqli_close($link);


Тут мы проверяем корректность подключения расширения php — mysqli, которое было добавлено при сборке Dockerfile.

И заметим, что для связи с контейнером используется название сервиса — mysql.

Структура всего проекта получилась следующей:

Структура проекта
habr/lesson2$ tree
.
├── docker
│   ├── mysql
│   │   └── data
│   ├── nginx
│   │   └── conf.d
│   │       └── default.nginx
│   └── php
│       ├── Dockerfile
│       └── php.ini
├── docker-compose.yml
├── html
│   └── index.php
└── proxy
    └── docker-compose.yml

Tags:dockerdocker-compose
Hubs: Website development PHP
+15
25.9k 165
Comments 16

Popular right now

Комплексное обучение PHP
April 19, 202120,000 ₽Loftschool
PHP. Уровень 1. Основы создания сайтов
February 1, 202115,990 ₽Специалист.ру
SEO-специалист
January 25, 202136,000 ₽GeekBrains
UI-дизайнер
January 25, 202159,900 ₽Нетология
UX/UI дизайнер
January 25, 2021104,900 ₽Нетология

Top of the last 24 hours