null

Docker networking или способы прострелить себе ногу

Мотивация

7 правил мотивации

Начну с того, почему выбрал именно эту тему для курения сегодня. Для веб-разработчиков даже с минимальным опытом работы известна по крайней мере одна технология контейнеризации, в частности, наиболее известная (ИМХО) - Docker. Докер позволяет изолировать веб-приложение от других приложений на рабочей станции разработчика, описать универсальную конфигурацию, которая вообще негарантированно гарантированно запустится на компьютерах коллег. Также docker упрощает управление локальными базами данных, особенно, если в них не нужно разворачивать дампы. Накосячил в коде и разрушил согласованность данных, тыкая API в постмане? Не беда, 1-2 команды и ты получишь девственно чистую бд. Лень засорять локальный компьютер брокерами сообщений, SSO, СУБД и прочим зоопарком вспомогательных сервисов прекрасного и чудесного мира веба? Docker - в помощь.

Вот только есть одно но. Большое и жирное НО. А что если проект включает в себя несколько независимо разрабатываемых сервисов? И что, скажете Вы, один конкретный разработчик должен трудиться над одним конкретным сервисом! Так завещала нам СОА. Вы, конечно, будете правы, однако когда это все идет "по учебнику"? :) Даже если не брать в рассмотрение лидов, которые чаще всего знают обо всех сервисах в проекте и периодически трогают их всех, иногда разработчику приходится реализовывать таски, затрагивающие сразу 2 или даже 3 сервиса. Без локального запуска всех необходимых инстансов протестировать локально изменения не получится.

Как раз в этот момент возникает необходимость обеспечить общение нескольких сервисов из разных контейнеров между собой, а это значит нужно:

а) настроить циркуляцию трафика между контейнерами А и Б на уровне docker-сетей,

б) сконфигурировать сервисы, указав нужные ip-адреса, порты и прочее.

И вот здесь идеальный мир с логотипа докера "кораблик и контейнеры" рассыпается в прах.

Да, да, я знаю, что необходимость в докер-сети возникает даже в сценарии сервер-бд. Однако он решается простым использованием имени контейнера с бд и пробросом порта сервера на localhost, так как с localhost-а ходить по ip в бд требуется лишь в исключительно редких случаях, с которыми автор сталкивался... никогда. Более наглядно проблема описывается именно примером с несколькими сервисами - далее будет понятно, почему так.

Суть проблемы

Проблемы трейдеров или почему сложно торговать

Итак, давайте подумаем, какой самый распространенный способ используется разработчиком, настраивающим связь между сервисами в разных контейнерах? Правильно, проброс портов на localhost. 

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

Почему это проблема? - как только Dockerfile или docker-compose файл должен оказаться на локальной машине коллег, начинаются проблемы с тем, что порт на локалхосте уже занят и нужно менять файл под себя. В целом, проблема небольшая, ровно до тех пор, пока этот файл не оказывается в системе управления версиями.

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

Идем дальше. Вторая страница гугла приводит нас к "выковыриванию" IP адреса, назначенного контейнеру и обращению по нему. Почему выковыривания? Нет, ну вы видели, какую команду надо написать, чтобы достать этот IP адрес??? Либо копаться в тонне json-а, что тоже не очень приятно. Однако этот способ, мягко говоря, неуниверсальный - docker не гарантирует, что при пересоздании контейнера IP адрес будет назначен ему такой же. Сразу возникает желание "прибить гвоздями" конкретный немаршрутизируемый IP-адрес к контейнеру. Это приводит нас к третьему способу - ручное конфигурирование сети docker.

Надо признать, это наиболее гибкий способ организации взаимодействия между контейнерами с сохранением возможности обращаться к приложениям в контейнерах с localhost-а. Еще более удобный, когда речь идет о docker-compose-файле. Однако и этот способ несет в себе подводные камни.  Например, знакомый разработчик, у которого есть macbook жаловался, что доступ по IP-адресу контейнера на OS X не работает. Также все еще остается вероятность конфликта ip адресов между различными контейнерами. Но самое главное. Никогда. НИКОГДА. Не назначайте сети 192.168.X.X докерным сетям. Устройства в вашей локальной сети (или VPN) могут резко и без предупреждения оказаться НЕДОСТУПНЫМИ. По этой же причине, лучше избегать автоматического назначения IP адресов контейнерам - Docker использует сети 172.16.0.0/12 - 172.31.0.0/12 и когда они заканчиваются (а они заканчиваются, поверьте) докер начинает использовать 192.168.X.X и с определенной долей вероятности Вы получите конфликт.

Привет нашему корпоративному VPN :) Фух, выговорился.

Решение

УЗБАЙГОЙЗЯ! Есть решение!, Мем Лемур узбагойся

Чек-лист как не отстрелить себе ногу (автор честно, честно будет его придерживаться и сам):

1. Проброс портов - зло (если очень любишь mac - люби страдать).

2. В конфигурации приложений для обращения к другим сервисам лучше всего использовать имена контейнеров - они изменяются реже всего.

3. Много сервисов - один docker-compose. Наиболее удобным решением с точки зрения управления docker-compose файлами, да и Dockerfile-ами - это один, специально созданный для этого репозиторий. Не будет такого, что нужно поднять сервисы из двух реп, а при этом в docker сетях конфликты или есть строго определенный порядок запуска контейнеров. К тому же, нередко, если сервис А ходит в сервис Б (и это не почему-то не мокается в dev окружении) - ему всегда нужен доступный сервис Б, то есть наш сервис А, поднятый без Б - просто бесполезный набор кода. Кроме того, если конфигурационные файлы docker-а лежат в одном месте, их проще править без ошибок.

4. Сеть 192.168.Х.Х для Docker-а - зло.

5. Обязательно. Обязательно. Обязательно описывайте любые "костыли", привинченные для запуска, особенно первого, вашего зоопарка контейнеров. Что, где поправить, какие конфликты возможны, желательно, тестировать конфигурацию контейнеров на всех используемых разработчиками платформах и описывать различие использования (например, тот же OS X). Настроив один раз на одной машине, вы сами обязательно забудете все тонкости, когда нужно будет повторить это на другой машине.

6. Не забывайте периодически чистить docker-сети (docker network list && docker network rm).

 

Будьте здоровы и не прострелите себе ногу!