[Перевод] Устранение беспорядка маршрутизации сервисов при помощи Docker

Устранение беспорядка маршрутизации сервисов при помощи Docker


«Не трудности «ломают» вас, а то, как вы их переносите» —  Lou Holtz

В соавторстве с Emmet O«Grady (основателем NimbleCI и Docker Ninja)


В книге Франца Кафки «Превращение» («Метаморфозы») человек просыпается однажды утром и обнаруживает, что он превратился в гигантское насекомоподобное существо. Как у инженеров DevOps, у нас есть такие же сюрреалистические моменты в жизни. Мы находим экзотические ошибки «под ковриком» (скрытые в самых труднодоступных местах) или бываем атакованы червями либо другими опасными сущностями. Если вы занимаетесь этим достаточно долго, у вас рано или поздно появится ужасная история, или даже две (поделитесь ими с нами!). В такой момент мы не можем сидеть и ждать, когда наступит кризис, мы должны действовать быстро. Торопясь исправить это как можно раньше, мы должны развернуть (deploy) новую сущность и выпустить новую версию нашего сервиса, устраняя проблему.
Как насчет другого сюрреалистичного момента: ваше приложение получает популярность в средствах массовой информации, многолюдном Slack канале (чате) или вашей внутренней доске объявлений. Пользователи придут быстро и молниеносно на ваш сайт, ваш сервис будет переполнен ими, количество пользователей достигнет лимита и даже выйдет за его пределы. В бешеной гонке, пытаясь устранить ошибку 502, вы должны найти серверы, запустить новые сущности, и переконфигурировать вашу сеть и ваши балансировщики нагрузки. Вы судорожно пытаетесь сложить все кусочки вместе. Вы чувствуете себя, как будто бы вам предстоит бой ложкой против ножа. Мечтательно вы думаете: «Это должно быть автоматизировано», возможно тогда у меня будет больше времени, чтобы расслабиться и почитать классику.


Не испытывайте больше подобных чувств! Docker 1.12 поможет вам. Этот релиз поставляется с набором новых функций, обеспечивающих следующие условия:


  • Быстрое развертывание и откат сервиса
  • Автоматическая балансировка нагрузки
  • Включение масштабирования услуг и инфраструктуры, в которой они работают
  • Восстановление службы или отказавших узлов автоматически

И, когда вы изучите то, что предлагает Docker, он окажется прост в использовании.


Сетка маршрутизации


Docker 1.12 предоставляет полный набор новых определений. Один из них — новая функция, называемая «Сетка маршрутизации». Геометрия компьютерной сети и ее алгоритм маршрутизации не являются чем-то новым, гениальность инженерной команды Docker«а состояла в том, чтобы использовать этот подход для упрощения поставки изменений программного обеспечения и службы обнаружения в Микро-сервисной архитектуре. «Сетка» — это просто новый путь маршрутизации и балансировки траффика в контейнерах Docker«а 1.12. Новая стратегия маршрутизации позволяет сервису достигнуть одинакового порта на всех узлах в Swarm, даже если узел не имеет сервиса, развернутого на нем. Сетка маршрутизации также прозрачно распределяет запросы по всем доступным сервисам в Swarm, определяя сбойные узлы.


Ваша сеть сегодня выглядит также?


Новый подход делает очень простым настройку балансировки нагрузки сервисов: представьте три узла Swarm с семью различными сервисами запущенными в Swarm. Извне мы можем послать запрос на любой узел и он будет передан (маршрутизирован) случайному сервису автоматически, или мы всегда можем послать запрос одному узлу и Docker внутренне распределит баланс между сервисами. Таким образом мы получаем балансирование нагрузки сервисов своими внутренними силами.


Сетка маршрутизации позволяет нам обрабатывать контейнеры по настоящему прозрачно, в том смысле, что мы не волнуемся, как много контейнеров обслуживает наше приложение, кластер обрабатывает всю сеть и производит балансирование нагрузки. Если до этого мы должны были подключить обратный прокси-сервер перед сервисами, который должен был действовать в качестве балансировщика нагрузки, теперь мы можем расслабиться и позволить Docker«у осуществлять балансировку нагрузки за нас. Поскольку Docker выполняет всю грязную работу за нас, различие между ситуациями, когда у нас есть один контейнер и 100 контейнеров теперь стало тривиально маленьким; это просто причина иметь еще компьютерные (вычислительные) ресурсы, чтобы быть в состоянии масштабировать и затем запускать всего одну команду для увеличения масштабирования. Нам больше не нужно думать о архитектурном анализе перед масштабированием, с тех пор как все это выполняет Docker. Когда он начинает масштабирование, мы можем расслабиться, зная, что Docker выполнит его прозрачно, для одного контейнера также как и 100.


Вам может быть интересно, почему мы используем терминологию «сервисы», вместо «контейнеры». Это еще кое-что новое в Docker«е 1.12, давайте посмотрим.


Сервисы и задачи


Docker 1.12 вводит новую абстракцию — «сервисы». Сервисы определяют желаемое состояние контейнеров в кластере. Внутренне ядро использует этот новый концепт, чтобы убедиться, что контейнеры запущены, обрабатывают ошибки и маршрутизируют трафик к контейнерам.


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


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


$ docker service create --name redisdb --replicas=3 redis:alpine

Сетка маршрутизации в действии


Хватит теории, давайте увидим все это в действии. Мы начнем с небольшого nodejs-сервиса, который поможет показать внутреннюю работу сетки маршрутизации. Вы найдете код на GitHub здесь. Давайте рассмотрим самый важный файл webapp.js:


// Пример веб-сервера, который возвращает свою версию, имя хоста, IP и количество посещений
var http = require(‘http’);
var os = require("os”);
var redis = require(‘redis’);

var server = http.createServer(function (request, response) {

// Начинаем создание и ждем ответ "OK”
 response.writeHead(200, {"Content-Type”: "text/plain”});

// а также записываем в log переменные, которые мы будем использовать в нашем ответе
 var version = "2.0”;
 var log = {};
 log.header = ‘webapp’;
 log.name = os.hostname();
 log.version = version;

// Пройдем через сетевые интерфейсы для получения адресов IPv4
 var interfaces = os.networkInterfaces();
 var addresses = [];
 for (var k in interfaces) {
  for (var k2 in interfaces[k]) {
    var address = interfaces[k][k2];
    if (address.family === ‘IPv4’ && !address.internal) {
       addresses.push(address.address);
    }
  }
 }
 log.nics = addresses;

// Постараемся получить наш redis-сервер (redisdb) по ИМЕНИ
 var client = redis.createClient(‘6379’, ‘redisdb’);
 client.on(‘connect’, function(err,reply) {
   if (err) return next(err);
   console.log(‘Connected to Redis server’); 
   client.incr(‘counter’, function(err, reply) {
     if(err) return next(err);
     console.log(‘This page has been viewed ‘ + reply + ‘ times!’);
     console.log(JSON.stringify(log));
     response.end(" Hello, I’m version "+ log.version +”.My hostname is "+ log.name +”, this page has been viewed "+ reply +”\n and my ip addresses are " + log.nics + "\n” );

    }); // client.incr
 }); // client.on

}); // http.createHttpServer

// Давайте подождем http-ответа на порт 8000
server.listen(8000);

Сервис только возвращает имя хоста, IP-адрес контейнера, запустившего его, и счетчик с количеством визитов.


Вот и все наше приложение. Перед тем как погрузиться в него, мы должны быть уверены, что перешли на Docker 1.12


$ docker version
Client:
Version: 1.12.0
API version: 1.24
Go version: go1.6.3
Git commit: 8eab29e
Built: Thu Jul 28 21:04:48 2016
OS/Arch: darwin/amd64
Experimental: true

Server:
Version: 1.12.0
API version: 1.24
Go version: go1.6.3
Git commit: 8eab29e
Built: Thu Jul 28 21:04:48 2016
OS/Arch: linux/amd64
Experimental: true

Мы начнем с создания кластера из 3 узлов, используя Docker-машину:


$ docker-machine create --driver virtualbox swarm-1
Running pre-create checks…
Creating machine…
…
Setting Docker configuration on the remote daemon…
Checking connection to Docker…
Docker is up and running!
…

$ docker-machine create --driver virtualbox swarm-2
…
Docker is up and running!
$ docker-machine create --driver virtualbox swarm-3
…
Docker is up and running!

$ docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
swarm-1 — virtualbox Running tcp://192.168.99.106:2376 v1.12.0
swarm-2 — virtualbox Running tcp://192.168.99.107:2376 v1.12.0
swarm-3 — virtualbox Running tcp://192.168.99.108:2376 v1.12.0

Для ясности мы сократили вывод некоторых команд. Затем, мы настроим наш Swarm. Docker 1.12 позволяет сделать это очень просто:


$ eval $(docker-machine env swarm-1)
$ docker swarm init --advertise-addr $(docker-machine ip swarm-1)
Swarm initialized: current node (bihyblm2kawbzd3keonc3bz0l) is now a manager.

To add a worker to this swarm, run the following command:
docker swarm join \
--token SWMTKN-1–1n7gtfmvvrlwo91pv4r59vsdf73bwuwodj3saq0162vcsny89l-5zige8u81ug5adk3o4bsx32fi \
192.168.99.106:2377
…

Теперь, мы cкопируем и вставим команду, которая была на выходе предыдущей команды для узла «работник», в двух других узлах


$ eval $(docker-machine env swarm-2)
$ docker swarm join \
--token SWMTKN-1–1n7gtfmvvrlwo91pv4r59vsdf73bwuwodj3saq0162vcsny89l-5zige8u81ug5adk3o4bsx32fi \
192.168.99.106:2377
This node joined a swarm as a worker.

$ eval $(docker-machine env swarm-3)
$ docker swarm join \
--token SWMTKN-1–1n7gtfmvvrlwo91pv4r59vsdf73bwuwodj3saq0162vcsny89l-5zige8u81ug5adk3o4bsx32fi \
192.168.99.106:2377
…
This node joined a swarm as a worker.

Обратите внимание: когда вы инициализируете новый Swarm, команда выведет две новые команды, которые вы можете использовать, чтобы присоединить другие узлы к Swarm. Не беспокойтесь о «потере» этих команд; вы можете получить снова их в любое время, запустив «docker swarm join-token worker|manager».


Перед тем, как продолжить, давайте убедимся в том, что наш swarm-кластер функционирует. Укажем наш Docker-клиент для управляющего узла и проверим его статус:


$ eval $(docker-machine env swarm-1)
$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
5jqsuf8hsemi7bfbwzfdxfcg5 swarm-2 Ready Active
93f0ghkpsh5ru7newtqm82330 swarm-3 Ready Active
b2womami9n2t8tti1acvz6v5j * swarm-1 Ready Active Leader

$ docker info
…
Swarm: active
NodeID: b2womami9n2t8tti1acvz6v5j
Is Manager: true
ClusterID: bimg9n2ti2tnsprkdjqg07tdm
Managers: 1
Nodes: 3
Orchestration:
Task History Retention Limit: 5
Raft:
Snapshot interval: 10000
Heartbeat tick: 1
Election tick: 3
Dispatcher:
Heartbeat period: 5 seconds
CA configuration:
Expiry duration: 3 months
Node Address: 192.168.99.106

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


$ docker network create --driver overlay webnet
a9j4al25hzhk2m7rdfv5bqt54

Таким образом, теперь мы готовы запустить новый сервис для Swarm. Мы начнем с запуска базы данных redis и контейнера «lherrera/webapp», который сохраняет страницы посещения в redis и выводит другие интересные детали, такие как имя хоста и IP-адрес контейнера.


Когда мы разворачиваем наше веб-приложение, для него становится возможным соединиться с базой данных redis используя сервис Redis «redisdb». Нам не нужно использовать IP-адреса, потому что Swarm имеет внутренний DNS, который автоматически присваивает каждой службе DNS-запись. Все очень проcто!


Мы можем только развернуть сервисы из управляющих узлов. Пока ваш клиент Docker«а все еще указывает на управляющий узел, мы можем всего лишь напечатать в командной строке «docker service create»:


$ docker service create --name webapp --replicas=3 --network webnet --publish 80:8000 lherrera/webapp:1.0
avq41soybmtw2jamdus0m3pg

$ docker service create --name redisdb --network webnet --replicas 1 redis:alpine
bmotumdr8bcewcfj6zqkws97x

$ docker service ls 
ID            NAME     REPLICAS  IMAGE                COMMAND
avq41soybmtw  webapp   3/3       lherrera/webapp:1.0
bmotumdr8bce  redisdb  1/1       redis:alpine

В команде создания сервиса нам необходимо указать по меньшей мере образ (в нашем случае lherrera/webapp). По соглашению, мы также указываем и имя webapp. Мы должны также задать команду, для того чтобы осуществить запуск внутри контейнеров только после (подключения) образа. В предыдущей команде мы также указываем, что мы хотим три реплики контейнера, запущенного в любой текущий момент времени. Использование флага » — replicas» подразумевает, что мы не должны беспокоиться, на какой узел он попадет, если мы хотим один сервис на узел, мы можем использовать вместо этого команду » — mode=global».


Вы можете увидеть, что мы используем сеть «webnet», которую мы создали ранее. Чтобы можно было создать наш сервис извне Swarm, нам нужно задать порт, по которому Swarm прослушивает веб-запросы. В нашем примере, мы «задали» порт 8000 внутри сервиса на порт 80 на всех узлах. Благодаря решетке маршрутизации он будет резервировать порт 80 на всех узлах маршрутизации и Docker Engine будет балансировать трафик между портом 8000 во всех репликах контейнера.


Swarm будет использовать описание этого сервиса как желаемое состояние для вашего приложения и начнут развертывание приложения на узлах для достижения и поддержания этого желаемого состояния. Мы могли бы указать дополнительные параметры сервиса, чтобы завершить наше описание, такие как правила перезагрузки, лимиты памяти и ресурсов центрального процессора, контейнеры узлов и т.д. Для полного списка всех доступных флагов вы можете использовать команду «docker service create» (смотрите здесь).


На этом этапе, мы можем быть уверены, что наше веб-приложение корректно соединилось с webnet сетью


$ docker network ls
NETWORK ID NAME DRIVER SCOPE
df19a5c87d90 bridge bridge local
7c5762c8c6ff docker_gwbridge bridge local
eb0bd5a4920b host host local
bqqh4te5f5tf ingress overlay swarm
3e06a1616b7b none null local
a9j4al25hzhk webnet overlay swarm

$ docker service inspect webapp
…
"VirtualIPs”: [
 {
 "NetworkID”: "7i9t9uhtr7x0hybsvnsheh92u”,
 "Addr”: "10.255.0.6/16”
 },
 {
 "NetworkID”: "a9j4al25hzhk2m7rdfv5bqt54”,
 "Addr”: "10.0.0.4/24”
 }
 ]
 },
…

Теперь давайте вернемся к нашему примеру и убедимся, что наш сервис запущен и работает:


$ docker service ps webapp
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR
bb8n4iagxcjv4rn2oc3dj6cvy webapp.1 lherrera/webapp:1.0 swarm-1 Running Preparing about a minute ago
chndeorlvglj45alr4vhy50vg webapp.2 lherrera/webapp:1.0 swarm-3 Running Preparing about a minute ago
4txay0gj6p18iuiknacdoa91x webapp.3 lherrera/webapp:1.0 swarm-2 Running Preparing about a minute ago

Один вопрос, в курсе которого надо быть: хотя Docker-сервис создает команду, которая быстро возвращается с id, фактическое масштабирование сервиса может занять некоторое время, особенно, если образы должны находиться на узлах. В таком случае всего лишь запустите команду «docker service ps webapp» несколько раз, пока все реплики не будут предоставлены сервису.


Когда сервисы запущены (это может занять несколько минут, запаситесь кофе на это время), мы можем проверить, что наши сервисы получают в ответ на запросы.
Первоначально, мы должны получить IP первого узла:


$ NODE1=$(docker-machine ip swarm-1)

Тогда мы установим порт 80 для того IP-адреса, потому что мы опубликовали сервис на порт 80 в Swarm. Мы используем curl в примере ниже, но вы также можете задать IP в вашем браузере, чтобы получить тот же результат.


c8b37f48598440cf9a4f788eddf9706d.png


Теперь, мы готовы протестировать наш новый сервис:


$ curl $NODE1
Hello, I’m version 1.0.My hostname is 5a557d3ed474, this page has been viewed 1
and my ip addresses are 10.255.0.9,10.255.0.6,172.18.0.3,10.0.0.7,10.0.0.4

$ curl $NODE1
Hello, I’m version 1.0.My hostname is 4e128c8af4ae, this page has been viewed 2
and my ip addresses are 10.255.0.7,10.255.0.6,172.18.0.3,10.0.0.5,10.0.0.4

$ curl $NODE1
Hello, I’m version 1.0.My hostname is eaa73f01996c, this page has been viewed 3
and my ip addresses are 10.255.0.8,10.255.0.6,172.18.0.4,10.0.0.6,10.0.0.4

Мы можем увидеть, что первый запрос был направлен к контейнеру »5a557d3ed474». Если вы нажмете «обновить» несколько раз или вызовете «curl» повторно из командной строки, вы увидите запрос, который был послан на все три реплики контейнеров, которые мы создали.


Для демонстрации других аспектов сетки маршрутизации, попробуйте «назначить» порт 80 для IP двух других узлов Docker«а. Вы увидите то же самое, что раньше, из этого следует, что не имеет значения, какой узел вы запросили; над запросом будет всегда осуществлена внутренняя балансировка нагрузки между тремя контейнерами.


Плавающие обновления (Роллинг-обновления)


В качестве части описания сервиса, вы можете задать стратегию обновления вашего сервиса. Например, вы можете использовать флаг  — update-delay, чтобы сконфигурировать задержку между обновлениями для задачи сервиса (контейнера) или набора задач. Или вы можете использовать флаг  — update-parallelism, чтобы изменить поведение по умолчанию: обновление одного контейнера за раз. Вы можете установить все эти флаги в процессе создания сервиса или позже, используя команду «docker service update», как мы увидим в следующем примере. Команда обновления требует почти одинаковые параметры с командой «docker service create», но некоторые флаги имеют разные имена с командой обновления. Все, что вы можете установить в команде создания (create), вы можете изменить в команде обновления (update), таким образом, вы имеете полную свободу изменять любую часть описания сервиса в любое время.


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


Давайте обновим наш сервис webapp, чтобы увидеть, как работает механизм «rolling update». Команда разработчиков усердно трудилась, чтобы улучшить webapp и мы выпустили новый тег под названием »2.0». Сейчас мы обновим наш новый сервис этим новым образом:


$ docker service update --update-parallelism 1 --update-delay 5s --image lherrera/webapp:2.0 webapp

Обратите внимание, мы также указали, что мы хотим, чтобы обновление происходило одно за раз и задали интервал в 5 секунд между каждым обновлением. Данный подход означает, что у нас не будет никаких простоев!


Пока происходят обновления, посмотрите их в реальном времени, запуская «docker service ps webapp» каждые несколько секунд. Вы увидите старые образы, выключаемые один за одним, и заменяемые новыми:


$ docker service ps webapp
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR
7bdpfbbjok78zt32xd9e2mqlt webapp.1 lherrera/webapp:2.0 swarm-3 Running Running 43 seconds ago
bb8n4iagxcjv4rn2oc3dj6cvy \_ webapp.1 lherrera/webapp:1.0 swarm-1 
Shutdown Shutdown 44 seconds ago
af2rlu0laqhrj3uz8brym0f8r webapp.2 lherrera/webapp:2.0 swarm-2 Running Running 30 seconds ago
chndeorlvglj45alr4vhy50vg \_ webapp.2 lherrera/webapp:1.0 swarm-3
Shutdown Shutdown 30 seconds ago
0xjt6gib1hql10iqj6866q4pe webapp.3 lherrera/webapp:2.0 swarm-1 Running Running 57 seconds ago
4txay0gj6p18iuiknacdoa91x \_ webapp.3 lherrera/webapp:1.0 swarm-2
Shutdown Shutdown 57 seconds ago

Это просто что-то невероятное!


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


$ curl $NODE1
Hello, I’m version 1.0.My hostname is 5a557d3ed474, this page has been viewed 4
and my ip addresses are 10.255.0.9,10.255.0.6,172.18.0.3,10.0.0.7,10.0.0.4

$ curl $NODE1
Hello, I’m version 2.0.My hostname is e0899324d9df, this page has been viewed 5
and my ip addresses are 10.255.0.10,10.255.0.6,172.18.0.4,10.0.0.8,10.0.0.4

$ curl $NODE1
...

Обратите внимание, что Redis считает продолжения неэффективными (граф Рэдис-сервиса неизменен), так как мы не изменили сервис Redis, только образ webapp.


Решение проблемы роста


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


Давайте объединимся в рой...


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


$ docker-machine create -d virtualbox swarm-4
…
Docker is up and running!

Join the node the swarm as a worker as before:

$ eval $(docker-machine env swarm-4)
$ docker swarm join \
--token SWMTKN-1–1n7gtfmvvrlwo91pv4r59vsdf73bwuwodj3saq0162vcsny89l-5zige8u81ug5adk3o4bsx32fi \
192.168.99.106:2377
This node joined a swarm as a worker.

Проверим, что и новый узел и webapp-сервис запущены:


$ eval $(docker-machine env swarm-1)

$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
93f0ghkpsh5ru7newtqm82330 swarm-3 Ready Active
e9rxhj0w1ga3gz89g927qvntd swarm-4 Ready Active
5jqsuf8hsemi7bfbwzfdxfcg5 swarm-2 Ready Active
b2womami9n2t8tti1acvz6v5j * swarm-1 Ready Active Leader

$ docker service ps webapp
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR
7bdpfbbjok78zt32xd9e2mqlt webapp.1 lherrera/webapp:2.0 swarm-3 Running Running 5 minutes ago
bb8n4iagxcjv4rn2oc3dj6cvy \_ webapp.1 lherrera/webapp:1.0 swarm-1 Shutdown Shutdown 5 minutes ago
af2rlu0laqhrj3uz8brym0f8r webapp.2 lherrera/webapp:2.0 swarm-2 Running Running 5 minutes ago
chndeorlvglj45alr4vhy50vg \_ webapp.2 lherrera/webapp:1.0 swarm-3 Shutdown Shutdown 5 minutes ago
0xjt6gib1hql10iqj6866q4pe webapp.3 lherrera/webapp:2.0 swarm-1 Running Running 6 minutes ago
4txay0gj6p18iuiknacdoa91x \_ webapp.3 lherrera/webapp:1.0 swarm-2 Shutdown Shutdown 6 minutes ago
luis@aurelio [13:14:27] [~/webapp] [2.0]

Теперь давайте активизируем наш webapp-сервис для четырех реплик:


$ docker service update --replicas=4 webapp

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


$ docker service ps webapp
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR
7bdpfbbjok78zt32xd9e2mqlt webapp.1 lherrera/webapp:2.0 swarm-3 Running Running 10 minutes ago
bb8n4iagxcjv4rn2oc3dj6cvy \_ webapp.1 lherrera/webapp:1.0 swarm-1 Shutdown Shutdown 10 minutes ago
af2rlu0laqhrj3uz8brym0f8r webapp.2 lherrera/webapp:2.0 swarm-2 Running Running 10 minutes ago
chndeorlvglj45alr4vhy50vg \_ webapp.2 lherrera/webapp:1.0 swarm-3 Shutdown Shutdown 10 minutes ago
0xjt6gib1hql10iqj6866q4pe webapp.3 lherrera/webapp:2.0 swarm-1 Running Running 10 minutes ago
4txay0gj6p18iuiknacdoa91x \_ webapp.3 lherrera/webapp:1.0 swarm-2 Shutdown Shutdown 10 minutes ago
81xbk0j61tqg76wcdi35k1bxv webapp.4 lherrera/webapp:2.0 swarm-4 Running Running 20 seconds ago

Просто, да? Пчелы в рое всегда в хорошем настроении!


Для глобальных сервисов (в отличие от репликации, как мы видели в примере), Swarm запускает новую задачу автоматически для сервиса на новом доступном узле.


Устойчивость маршрутизации


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


Маршрутизация сети в традиционном отказоустойчивом окружении (HA environment)


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


Давайте убедимся, что наше webapp все еще запущено на нашем кластере:


$ docker service ps webapp
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR
7bdpfbbjok78zt32xd9e2mqlt webapp.1 lherrera/webapp:2.0 swarm-3 Running Running 10 minutes ago
bb8n4iagxcjv4rn2oc3dj6cvy \_ webapp.1 lherrera/webapp:1.0 swarm-1 Shutdown Shutdown 10 minutes ago
af2rlu0laqhrj3uz8brym0f8r webapp.2 lherrera/webapp:2.0 swarm-2 Running Running 10 minutes ago
chndeorlvglj45alr4vhy50vg \_ webapp.2 lherrera/webapp:1.0 swarm-3 Shutdown Shutdown 10 minutes ago
0xjt6gib1hql10iqj6866q4pe webapp.3 lherrera/webapp:2.0 swarm-1 Running Running 10 minutes ago
4txay0gj6p18iuiknacdoa91x \_ webapp.3 lherrera/webapp:1.0 swarm-2 Shutdown Shutdown 10 minutes ago
81xbk0j61tqg76wcdi35k1bxv webapp.4 lherrera/webapp:2.0 swarm-4 Running Running 20 seconds ago

Узлы:


$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
21z5m0vik76msi90icrf8prvf swarm-3 Ready Active
7zyckxuwsruehcfosgymwiucm swarm-4 Ready Active
9aso727d8c4vc59cxu0e8g778 swarm-2 Ready Active
bihyblm2kawbzd3keonc3bz0l * swarm-1 Ready Active Leader

Здесь начинается самое весёлое — кто не любит отключать работающие узлы, чтобы посмотреть, что случиться?


$ docker-machine stop swarm-2

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


$ docker service ps webapp
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR
7bdpfbbjok78zt32xd9e2mqlt webapp.1 lherrera/webapp:2.0 swarm-3 Running Running 11 minutes ago
bb8n4iagxcjv4rn2oc3dj6cvy \_ webapp.1 lherrera/webapp:1.0 swarm-1 Shutdown Shutdown 11 minutes ago
af2rlu0laqhrj3uz8brym0f8r webapp.2 lherrera/webapp:2.0 swarm-4 Running Running 11 minutes ago
chndeorlvglj45alr4vhy50vg \_ webapp.2 lherrera/webapp:1.0 swarm-3 Shutdown Shutdown 11 minutes ago
0xjt6gib1hql10iqj6866q4pe webapp.3 lherrera/webapp:2.0 swarm-1 Running Running 11 minutes ago
4txay0gj6p18iuiknacdoa91x \_ webapp.3 lherrera/webapp:1.0 swarm-4 Shutdown Shutdown 11 minutes ago
15pxa5ccp9fqfbhdh76q78aza webapp.4 lherrera/webapp:2.0 swarm-3 Running Running 4 seconds ago
81xbk0j61tqg76wcdi35k1bxv \_ webapp.4 lherrera/webapp:2.0 swarm-2 Shutdown Running about a minute ago

Здорово! Docker развернул новый контейнер на узле swarm-3, после того, как мы «убили» узел swarm-2. Как вы можете видеть, мы все еще имеем три запущенные реплики (два в узле «swarm-3»)…


$ curl $NODE1
Hello, I’m version 2.0.My hostname is 0d49c828b675, this page has been viewed 7
and my ip addresses are 10.0.0.7,10.0.0.4,172.18.0.4,10.255.0.10,10.255.0.6

$ curl $NODE1
Hello, I’m version 2.0.My hostname is d60e1881ac86, this page has been viewed 8
and my ip addresses are 10.0.0.7,10.0.0.4,172.18.0.4,10.255.0.10,10.255.0.6

И трафик автоматически перенаправляется на вновь созданный контейнер!


Рост и развитие Swarm


Программное обеспечение меняется быстро. Отказы случаются, и они неизбежно происходят, когда меньше всего ожидаешь. Новая абстракция «сервисы» была введена в Docker 1.12, упрощая обновления и предотвращая простои и отказы сервиса. К тому же, этот новый подход делает планирование и обслуживание долго работающих сервисов, таких как веб сервера, намного проще. Вы можете теперь развернуть реплицируемый, распределенный, сбалансированный по нагрузке сервис в Swarm на Docker Engine с помощью нескольких команд. Кластер может управлять состоянием ваших сервисов и даже приводит сервис в желаемое состояние в случае если узел или контейнер отказал.


Solomon Hykes хорошо описал, как мы надеемся сделать жизнь разработчиков проще: «С развитием в нашем оркестрационном (orchestration) инструментарии, сетях и безопасности, Docker подключает разработчиков, чтобы построить более комплексные приложения, которые могут быть развиты в масштабе от рабочего компьютера до облака, независимо от базовой инфраструктуры.»


С релизом Docker 1.12 мы думаем, он, безусловно, обеспечит свое обещание. Docker делает вещи более простыми, надеюсь, это тот момент, когда эти сюрреалистические моменты, что мы упомянули в начале, начнут пропадать. У вас есть какие-либо еще сюрреалистические моменты в DevOps? Поделитесь этим с нами!


Больше информации и ссылок на Docker 1.12


  • https://www.youtube.com/watch? v=dooPhkXT9yI — отличное глубокое рассмотрение специфики, как узлы Swarm«а работают и обмениваются информацией между собой.
  • https://www.youtube.com/watch? v=_F6PSP-qhdA — часть 2 глубокого рассмотрения, сфокусированная на том, как оркестрируются (orchestrated) сервисы и как обрабатываются отказы.

Комментарии (3)

  • 28 сентября 2016 в 12:36

    +1

    Интересная статья! С большим удовольствием читаю статьи от вашей команды! Причем всегда очень радует, что темы довольно не тривиальные и основанные на личном опыте, а не «гдетокогдатоктото». Хочется сказать спасибо, так держать!
    • 28 сентября 2016 в 12:37

      0

      Спасибо!
  • 28 сентября 2016 в 13:10

    0

    Не подскажите, а как быть с оверхедом? Overlay-network, который дает docker-swarm режет канал сети больше, чем в двое. С сеткой маршрутизации таких проблем не будет?

© Habrahabr.ru