Взаимодействие Docker контейнеров

Запустить приложение в контейнере это, конечно, интересно, но еще интереснее запустить сразу несколько и разрешить им между собой взаимодействовать. Допустим, вдоволь наигравшись с микросервисами, я решил разделить своё настоящее вэб-приложение на:

  1. контейнер со статическим контентом и вэб-сервером и
  2. контейнер с данными и RESTful сервисом.

Первый контейнер открывает 80й порт, самостоятельно раздаёт html/css/js и обращается ко второму контейнеру, когда приходит запрос на данные.

Схема приложения

Схема простая, кроме одного «но». Как первому контейнеру найти и достучаться до второго?

Сеть в Docker.

Кроме контейнеров и образов Docker оперирует еще и собственными сетями, которых по-умолчанию он устанавливает аж три ( docker network ls ):

  1. bridge
  2. host
  3. none

Контейнеры подключаются к bridge по-умолчанию, а тот уже раздаёт IP адреса из своего диапазона, и через сетевые мосты перенаправляет траффик в большой интернет. Самое приятное: если пытливый программистский разум не вмешивался в настройки Докера, то все контейнеры внутри своей сети могут свободно видеть друг друга и общаться между собой, а снаружи к ним можно достучаться только привязав контейнерные порты к  портам хоста (например, -p80:80 ).

Это настолько круто, что я повторю последнее предложение примером: можно запустить nginx контейнер и не открывать его 80й порт хосту — другие контейнеры из его сети всё еще смогут с ним общаться.

То есть, зная IP адрес контейнера, общаться с ним — не проблема. А вот знать адрес контейнера — проблема. То, что сегодня nginx висит на 172.17.0.2 совсем не значит, что здесь же он будет и завтра. Поменялся порядок запуска контейнера, перенесли его в другую сеть, поменяли настройки Docker-сервера — и айпишка поменялась. Нужно что-то более постоянное.

Как пережить динамическую айпишку.

Есть аж три способа:

  1. использовать --link
  2. использовать имя контейнера и свою сеть
  3. использовать сетевой псевдоним

 —link

Link уже сегодня считается устаревшей фичей, но при этом всё еще работает, так что иногда — почему бы и нет.

У всех контейнеров есть имя — динамически сгенерированное, либо явно заданное. Я запускал nginx контейнер без --name  параметра, поэтому Docker обозвал его вместо меня — nauseous_aryabhata. Если запустить второй контейнер с параметром --link=nauseous_aryabhata , то он получит целую кучу переменных среды и запись в /etc/hosts  впридачу — всё для того, чтобы найти связанный контейнер:

Теперь, если мне нужно отправить запрос ко второму контейнеру, можно просто воспользоваться его именем.

Использовать имя контейнера и создать свою сеть.

Что мне не нравится в приятных фичах, так это слово legacy, поэтому --link  всё-таки хорош больше с эстетической точки зрения. Есть другой способ: если не использовать bridge-сеть по умолчанию, в создать свою (user defined network), то Docker добавит к ней маленький невидимый DNS сервис, через который можно общаться внутри своей сети со всеми контейнерами по имени. И тут же быстрый пример:

Шаг первый. Создаём свою сеть:

Шаг второй. Запускаем в этой сети nginx. В этот раз с явно указанным именем web:

Шаг третий. Запустим в этой же сети еще один контейнер, и проверим, видит ли он web:

Та-дааам!

Использовать сетевой псевдоним.

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

Теперь все запросы к http://webserver встроенный DNS так же перенаправит к nginx-контейнеру. Можно также задавать одновременно и имя, и псевдоним, и использовать оба.

Мораль.

В общем, настроить взаимодействие контейнеров между собой по TCP/HTTP — не проблема. Причём, я взял только самые базовые инструменты. А ведь есть еще разные типы сетей, можно создавать собственные сетевые мосты и даже overlay networks, которые объединяют сети между несколькими машинами в одну так, что контейнерам кажется, будто они все находятся на одном хосте. Но это — повод для других статей.

4 комментария для “Взаимодействие Docker контейнеров

  1. А если я хочу по IP подключатся с одного контейнера на 2-й вместо имени. и чтобы IP был статический для nginx контейнера. Это возможно?

    1. По IP должно тоже работать, потому что обращение по имени, в конечном итоге, это запрос на DNS сервер за реальным IP адресом, и потом уже идёт общение по нему.
      Контейнеру можно привязать статическую айпишку через параметр --ip. Я так никогда не делал, но интернеты говорят, что сработает — https://stackoverflow.com/questions/27937185/assign-static-ip-to-docker-container

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *