Проблема

После прохождения этих руководств:







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

К сожалению, чтобы получить максимальную отдачу от контейнеров, нам необходимо разрешить совместную работу нескольких контейнеров. Это откроет еще одно измерение в нашем наборе инструментов при разработке любого приложения/системы/(микро)сервиса(ов).

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

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

Но как заставить несколько контейнеров работать вместе?

Решение

Мы можем заставить несколько контейнеров работать вместе так же, как все наши компьютеры работают вместе в Интернете, через сеть. Docker Compose заботится об объединении контейнеров в сеть. Нам просто нужно знать, как его использовать.

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

Вот шаги, которые мы предпримем:

  1. обновить наш существующий docker-compose.yml, чтобы создать несколько контейнеров
  2. убедиться, что все контейнеры могут достигать и взаимодействовать друг с другом
  3. обновите наш docker-compose.yml, чтобы хост мог получить доступ ко всем контейнерам

На этой диаграмме показано конечное состояние нашей системы:

Передний вопрос

В этом руководстве предполагается, что вы выполнили шаги из этого руководства:



Вот соответствующий код из этого руководства, вставленный сюда для удобства:

# docker-compose.yml
version: '3'
services:
  app:
    build: .
    ports:
      - "8081:8080"
# Dockerfile
FROM tomcat:9.0.12                                               COPY index.html /usr/local/tomcat/webapps/ROOT/index.html
# index.html
<html>
  <body>
    <h1>docker-compose build worked!</h1>
  </body>
</html>

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



Обновите docker-compose.yml, чтобы создать несколько контейнеров.

Во-первых, давайте добавим еще 2 службы к нашему docker-compose.yml, чтобы у нас было всего 3 службы для исследования нашей сети.

version: '3'
services:
  app:
    build: .
    ports:
      - "8081:8080"

  dependent-service1:
    image: tomcat:9.0.12

  dependent-service2:
    image: tomcat:9.0.12

Этого было бы достаточно, чтобы мы начали тестировать сетевую функцию Docker Compose, но трудно различить две зависимые службы, поскольку они идентичны. Итак, давайте настроим заставку каждой зависимой службы, чтобы мы могли точно знать, что взаимодействуем с правильной зависимой службой.

version: '3'
services:
  app:
    build: .
    ports:
      - "8081:8080"

  dependent-service1:
    image: tomcat:9.0.12
    volumes:
    - ./index.dependent-service1.html:/usr/local/tomcat/webapps/ROOT/index.html

  dependent-service2:
    image: tomcat:9.0.12
    volumes:
    - ./index.dependent-service2.html:/usr/local/tomcat/webapps/ROOT/index.html

Этот docker-compose.yml использует функцию volumes, описанную здесь:



По сути, мы изменили заставку каждой зависимой службы, не требуя отдельного Dockerfile для каждой службы.

Затем нам нужно добавить новые страницы-заставки, которые будут предоставляться нам из каждой зависимой службы.

# index.dependent-service1.html
<html>
  <body>
    <h1>dependent-service1</h1>
    <p>this could be an backend API service</p>
  </body>
</html>
# index.dependent-service2.html
<html>
  <body>
    <h1>dependent-service2</h1>
    <p>this could be a database service</p>
  </body>
</html>

Теперь у нас есть 3 разных сервиса с 3 разными страницами-заставками.

Убедитесь, что все контейнеры могут связаться друг с другом

Во-первых, давайте поднимем все службы:

$ docker-compose up
Creating network "docker-compose-network_default" with the default driver
Creating docker-compose-network_dependent-service1_1 ... done
Creating docker-compose-network_dependent-service2_1 ... done
Creating docker-compose-network_app_1                ... done
Attaching to docker-compose-network_dependent-service2_1, docker-compose-network_dependent-service1_1, docker-compose-network_app_1                                                                                
dependent-service2_1  | NOTE: Picked up JDK_JAVA_OPTIONS:  --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED                
dependent-service1_1  | NOTE: Picked up JDK_JAVA_OPTIONS:  --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED                
app_1                 | NOTE: Picked up JDK_JAVA_OPTIONS:  --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED
...
dependent-service1_1  | 10-Nov-2018 23:25:58.010 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 6203 ms
dependent-service2_1  | 10-Nov-2018 23:25:58.320 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 5960 ms
app_1                 | 10-Nov-2018 23:25:58.735 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 6230 ms

Теперь мы можем включить службу app с помощью docker-compose run и взаимодействовать с зависимыми службами. В этом руководстве более подробно рассказывается о docker-compose run:



$ docker-compose run app bash
app$ curl dependent-service1:8080
<html>
  <body>
    <h1>dependent-service1</h1>
    <p>this could be an backend API service</p>
  </body>
</html>
app$ curl dependent-service2:8080
<html>
  <body>
    <h1>dependent-service2</h1>
    <p>this could be a database service</p>
  </body>
</html>

Мы только что убедились, что контейнер app может обращаться к обоим зависимым контейнерам служб по их именам служб, dependent-service1 и dependent-service2. Эти имена хостов службы берутся непосредственно из docker-compose.yml и могут быть настроены так, чтобы они отличались от имени службы, используя aliases.

Мы также можем убедиться, что зависимые службы могут связаться с app и друг с другом.

$ docker-compose run dependent-service1 bash
dependent-service1$ curl app:8080
<html>
  <body>
    <h1>docker-compose build worked!</h1>
  </body>
</html>
dependent-service1$ curl dependent-service2:8080
<html>
  <body>
    <h1>dependent-service2</h1>
    <p>this could be a database service</p>
  </body>
</html>
dependent-service1$ exit
exit
$ docker-compose run dependent-service2 bash
dependent-service2$ curl app:8080
<html>
  <body>
    <h1>docker-compose build worked!</h1>
  </body>
</html>
dependent-service2$ curl dependent-service1:8080
<html>
  <body>
    <h1>dependent-service1</h1>
    <p>this could be an backend API service</p>
  </body>
</html>

Мы только что показали, как каждый контейнер может взаимодействовать с другими контейнерами, используя сетевую функцию Docker Compose.

Обновите docker-compose.yml, чтобы разрешить хосту доступ к контейнерам.

Чтобы наше приложение функционировало, достаточно, чтобы наши контейнеры взаимодействовали друг с другом. Однако, когда мы устраняем неполадки при разработке приложения, часто бывает полезно взаимодействовать с нашими контейнерами с хоста.

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

version: '3'
services:
  app:
    build: .
    ports:
      - "8081:8080"

  dependent-service1:
    image: tomcat:9.0.12
    volumes:
    - ./index.dependent-service1.html:/usr/local/tomcat/webapps/ROOT/index.html
    ports:
      - "8082:8080"

  dependent-service2:
    image: tomcat:9.0.12
    volumes:
    - ./index.dependent-service2.html:/usr/local/tomcat/webapps/ROOT/index.html
    ports:
      - "8083:8080"

Используя ключ ports, как мы это делали для службы app, теперь мы можем перейти к каждой странице-заставке контейнера. Не забудьте отключить и поднять (docker-compose down && docker-compose up) сервисы, чтобы новые конфигурации портов подхватывались.

Поздравляем, вы только что подключили к сети все свои контейнеры, что позволяет любой службе использовать любую другую службу. Это означает, что вы можете объединить любое количество приложений/сервисов/контейнеров в любую систему, о которой только можно мечтать. Каждый контейнер аналогичен блоку Lego; соединяя вместе любое количество различных контейнеров, вы можете создавать неограниченное количество творений Lego.

Примечания

Хост-порт против контейнерного порта

Обратите внимание на разницу между портом контейнера и портом хоста.

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

С другой стороны, когда хост пытается получить доступ к контейнеру, он использует порты хоста 8081, 8082 и 8083, потому что это порт, который Docker Compose сопоставил с портами контейнера. Каждый порт хоста должен быть другим, потому что один порт хоста может быть сопоставлен только с одним портом контейнера. Порты 8081, 8082 и 8083 указаны в docker-compose.yml, решение о котором принимает разработчик.

использованная литература