Статьи

Развертывание контейнеров с помощью Docker Swarm и Docker Networking

Цель этой статьи — изучить новые сетевые функции Docker, представленные в версии 1.9. Мы применим их к кластеру Docker Swarm. В практических целях мы будем развертывать контейнеры в кластере Swarm, созданном локально с помощью Vagrant и использующим Consul в качестве реестра служб и Registrator в качестве инструмента, который будет отслеживать демоны Docker и регистрировать / отменять регистрацию контейнеров, которые мы запускаем / останавливаем. В совокупности Консул и Регистратор будут действовать как обнаружение службы в нашем кластере. Я не буду вдаваться в подробности того, как работает Docker Swarm или служба обнаружения. Вы можете найти больше информации об этом предмете в следующих статьях.

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

Настройка кластера

Обо всем по порядку. Давайте создадим три виртуальные машины, которые мы будем использовать в качестве тренировочного поля. Главный узел swarm будет действовать как главный, а две другие виртуальные машины будут представлять наш кластер, состоящий из двух узлов. Все три виртуальные машины будут работать под управлением Ubuntu и будут созданы с использованием VirtualBox и Vagrant . Пожалуйста, убедитесь, что оба установлены. Вам также понадобится Git для клонирования кода, который будет использоваться в этой статье. Если вы пользователь Windows, пожалуйста, следуйте инструкциям, описанным в разделе Запуск виртуальных машин Linux в Windows, прежде чем углубляться в описанные ниже.

Давайте начнем с создания виртуальных машин, которые будут моделировать наш кластер Swarm.

1
2
3
4
5
6
7
git clone https://github.com/vfarcic/docker-swarm-networking.git
 
cd docker-swarm-networking
 
vagrant up swarm-master swarm-node-1 swarm-node-2
 
vagrant ssh swarm-master

Теперь, когда виртуальные машины созданы, и мы находимся внутри Swarm-Master , давайте предоставим виртуальным машинам Docker, Docker Compose, Consul, Registrator и Swarm. Мы сделаем это через Ansible . Если вы новичок в Ansible, вы найдете множество примеров в этом блоге.

1
2
ansible-playbook /vagrant/ansible/swarm.yml \
    -i /vagrant/ansible/hosts/prod

Playbook swarm.yml убедился, что Docker запущен и настроен для поддержки Swarm. Он также предоставил серверам Docker Compose, Consul, Swarm и Registrator.

Давайте дважды проверим, что все работает как положено.

1
2
3
4
5
export DOCKER_HOST=tcp://localhost:2375
 
docker info
 
curl localhost:8500/v1/catalog/nodes | jq '.'

Переменная DOCKER_HOST указывает Docker отправлять команды ведущему Swarm, работающему через порт 2375 . За этим последовала docker info о docker info которая показала, что в кластере Swarm есть два узла. Наконец, последняя команда запросила список всех узлов, зарегистрированных в Консуле, и получила все три виртуальные машины (один Swarm-мастер и два Swarm-узла) в качестве ответа.

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

Развертывание с помощью Docker Swarm и Docker Networking

Недавно Docker представил новую версию 1.9. Это, без сомнения, самый важный выпуск с версии 1.0. Это дало нам две долгожданные функции; многоузловая сеть и постоянные тома. Сеть делает связывание устаревшим и является функцией, которая нам нужна для соединения контейнеров между несколькими хостами. Больше не нужно, чтобы прокси связывал несколько контейнеров, которые составляют сервис. Это не означает, что прокси-сервер бесполезен, но мы должны использовать его в качестве открытого интерфейса для наших служб и сетей для соединения контейнеров, которые образуют логическую группу. Новые сетевые и прокси-сервисы Docker имеют разные преимущества и должны использоваться для разных случаев использования. Среди прочего, прокси-сервисы обеспечивают балансировку нагрузки и могут контролировать доступ к нашим сервисам. Сеть Docker — это удобный способ подключения отдельных контейнеров, которые образуют единый сервис и находятся в одной сети. Распространенным вариантом использования Docker-сетей может быть служба, требующая подключения к базе данных. Мы можем соединить эти два через сеть. Более того, может потребоваться масштабирование самой службы и запуск нескольких экземпляров. Прокси-сервис с балансировщиком нагрузки должен соответствовать этому требованию. Наконец, другие службы могут нуждаться в доступе к этой услуге. Поскольку мы хотим использовать преимущества балансировки нагрузки, этот доступ также должен осуществляться через прокси-сервер.

мульти-хост-сеть

Эта цифра представляет один общий случай использования. У нас есть масштабированная служба с двумя экземплярами, работающими на узлах 1 и 3. Вся связь с этими службами осуществляется через прокси-службу, которая заботится о балансировке нагрузки и безопасности. Любой другой сервис (будь то внешний или внутренний), который хочет получить доступ к нашему сервису, должен пройти через прокси. Внутренне служба использует базу данных. Связь между службой и базой данных является внутренней и осуществляется через сеть с несколькими узлами. Этот параметр позволяет нам легко масштабировать внутри кластера, сохраняя при этом всю связь между контейнерами, которые составляют один внутренний сервис. Другими словами, вся связь между контейнерами, составляющими сервис, осуществляется через сеть, тогда как связь между различными сервисами осуществляется через прокси.

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

1
2
3
docker network create my-network
 
docker network ls

Вывод команды network ls выглядит следующим образом.

01
02
03
04
05
06
07
08
09
10
NETWORK ID          NAME                           DRIVER
f8a50a3c9c13        swarm-node-1/host              host
8ae6cefc3957        swarm-node-2/host              host
5f68a88668f6        swarm-node-2/bridge            bridge
397107ba0daf        swarm-node-2/none              null
b655577f0030        swarm-node-1/bridge            bridge
efb02b0fa9b9        swarm-node-1/docker_gwbridge   bridge
eb5ff0f0136a        swarm-node-1/none              null
71b80ae02620        my-network                     overlay
ac4261d5e27a        swarm-node-2/docker_gwbridge   bridge

Вы можете видеть, что одной из сетей является my-network, которую мы создали ранее. Он охватывает весь Рой кластер. Мы можем использовать эту сеть с аргументом –net .

1
2
3
4
5
6
7
8
9
docker run -d --name books-ms-db \
    --net my-network \
    mongo
 
docker run -d --name books-ms \
    --net my-network \
    -e DB_HOST=books-ms-db \
    -p 8080 \
    vfarcic/books-ms

Прежде чем мы продолжим, давайте подтвердим, что Swarm распределил контейнеры внутри кластера.

1
docker ps --filter name=books --format "table {{.Names}}"

Вывод в моем случае следующий.

1
2
3
NAMES
swarm-node-2/books-ms
swarm-node-1/books-ms-db

Вы можете видеть, что каждый контейнер был развернут на другом узле. Это главная цель Docker Swarm; распределить контейнеры по кластеру. Вопрос в том, как эти контейнеры могут взаимодействовать друг с другом, если они находятся на отдельных узлах?

Мы запустили два контейнера, которые составляют один сервис; books-ms — это API, который связывается с books-ms-db, который действует как база данных. Поскольку оба контейнера имеют аргумент -net my-network , они оба принадлежат сети my- network. В результате Docker обновил файл hosts, предоставив каждому контейнеру псевдоним, который можно использовать для внутренней связи.

Давайте войдем в контейнер books-ms и посмотрим на файл hosts.

1
docker exec -it books-ms cat /etc/hosts

Вывод команды exec выглядит следующим образом.

1
2
3
4
5
6
7
8
9
10.0.0.2    3166318f0f9c
127.0.0.1   localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
10.0.0.2    books-ms-db
10.0.0.2    books-ms-db.my-network

Интересной частью файла hosts являются последние две записи. Докер обнаружил, что контейнер books-ms-db использует ту же сеть, и обновил файл hosts , добавив books-ms-db (имя контейнера DB) и books-ms-db.my-network (имя контейнера DB плюс название сети) псевдонимы. Если используется какое-то соглашение, тривиально кодировать наши сервисы так, чтобы они использовали псевдонимы, подобные этому, для связи с ресурсами, расположенными в отдельном контейнере (в данном случае с базой данных).

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

1
docker exec -it books-ms env

Вывод команды следующий.

1
2
3
4
5
6
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=eb3443a66355
DB_HOST=books-ms-db
DB_DBNAME=books
DB_COLLECTION=books
HOME=/root

Как видите, одной из переменных среды является DB_HOST со значением books-ms-db .

Сейчас у нас есть сеть Docker, которая создала псевдоним hosts -ms-db, указывающий на IP сети, созданной Docker. У нас также есть переменная окружения DB_HOST со значением books-ms-db . Код службы использует эту переменную для подключения к базе данных. Вы можете использовать другую логику. Важной частью является то, что Docker обновил файл hosts с помощью псевдонимов, которые можно использовать для доступа к любому другому контейнеру, который принадлежит той же сети наложения .

Существует даже лучший способ создать сеть, чем запуск команды create network . Прежде чем мы попробуем это, давайте остановим эти два контейнера и удалим сеть.

1
2
3
docker rm -f books-ms books-ms-db
 
docker network rm my-network

На этот раз мы будем запускать контейнеры через Docker Compose. Хотя мы могли бы использовать аргумент net внутри docker-compose.yml и, таким образом, выполнять точно такой же процесс, как мы делали ранее, лучше использовать новый аргумент Docker Compose — x-network .

1
2
3
cd /vagrant/booksms
 
docker-compose --x-networking up -d db app

Вывод команды, которую мы только что выполнили, следующий.

1
2
3
Creating network "booksms" with driver "None"
Creating booksms_app_1
Creating books-ms-db

Перед созданием приложения служб и базы данных Docker создал новую сеть под названием booksms . Имя сети совпадает с именем проекта (по умолчанию используется имя каталога).

Мы можем подтвердить, что сеть была создана с помощью команды docker network ls .

1
docker network ls

Вывод следующий.

01
02
03
04
05
06
07
08
09
10
NETWORK ID          NAME                           DRIVER
6e5f816d4800        swarm-node-1/host              host
aa1ccdaefd70        swarm-node-2/docker_gwbridge   bridge
cd8b1c3d9be5        swarm-node-2/none              null
ebcc040e5c0c        swarm-node-1/bridge            bridge
6768bad8b390        swarm-node-1/docker_gwbridge   bridge
8ebdbd3de5a6        swarm-node-1/none              null
58a585d09bbc        booksms                        overlay
de4925ea50d1        swarm-node-2/bridge            bridge
2b003ff6e5da        swarm-node-2/host              host

Как видите, наложенные сетевые книги были созданы.

Мы также можем проверить, что файл hosts внутри контейнеров был обновлен.

1
docker exec -it booksms_app_1 cat /etc/hosts

Вывод следующий.

1
2
3
4
5
6
7
8
9
10.0.0.2    3166318f0f9c
127.0.0.1   localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
10.0.0.3    books-ms-db
10.0.0.3    books-ms-db.my-network

Наконец, давайте посмотрим, как Swarm распространял наши контейнеры.

1
docker ps --filter name=books --format "table {{.Names}}"

Вывод следующий.

1
2
3
NAMES
swarm-node-2/books-ms-db
swarm-node-1/booksms_app_1

Swarm развернул контейнер приложения в swarm-node-1, а контейнер db — в swarm-node-2 .

Наконец, давайте проверим, правильно ли работает сервис book-ms . Мы не знаем, на каком сервере Swarm развернут контейнер или какой порт открыт. Поскольку у нас нет (пока) прокси-сервера, мы получим IP-адрес и порт службы от Consul, отправим запрос PUT для сохранения некоторых данных в базе данных, находящейся в другом контейнере, и, наконец, отправим запрос GET чтобы проверить, можем ли мы получить запись. Поскольку у нас нет прокси-службы, которая бы обеспечивала перенаправление запросов на правильный сервер и порт, нам придется получить адрес и порт от Consul. Для получения дополнительной информации о том, как настроить прокси-сервис, пожалуйста, обратитесь к статьям Scaling To Infinity с Docker Swarm, Docker Compose и Consul .

01
02
03
04
05
06
07
08
09
10
11
12
ADDRESS=`curl \
    localhost:8500/v1/catalog/service/books-ms \
    | jq -r '.[0].ServiceAddress + ":" + (.[0].ServicePort | tostring)'`
 
curl -H 'Content-Type: application/json' -X PUT -d \
  '{"_id": 2,
  "title": "My Second Book",
  "author": "John Doe",
  "description": "A bit better book"}' \
  $ADDRESS/api/v1/books | jq '.'
 
curl $ADDRESS/api/v1/books | jq '.'

Последний вывод команды выглядит следующим образом.

1
2
3
4
5
6
7
[
  {
    "author": "John Doe",
    "title": "My Second Book",
    "_id": 2
  }
]

Если бы служба не могла обмениваться данными с базой данных, расположенной в другом узле, мы не смогли бы ни поместить, ни получить данные. Сеть между контейнерами, развернутыми на отдельных серверах, работала! Все, что нам нужно сделать, это использовать дополнительный аргумент с Docker Compose ( –x-network ) и убедиться, что код службы использует информацию из файла hosts.

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

Сеть Docker была долгожданной функцией, которая позволяет нам распределять контейнеры, не опасаясь, смогут ли они общаться друг с другом. Наконец, мы можем распространять контейнеры без ограничений, введенных ссылками (связанный контейнер должен был работать на том же сервере). Больше нет необходимости в обходных решениях, которые некоторым из нас приходилось использовать в прошлом. Это захватывающая функция, которая, несомненно, позволит Docker Swarm перейти на следующий уровень.

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

1
2
3
exit
 
vagrant halt