Как получить доступ из одного докер-контейнера в другой докер-контейнер

Автор оригинала: Rafiullah Hamedy
  • Перевод
  • Tutorial
Изображение от Mike Wheatley
Изображение от Mike Wheatley

Цель этой статьи — показать как мы можем сконфигурировать два и более контейнера, чтобы они могли взаимодействовать друг с другом. В этой статье мы сделаем следующее:

  • Создадим образ Docker используя простой веб-сервис с использованием Python и Flask.

  • Запустим два отдельных контейнера

  • Создадим сеть в Docker

  • Объединим контейнеры используя созданную сеть

Подготовка

Чтобы пойти дальше вы должны обладать средними знаниями в программировании и API. Также понадобится докер инсталлированный локально на вашей машине.

Руководство об основах работы с контейнерами можно найти здесь:

Идея

Для этой статьи мы будем использовать два простых веб-сервиса, каждый со своим эндпойнтом. Давайте назовём первый сервис "ping", второй — "pong", а нашим замыслом будет отправка запроса сервисом "ping" к сервису "pong" так, чтобы они могли играть в пинг-понг.

Я использовал Flask и Docker чтобы создать простое приложение и вы можете прочитать больше о тех базовых командах Docker что я использовал, в этом руководстве.

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

Фото от https://unsplash.com/@ellenqin
Фото от https://unsplash.com/@ellenqin

Сервис "ping"

Наши сервисы - очень простые flask-приложения. В app.py будут наши эндпойнты.

В нашем случае сервис "ping" будет иметь эндпойнт "/ping", который будет отправлять запросы к сервису "pong" в эндпойнт "/pong". Если сервис "pong" недоступен, то он просто вернёт "Ping …”. В противном случае сервис вернёт “Ping … Pong”.

В requirements.txt перечислены все модули, которые мы будем использовать, а в Dockerfile перечислены все шаги, которые помогут нам собрать образ.

Сервис "pong"

Так же, как и сервис "ping", наш сервис "pong" представляет собой flask-приложение и имеет эндпойнт "/pong", как показано ниже.

Сервис "ping" сервис мы запустим на порту 5000, а сервис "pong" на порту 5001.

Собираем образы Docker

Source: https://www.metricfire.com/blog/how-to-build-optimal-docker-images/
Source: https://www.metricfire.com/blog/how-to-build-optimal-docker-images/

Сейчас у нас есть два python-сервиса с их Dockerfile. Давайте соберём образы Docker для них.

cd ping-service
docker build -t ping-service .

и

cd pong-service
docker build -t pong-service .

После того как выполним команду docker images мы должны увидеть два образа:

REPOSITORY          TAG                 IMAGE ID            CREATED              SIZE
pong-service        latest              968a682344de        7 seconds ago        124MB
ping-service        latest              6e079525fd69        About a minute ago   128MB
python              3.8-slim-buster     b281745b6df9        8 days ago           114MB

Запуск контейнеров

Теперь у нас есть образы, давайте создадим из них контейнеры и запустим их.

cd ping-service
docker run --name ping-service-container -p 5000:5000 ping-service

И ожидаемый вывод в консоль будет подобен следующему:

 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 761-609-740

Если мы выполним команду curl http://0.0.0.0:5000 мы должны получить вывод сообщения Hello, I am ping service!

Теперь давайте запустим контейнер для сервиса "pong":

cd pong-service
docker run --name pong-service-container -p 5001:5001 pong-service

А сейчас давайте выполним docker container ls, чтобы посмотреть на созданный контейнеры:

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                    NAMES
d7eb5ee014fb        pong-service        "python app.py"     13 seconds ago      Up 11 seconds       0.0.0.0:5001->5001/tcp   pong-service-container
d2331893e5b9        ping-service        "python app.py"     3 minutes ago       Up 3 minutes        0.0.0.0:5000->5000/tcp   ping-service-container

Мы видим, что у нас теперь есть два контейнера с именами pong-service-containerиping-service-container.

Настраиваем сеть в Docker

Без сети наши контейнеры не смогут взаимодействовать друг с другом. Или другими словами, "ping-service-container" не сможет отправить запрос в эндпойнт "/pong" контейнера "pong-service-container".

Мы можем сделать доступным взаимодействие через сеть в Docker посредством следующих шагов:

  • Создаём сеть

  • Добавляем контейнеры в сеть

И таким образом всем контейнеры в одной докер-сети могут взаимодействовать между собой через имя контейнера или IP-адрес.

Давайте выполним эти вышеуказанные шаги.

Создаём сеть в Docker

Давайте создадим сеть с именем ping-pong-network

docker network create ping-pong-network

и когда мы выполним команду docker network inspect ping-pong-network, мы получим:

TheDarkSide:pong-service raf$ docker network inspect ping-pong-network
[
    {
        "Name": "ping-pong-network",
        "Id": "b496b144d72d9d02795eb0472351b093d6b4f1d0015a37e1525d4d163e7ec532",
        "Created": "2021-04-18T22:16:25.2399196Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.25.0.0/16",
                    "Gateway": "172.25.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {},
        "Labels": {}
    }
]

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

docker network connect ping-pong-network ping-service-container
docker network connect ping-pong-network pong-service-container

И если теперь запустим инспектирование сети(docker network inspect ping-pong-network), то в секции Containers мы увидим наши контейнеры:

"Containers": {
            "d2331893e5b9dad95a2691b81c256a9f07d4bf62c10601115483d45f8d7b8e2a": {
                "Name": "ping-service-container",
                "EndpointID": "3a9e8eea9802602652719461681d3ad4bc7c603697bc1c1b027e35876fdddad7",
                "MacAddress": "02:42:ac:19:00:02",
                "IPv4Address": "172.25.0.2/16",
                "IPv6Address": ""
            },
            "d7eb5ee014fbdb850a19ebb216a56f8b7ebd10db62af197d2d17f5be30ee0210": {
                "Name": "pong-service-container",
                "EndpointID": "901ba7f76df59498bd662742536ee31a56a26cc4eedd35d4bd681c9788be5291",
                "MacAddress": "02:42:ac:19:00:03",
                "IPv4Address": "172.25.0.3/16",
                "IPv6Address": ""
            }
        }

И как было сказано выше, контейнеры могут взаимодействовать друг с другом используя имя контейнера или IPv4 адрес.

Проверяем взаимодействие контейнеров

Когда оба сервиса — "ping" и "pong" будут объединены общей сетью, то запрос к эндпойнту "/ping" сервиса "ping":

TheDarkSide:pong-service raf$ curl http://0.0.0.0:5000/ping

нам вернёт:

Ping ... Pong

Для тестирования остановим один из контейнеров и затем проведём инспекцию сети. Мы должны будем увидеть только один контейнер.

Видеоруководство

Кому лень читать, кто больше любит видео и сюда пролистал "по диагонали", может посмотреть это руководство в формате видео.


Заключение

Контейнеры в одной докер-сети могут взаимодействовать используя свой IP адрес или имя контейнера.

Это удобно при использовании Docker во время разработки или в производственном окружении, когда вы бы хотели использовать отдельные контейнеры для разных сервисов — например, базы данных, фронтенда, бэкенда, поиска и т.д.

Средняя зарплата в IT

120 000 ₽/мес.
Средняя зарплата по всем IT-специализациям на основании 9 227 анкет, за 1-ое пол. 2021 года Узнать свою зарплату
Реклама
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее

Комментарии 10

    +1
    Спасибо за статью! В свое время замучился искать способ взаимодействия между контейнерами.
      +1
      Спасибо за статью. Я осваиваю сейчас микросервисную архитектуру (правда с другими ЯП). К моим сервисам есть требование − они не должны «вылетать» если теряют сеть. Должны терпеливо ждать пока сеть востановится и продолжить работу.
      Я это требование имплементировал. Как теперь эффективнее всего его тестировать? Как эмулировать разрыв и восстановление связи между пинг и понг контейнерами?
        0
        Как эмулировать разрыв и восстановление связи между пинг и понг контейнерами?

        Потушить понг контейнер, а потом его запустить.
        docker stop pong-service-container / docker start pong-service-container

          0
          Спасибо. А реально без остановки понга? У меня на самом деле два десятка контейнеров с микросервисами. Хотелось бы потушить сеть и тестировать одновременно поведение всех контейнеров. А потом ее включить и снова смотреть поведение всех контейнеров.
            0

            Ну скажем можно перечислить имена контейнеров в файле my-containers
            Далее глушим сеть (убираем контейнеры из нее):
            cat my-containers | xargs docker network disconnect my-net
            И поднимаем (возвращаем контейнеры):
            cat my-containers | xargs docker network connect my-net

        +4

        Я бы порекомендовал сразу использовать docker-compose, он автоматически создаёт сеть для контейнеров, плюс в пару директив можно раскидать контейнеры по любому количеству сетей

          –2

          +1, но есть один ньюанс.
          Если изменится один из образов, после пересоздания контейнера из этого образа (например через docker-compose up -d), так же может измениться и его IP адрес. Но другие контейнеры об этом не узнают и будут долбиться на старый (который резолвился при их запуске). Решается перезапуском всех контейнеров из docker-compose.yaml (docker-compose restart)
          Например в связке nginx+php-fpm при изменении php образа и пересоздания контейнера, nginx может начать отдавать 502 Bad gateway

            0
            А использование системы сетевого обнаружения, например Consul, это уже перебор будет?
              0
              Можно захардкодить в docker-compose.yml IPv4 и проблемы быть не должно.
              Пример: pastebin.com/NUBeVbX3
              Как уже писали, можно использовать что-то для управления DNS именами внутри виртуальных сетей Docker (docker swarm, k8s, etc.). Когда сам искал решение, то предлагали просто в nginx выставить resolver на IP docker DNS (в названии не уверен), но у меня не срабатывало. Вам уже дали ответ, что Consul как вариант, но если проект мелкий, то не вижу смысла тащить его.
            0
            Смущает, что в статье нет упоминания такой вещи как host.docker.internal, правда это для Mac и Windows, в более старых версиях docker.for.mac.localhost, docker.for.win.localhost. Для LINUX так же можно реализовать с помощью --add-host=host.docker.internal:host-gateway. В целом сиже на Mac, постоянно пользуюсь, когда нужно проверить взаимодействие нескольких сервисов поднятых локально.

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

            Самое читаемое