Статьи

Масштабирование до бесконечности с помощью Docker Swarm, Docker Compose и Consul (Часть 1/4) — Вкус того, что грядет

Эта серия разделена на следующие статьи.

В предыдущих статьях много внимания уделялось непрерывной доставке и контейнерам с Docker . В статье «Непрерывная интеграция, доставка или развертывание с Jenkins, Docker и Ansible» я объяснил, как непрерывно создавать, тестировать и развертывать микросервисы, упакованные в контейнеры, и делать это на нескольких серверах, без простоев и с возможностью отката. Мы использовали Ansible, Docker, Jenkins и несколько других инструментов для достижения этой цели.

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

Мы продолжим использовать некоторые из инструментов, которые мы использовали раньше.

  • Vagrant с VirtualBox обеспечит простой способ создания и настройки облегченных, воспроизводимых и переносимых виртуальных машин, которые будут выполнять роль наших серверов.
  • Docker предоставит простой способ создания, доставки и запуска распределенных приложений, упакованных в контейнеры.
  • Ansible будет использоваться для настройки серверов и развертывания приложений.
  • Мы будем использовать Jenkins для обнаружения изменений в наших репозиториях кода и запуска заданий, которые будут тестировать, создавать и развертывать приложения.
  • Наконец, nginx будет предоставлять прокси для разных серверов и портов, на которых будут работать наши микро сервисы.

Вдобавок к этому мы увидим некоторые новые, такие как следующие.

  • Docker Compose — удобный инструмент, который позволит нам запускать контейнеры.
  • Docker Swarm превратит пул серверов в один виртуальный хост.
  • Наконец, мы будем использовать Consul для обнаружения и настройки сервиса.

Чтобы следовать этой статье, настройте Vagrant с VirtualBox . После этого установите плагин vagrant-cachier. Хотя это и не обязательно, это ускорит создание и установку виртуальных машин.

1
vagrant plugin install vagrant-cachier

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

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

Мы создадим четыре виртуальные машины. Один (Swarm-Master) будет использоваться для организации развертываний. Его основная функция — выполнять роль главного узла Docker Swarm . Вместо того, чтобы заранее решать, где что-то развертывать, мы сообщим Docker Swarm, что нужно развернуть, и он развернет его на узле, на котором запущено минимальное количество контейнеров. Есть и другие стратегии, которые мы могли бы использовать, но для демонстрации этого по умолчанию должно быть достаточно. Помимо Swarm, мы также настроим Ansible , Jenkins , Consul и Docker Compose на этом же узле. Будут созданы три дополнительные виртуальные машины с именами swarm-node-01 , swarm-node-02 и swarm-node-03 . В отличие от Swarm-Master , в этих узлах будут только агенты Консул и Рой. Их целью является размещение наших услуг (упакованных в контейнеры Docker). Позже, если нам понадобится больше оборудования, мы просто добавим еще один узел и позволим Swarm позаботиться о балансировке развертываний.

Мы начнем с поднятия Vagrant VM. Имейте в виду, что будут созданы четыре виртуальные машины, и для каждой из них требуется 1 ГБ ОЗУ. На 8-битном 64-битном компьютере у вас не должно возникнуть проблем при запуске этих виртуальных машин. Если у вас недостаточно памяти, попробуйте отредактировать Vagrantfile , изменив v.memory = 1024 на меньшее значение.

Весь код находится в репозитории GitHub vfarcic / docker-swarm .

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

Мы можем настроить все серверы, запустив Infra.yml Ansible playbook.

1
ansible-playbook /vagrant/ansible/infra.yml -i /vagrant/ansible/hosts/prod

При первом запуске Ansible на одном сервере вам будет задан вопрос, хотите ли вы продолжить подключение. Ответь да .

Многие вещи будут загружены (контейнер Jenkins является самым большим) и установлены с помощью этой команды, так что будьте готовы немного подождать. Я не буду вдаваться в подробности, касающиеся Ansible. Вы можете найти множество статей об этом как на официальном сайте, так и в других публикациях в этом блоге. Важной деталью является то, что после выполнения книги воспроизведения Ansible Swarm-Master установит Jenkins , Consul , Docker Compose и Docker Swarm . Остальные три узла получили инструкции по установке только агентов Consul и Swarm . Для получения дополнительной информации обратитесь к разделу « Непрерывная интеграция, доставка или развертывание» с Jenkins, Docker и Ansible и другим статьям в разделе «Непрерывная интеграция доставки и развертывания» .

В этой статье мы никогда не будем входить ни на один из серверов узла роя . Все будет сделано из единого места ( Swarm-Master ).

Теперь давайте рассмотрим несколько инструментов, которые мы не использовали ранее в этом блоге; Консул и Докер Рой .

консул

Consul — это инструмент, предназначенный для простого обнаружения и настройки сервисов распределенных и высокодоступных центров обработки данных. Он также имеет простую настройку обнаружения ошибок и хранения ключей / значений.

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

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

1
consul members

На выходе должно быть что-то похожее на следующее.

1
2
3
4
5
Node           Address              Status  Type    Build  Protocol
swarm-master   10.100.199.200:8301  alive   server  0.5.0  2
swarm-node-01  10.100.199.201:8301  alive   client  0.5.0  2
swarm-node-02  10.100.199.202:8301  alive   client  0.5.0  2
swarm-node-03  10.100.199.203:8301  alive   client  0.5.0  2

Благодаря тому, что Консул работает везде, у нас есть возможность хранить информацию о приложениях, которые мы развертываем, и распространять ее на все серверы. Таким образом, приложения хранят данные локально и не должны беспокоиться о местонахождении центрального сервера. В то же время, когда приложение нуждается в информации о других, оно также может запрашивать ее локально. Возможность распространения информации по всем серверам является обязательным требованием для всех распределенных систем.

Другой способ получить ту же информацию — через REST API Консула. Мы можем запустить следующую команду.

1
curl localhost:8500/v1/catalog/nodes | jq .

Это приводит к следующему выводу JSON, отформатированному с помощью jq .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
[
  {
    "Node": "swarm-master",
    "Address": "10.100.199.200"
  },
  {
    "Node": "swarm-node-01",
    "Address": "10.100.199.201"
  },
  {
    "Node": "swarm-node-02",
    "Address": "10.100.199.202"
  },
  {
    "Node": "swarm-node-03",
    "Address": "10.100.199.203"
  }
]

Позже, когда мы развернем первое приложение, мы увидим Консула более подробно. Обратите внимание, что, хотя мы будем использовать Consul, выполняя команды из Shell (по крайней мере, до тех пор, пока не перейдем к разделу здоровья), он имеет пользовательский интерфейс, который можно открыть, открыв http://10.100.199.200:8500 .

Докер Рой

Docker Swarm позволяет нам использовать стандартный Docker API для запуска контейнеров в кластере. самый простой способ его использования — установить переменную окружения DOCKER_HOST. Давайте запустим информацию о команде Docker.

1
2
export DOCKER_HOST=tcp://0.0.0.0:2375
docker info

Вывод должен быть похож на следующее.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
Containers: 9
Strategy: spread
Filters: affinity, health, constraint, port, dependency
Nodes: 3
 swarm-node-01: 10.100.199.201:2375
  └ Containers: 3
  └ Reserved CPUs: 0 / 1
  └ Reserved Memory: 0 B / 1.019 GiB
 swarm-node-02: 10.100.199.202:2375
  └ Containers: 3
  └ Reserved CPUs: 0 / 1
  └ Reserved Memory: 0 B / 1.019 GiB
 swarm-node-03: 10.100.199.203:2375
  └ Containers: 3
  └ Reserved CPUs: 0 / 1
  └ Reserved Memory: 0 B / 1.019 GiB

Мы получаем немедленную информацию о количестве развернутых контейнеров (на данный момент 9), стратегии, которую Swarm использует для их распределения (распространение; работает на сервере с наименьшим количеством запущенных контейнеров), количестве узлов (серверов) и дополнительной информации для каждого из их. На данный момент на каждом сервере развернут один агент Swarm и два регистратора-консула (всего девять). Все эти развертывания были сделаны в рамках игровой книги infra.yml, которую мы запускаем ранее.

развертывание

Давайте развернем первый сервис. Мы будем использовать Ansible playbook, определенный в books-service.yml .

1
ansible-playbook /vagrant/ansible/books-service.yml -i /vagrant/ansible/hosts/prod

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

Книга, которую мы только что запустили, следует той же логике, что мы уже обсуждали в статье о сине-зеленом развертывании . Основное различие заключается в том, что в этот раз нам мало что неизвестно до запуска playbook. Мы не знаем, на какой IP будет развернута служба узла. Поскольку идея этой установки заключается не только в распределении приложений между несколькими узлами, но и в том, чтобы легко их масштабировать, порт также неизвестен. Если бы мы определили это заранее, была бы вероятная опасность, что несколько сервисов будут использовать один и тот же порт и конфликтовать.

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

Книжный сервис состоит из двух контейнеров. Одним из них является само приложение, а другое содержит MongoDB, которое необходимо приложению. Посмотрим, где они развернуты.

1
docker ps | grep booksservice

Результат будет отличаться от случая к случаю, и он будет выглядеть следующим образом. Команда Docker ps выведет больше информации, чем представлено ниже. Те, которые не имеют отношения к этой статье, были удалены.

1
2
vfarcic/books-service:latest  10.100.199.203:32768->8080/tcp   swarm-node-03/booksservice_blue_1  
mongo:latest                  10.100.199.201:32768->27017/tcp  swarm-node-01/booksservice_db_1

Мы видим, что контейнер приложения был развернут на swarm-node-03 и прослушивает порт 32768 . База данных, с другой стороны, перешла на отдельный узел swarm-node-01 и прослушивает порт 32768 . Целью сервиса книг является хранение и извлечение книг из базы данных Mongo.

Давайте проверим, связываются ли эти два контейнера друг с другом. Когда мы запрашиваем данные из контейнера приложения ( booksservice_blue_1 ), он извлекает их из базы данных ( booksservice_db_1 ). Чтобы проверить это, мы попросим сервис вставить несколько книг, а затем попросим его восстановить все записи магазина.

1
2
3
4
curl -H 'Content-Type: application/json' -X PUT -d '{"_id": 1, "title": "My First Book", "author": "Joh Doe", "description": "Not a very good book"}' http://10.100.199.200/api/v1/books | jq .
curl -H 'Content-Type: application/json' -X PUT -d '{"_id": 2, "title": "My Second Book", "author": "John Doe", "description": "Not a bad as the first book"}' http://10.100.199.200/api/v1/books | jq .
curl -H 'Content-Type: application/json' -X PUT -d '{"_id": 3, "title": "My Third Book", "author": "John Doe", "description": "Failed writers club"}' http://10.100.199.200/api/v1/books | jq .
curl http://10.100.199.200/api/v1/books | jq .

Результат последнего запроса следующий.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
[
  {
    "_id": 1,
    "title": "My First Book",
    "author": "John Doe"
  },
  {
    "_id": 2,
    "title": "My Second Book",
    "author": "John Doe"
  },
  {
    "_id": 3,
    "title": "My Third Book",
    "author": "John Doe"
  }
]

Все три книги, которые мы просили, чтобы служба поместила в ее базу данных, были сохранены. Вы могли заметить, что мы не выполняли запросы на IP / порт, на котором запущено приложение. Вместо того, чтобы делать curl против 10.100.199.203:32768 (это где служба в настоящее время работает), мы выполнили запросы к 10.100.199.200 на стандартном HTTP-порту 80 . Именно здесь развернут наш сервер nginx, и благодаря «магии» Consul, Registrator и Templating был обновлен nginx, чтобы он указывал на правильный IP и порт. Подробности того, как это произошло, объясняются в следующей статье . На данный момент важно знать, что данные о нашем приложении хранятся в Консуле и находятся в свободном доступе для всех служб, которые могут в них нуждаться. В этом случае этим сервисом является nginx, который действует как обратный прокси-сервер и балансировщик нагрузки одновременно.

Чтобы доказать это, давайте запустим следующее.

1
curl http://localhost:8500/v1/catalog/service/books-service-blue | jq .

Поскольку мы будем практиковать сине-зеленое развертывание , название службы чередуется между books-service-blue и books-service-green . Это первый раз, когда мы развернули его, поэтому имя синее . Следующее развертывание будет зеленым , затем синим и так далее.

01
02
03
04
05
06
07
08
09
10
11
[
  {
    "Node": "swarm-node-03",
    "Address": "10.100.199.203",
    "ServiceID": "swarm-node-03:booksservice_blue_1:8080",
    "ServiceName": "books-service-blue",
    "ServiceTags": null,
    "ServiceAddress": "",
    "ServicePort": 32768
  }
]

У нас также есть информация, хранящаяся как книжный сервис (общий, без синего или зеленого цвета) с IP и портом, который должен быть доступен для общественности.

1
curl http://localhost:8500/v1/catalog/service/books-service | jq .

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

01
02
03
04
05
06
07
08
09
10
11
12
13
[
  {
    "Node": "swarm-master",
    "Address": "10.100.199.200",
    "ServiceID": "books-service",
    "ServiceName": "books-service",
    "ServiceTags": [
      "service"
    ],
    "ServiceAddress": "10.100.199.200",
    "ServicePort": 80
  }
]

Независимо от того, где мы развертываем наши сервисы, они всегда доступны из одного места 10.100.199.200 (по крайней мере, пока мы не начнем добавлять несколько балансировщиков нагрузки) и всегда доступны через стандартный порт HTTP 80. nginx будет следить за тем, чтобы запросы отправлялись на правильный сервис на правильный IP и порт.

Мы можем развернуть другой сервис, используя тот же принцип. На этот раз это будет фронт-энд для нашего книжного сервиса.

1
ansible-playbook /vagrant/ansible/books-fe.yml -i /vagrant/ansible/hosts/prod

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

1
2
docker ps | grep booksfe
curl http://localhost:8500/v1/catalog/service/books-fe-blue | jq .

Вывод обеих команд должен быть похож на следующее.

01
02
03
04
05
06
07
08
09
10
11
12
13
vfarcic/books-fe:latest         10.100.199.201:32769->8080/tcp    swarm-node-01/booksfe_blue_1
 
[
  {
    "Node": "swarm-node-01",
    "Address": "10.100.199.201",
    "ServiceID": "swarm-node-01:booksfe_blue_1:8080",
    "ServiceName": "books-fe-blue",
    "ServiceTags": null,
    "ServiceAddress": "",
    "ServicePort": 32769
  }
]

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

1
ansible-playbook /vagrant/ansible/books-service.yml -i /vagrant/ansible/hosts/prod

Чтобы убедиться, что все прошло как положено, мы можем запросить Консул.

1
curl http://localhost:8500/v1/catalog/service/books-service-green | jq .

Вывод должен быть похож на следующее.

01
02
03
04
05
06
07
08
09
10
11
[
  {
    "Node": "swarm-node-02",
    "Address": "10.100.199.202",
    "ServiceID": "swarm-node-02:booksservice_green_1:8080",
    "ServiceName": "books-service-green",
    "ServiceTags": null,
    "ServiceAddress": "",
    "ServicePort": 32768
  }
]

В то время как синий выпуск был на IP 10.100.199.203 , на этот раз контейнер был развернут на 10.100.199.202 . Docker Swarm проверил, на каком сервере работает наименьшее количество контейнеров, и решил, что лучше всего его запустить — swarm-node-02 .

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

1
curl http://localhost:8500/v1/kv/services/books-service/color | jq .

Значения в Консуле хранятся в кодировке base64. Чтобы увидеть только значение, выполните следующее.

1
curl http://localhost:8500/v1/kv/services/books-service/color?raw

Вывод команды green .

Дженкинс

Единственное, чего не хватает для полностью реализованного непрерывного развертывания, — это иметь что-то, что будет обнаруживать изменения в нашем хранилище исходного кода, а затем создавать, тестировать и развертывать контейнеры. С Docker очень легко заставить все сборки, тестирование и развертывание следовать одному и тому же стандарту. Для этой статьи я создал только рабочие места, которые выполняют фактическое развертывание. Мы будем использовать их позже в следующей статье, когда будем исследовать способы восстановления после сбоев. А до тех пор вы можете посмотреть на запущенный экземпляр Jenkins, открыв http://10.100.199.200:8080/ .

Продолжение следует

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

Мы подробно рассмотрим, как все это было достигнуто, и покажем ручные команды с помощью Docker Compose, Consul-Template, Registrator и т. Д. Их понимание является необходимым условием для объяснения сборников пьес Ansible, которые мы видели (и запускали) ранее.

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

У вас есть вкус к чему, и теперь пришло время понять, как .

История продолжается в статье « Развертывание служб вручную» .