ADG Оставить заявку
Блог DevOps 5 мин чтения

Docker Swarm mode в продакшне: три менеджера, шесть воркеров, rolling update

Docker 1.12 встроил Swarm прямо в движок. Поднимаем первый production-кластер: три менеджера, шесть воркеров, rolling update без downtime - для заказчика без Kubernetes.

Контекст момента

Docker 1.12 встроил Swarm-режим прямо в движок, убрав необходимость в отдельном Swarm-контейнере для кластеризации

Docker 1.12 вышел в конце июля с одной главной новостью: Swarm-режим теперь встроен прямо в движок. Никакого отдельного контейнера swarm:latest, никакого внешнего сервиса обнаружения - просто docker swarm init и кластер поднят. Мы опробовали это на реальном проекте, и вот что из этого вышло.

Контекст

У нас есть заказчик - ритейл, несколько сервисов на Docker Compose, три хоста в боевой среде. Масштабирование пока ручное: ставишь новый хост, копируешь compose-файл, запускаешь. Отказоустойчивости нет. Когда мы в очередной раз подняли тему Kubernetes, получили стандартный ответ: «Это сложно, дорого, и вообще у нас не гугл». Аргумент, честно говоря, не лишённый смысла.

Docker Swarm mode в 1.12 выглядит как разумный ответ на этот запрос. Не Kubernetes по возможностям, но и задача другая: взять то, что уже работает на Docker, и добавить кластеризацию с минимальным порогом входа.

Архитектура кластера

Девять нод: три менеджера, шесть воркеров. Менеджеры держат Raft-консенсус - это встроено в 1.12, никакого Consul или etcd не нужно. Кворум при трёх менеджерах переживает падение одного; этого достаточно для нашего сценария.

Топология простая:

  • Менеджеры - три ноды, только управление, сами нагрузку не несут (ограничено через --availability drain).
  • Воркеры - шесть нод, на них крутятся сервисы. Одинаковые по железу, без хитрых labels пока.

docker swarm init на первом менеджере выдаёт токен. Два docker swarm join - и у тебя три менеджера в кластере. docker swarm join с воркерным токеном на оставшихся шести нодах. Весь процесс занимает минуты, а не часы.

Сервисы вместо контейнеров

Принципиальное отличие от Compose - ты больше не запускаешь контейнер, ты объявляешь сервис:

docker service create \
  --name api \
  --replicas 3 \
  --update-delay 10s \
  --update-parallelism 1 \
  -p 8080:8080 \
  registry.example.com/api:1.4.2

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

--update-delay и --update-parallelism - это и есть rolling update. При docker service update --image registry.example.com/api:1.4.3 api Swarm обновляет по одной реплике с паузой в 10 секунд между ними. Остальные реплики продолжают обслуживать трафик. Даунтайм - ноль, если приложение нормально стартует.

Routing mesh - неожиданно приятная штука

Встроенный ingress-лоад-балансер работает так: если у сервиса опубликован порт 8080, он доступен на порту 8080 на ЛЮБОЙ ноде кластера - независимо от того, есть ли на этой ноде реплика сервиса. Swarm сам перенаправит запрос на живую реплику.

Это означает что перед кластером достаточно поставить простой L4-балансер (или DNS round robin), который смотрит на все девять нод. Без нужды знать где именно живут реплики. Мы поставили Nginx снаружи - он балансирует на три случайных воркера, а дальше routing mesh делает своё дело.

Что реально потребовало времени

Первое - реестр образов. Swarm тащит образы на ноды самостоятельно, значит нужен приватный registry доступный со всех нод. Мы развернули registry:2 отдельно, настроили TLS и базовую аутентификацию. Час работы, но без него не взлетит ничего.

Второе - healthcheck в образах. Rolling update использует healthcheck чтобы решить готова ли новая реплика прежде чем убить старую. Если healthcheck не объявлен в Dockerfile - Swarm считает контейнер здоровым сразу после старта, и это неправда для приложений с длинной инициализацией. Пришлось добавить HEALTHCHECK в несколько образов.

Третье - конфиги и секреты. Пока официального механизма secrets в Swarm нет, мы вынесли чувствительные данные в отдельный config-репозиторий и монтируем их через bind-mount с ограниченными правами. Это лучше чем переменные окружения в compose-файле, который случайно улетает в git, но не идеально - ждём нормального решения от Docker.

Ограничения, с которыми живём

Volumes в Swarm - это боль. Локальный volume существует только на той ноде где запустился контейнер. При перезапуске на другой ноде - данных нет. Для stateless-сервисов это не проблема, для stateful - нужно или ограничивать запуск на конкретную ноду (--constraint), или выносить хранение наружу. Мы вынесли: базы живут за пределами кластера, в Swarm только stateless-сервисы.

Logs - пока без централизации на уровне Swarm. В 1.12 нет агрегации логов со всех реплик из коробки - смотришь логи на каждой ноде отдельно через docker logs. Гоним через Fluentd в сторону ELK, как и раньше.

Где сейчас

Кластер работает вторую неделю, прошло два плановых обновления сервисов - оба без downtime. Клиент доволен: он видит понятный инструмент, команды похожи на то что он уже знает по Docker, и никто не произносит слово «Kubernetes» с ударением на непроизносимые слоги.

Для нас это рабочий вариант кластеризации для проектов где managed-инфраструктура нужна без полного перехода на оркестрацию класса enterprise. Docker 1.12 Swarm mode - не замена Kubernetes для сложных сценариев, но вполне самостоятельное решение для среднего по масштабу продакшна.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.