Pull to refresh

Мастер-контейнер для Docker сети

Reading time3 min
Views8.9K

Упаковка веб-приложения в Docker - довольно простая задача, если разобраться с базовыми понятиями работы контейнеров. Готовим контейнер для бэкенда, для базы данных, для фронтенд-приложения - и вуаля, приложение полноценно функционирует. В большинстве случае стандартная настройка сети и конфигурация в Docker покрывает все нужды разработчиков.

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

Кейс

В работе был очень простой проект, состоящий всего из трех компонентов:

  1. SPA приложение на ReactJS

  2. Бэкенд на Symfony 6

  3. База данных PostreSQL 14

Загвоздкой оказался один нюанс. Бэкенд должен был динамически создавать новый внутренний сервер, слушающий другой порт, а приложение на React должно иметь возможность обращаться к нему напрямую.

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

Проблема

Первоначальный вариант конфигурации Docker мог бы выглядеть примерно так.

После сборки и запуска трех контейнеров у нас есть доступ как к бэкенду через localhost:8000, так и к фронтенду (стандартно на localhost:3000). Как уже было сказано, PHP контейнер создаст новый компонент. Для простоты начнем с попытки прописать в конфигурации того же контейнера фиксированный порт для этих целей. Тогда в docker-compose.yml мы сделаем примерно такие строки:

backend:
  ports:
    - 8000:80
    - 8888:8888

И с этой же попытки мы сталкиваемся с настоящей проблемой. Во время сборки и запуска контейнеров динамического компонента не существует, и он не выделен в отдельный контейнер, так что подключиться по имени контейнера средствами Docker network не получится. Как же заставить фронтенд обратиться к новому компоненту внутри контейнера “backend”?

Решение

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

Возможности настройки предоставляют нам не только разные типы сетей (host, bridge). На самом деле, мы можем внедрить один контейнер в сеть другого контейнера прямо в docker-compose.yml. В этом случае первый контейнер станет своего рода “мастер-контейнером”, а второй - дочерним с точки зрения сетевой настройки. И поскольку контейнер backend несет в себе все эти дополнительные сложности, сделаем мастером именно его.

Для начала добавим связку хоста с внутренней сетью контейнера. Для этого в конфигурации контейнера backend добавим секцию extra_hosts и уберем проброс портов для контейнера фронтенд-приложения, там они больше не понадобятся. Вместо этого, включим проброс порта 3000 с локальной машины сразу в контейнер ‘backend’.

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

Результат

С помощью новой конфигурации контейнер фронтенд-приложения имеет возможность обращаться к любому порту, назначенному родительскому контейнеру ‘backend’, так как теперь у них обоих общий localhost. Мы же можем обращаться к ним через localhost нашей машины, поскольку привязали их друг к другу с помощью секции extra_hosts.

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

Tags:
Hubs:
-2
Comments7

Articles