У тебя на одном сервере крутится compose-стек, и все хорошо. Пока сервер один. А теперь представь: трафик вырос, нужен второй и третий узел, надо чтобы при падении машины контейнеры переехали сами, чтобы релиз катился без даунтайма, чтобы пароль к БД не валялся в открытом виде в переменной окружения. Руками это превращается в ад: ssh на каждую ноду, ручной запуск, ручной перезапуск упавшего. Оркестратор - это робот-диспетчер, который держит в голове желаемое состояние ("хочу 5 реплик этого сервиса на кластере") и сам приводит реальность к нему. Упала нода - он поднимает реплики на живых. Ты сказал "обнови" - он катит новую версию по одной, проверяя, что не сломалось.
docker swarm - это оркестратор, встроенный прямо в Docker Engine. Не отдельный продукт, не лишний бинарник: тот же демон dockerd, который у тебя уже запускает контейнеры, умеет работать в режиме кластера. Это его главная фишка и главная причина, почему он до сих пор жив. Внутри сидит библиотека SwarmKit, и весь твой опыт работы с docker run и compose переносится почти один в один - меняются только глаголы: вместо docker run появляется docker service create, вместо docker compose up - docker stack deploy. В этом уроке разберем docker оркестрация изнутри: как устроен кворум, как пакеты летают между узлами через overlay-сети, как работает балансировка, секреты, обновления и откаты. И честно поговорим, где Swarm сегодня уместен, а где его давно вытеснил Kubernetes.

Архитектура: менеджеры, воркеры и raft
swarm кластер состоит из узлов (nodes) двух ролей. Manager - мозг: хранит состояние кластера, принимает API-команды, планирует, куда ставить контейнеры. Worker - руки: просто запускает то, что ему назначили. Менеджер тоже может быть воркером (по умолчанию он и работу тянет), но в проде менеджеры обычно разгружают.
Состояние кластера (какие сервисы есть, сколько реплик, где они) надо хранить надежно и согласованно на всех менеджерах. Для этого SwarmKit использует алгоритм консенсуса Raft. Если на пальцах: менеджеры голосуют и выбирают одного лидера, который принимает все записи, а остальные реплицируют его лог. Чтобы кластер мог принимать решения, нужен кворум - большинство (N/2 + 1) живых менеджеров. Отсюда железное правило: менеджеров должно быть нечетное число.
- 1 менеджер - кворум 1, переживает 0 падений (тестовый стенд).
- 3 менеджера - кворум 2, переживает падение 1 (минимум для прода).
- 5 менеджеров - кворум 3, переживает падение 2 (крупный кластер).
Поднимаем кластер: init и join
Инициализируем первый менеджер. Если на хосте несколько IP (а в облаке это норма), обязательно указывай --advertise-addr, иначе Swarm может выбрать не тот интерфейс и воркеры не достучатся.
Код: Выделить всё
docker swarm init --advertise-addr 192.168.10.11
Код: Выделить всё
Swarm initialized: current node (k7p2...) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-49nj1...xyz 192.168.10.11:2377
To add a manager to this swarm, run 'docker swarm join-token manager'.
На воркере выполняем ту самую join-команду, затем на менеджере смотрим список:
Код: Выделить всё
docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
k7p2x * ... node-1 Ready Active Leader
m3v9q ... node-2 Ready Active Reachable
a1b8t ... node-3 Ready Active
Порты, которые должен пропускать файрвол между узлами: TCP 2377 (управление), TCP+UDP 7946 (обнаружение узлов, gossip), UDP 4789 (трафик данных overlay, VXLAN). Забыл открыть 4789 - получишь самый коварный баг Swarm: кластер собрался, сервисы стартовали, а контейнеры на разных нодах не видят друг друга.
Сервисы: docker service вместо docker run
Контейнер в Swarm не запускают напрямую. Ты объявляешь сервис - желаемое состояние, а планировщик раскидывает его задачи (tasks) по узлам. Задача - это обертка вокруг одного контейнера; если контейнер умер, Swarm создаст новую задачу, а не "перезапустит" старую.
Код: Выделить всё
docker service create \
--name web \
--replicas 3 \
--publish published=8080,target=80 \
nginx:1.27-alpine
Код: Выделить всё
docker service ps web
ID NAME NODE DESIRED STATE CURRENT STATE
9fb2... web.1 node-1 Running Running 2 minutes ago
3ac7... web.2 node-2 Running Running 2 minutes ago
77de... web.3 node-3 Running Running 2 minutes ago
Код: Выделить всё
docker service scale web=5
Overlay-сети, ingress mesh и балансировка
Самое интересное под капотом docker swarm - сеть. Контейнеры на разных физических узлах должны общаться так, будто они в одной локалке. Это делает overlay-сеть поверх VXLAN: пакет L2 между контейнерами на разных хостах заворачивается (инкапсулируется) в UDP-датаграмму и летит на порт 4789 к нужной ноде, где разворачивается обратно. Снаружи это обычный UDP-трафик между серверами, внутри - изолированная виртуальная сеть твоих сервисов. Связку с физической сетью хоста обеспечивает служебный мост docker_gwbridge, который Swarm создает автоматически.
Код: Выделить всё
docker network create -d overlay --attachable backend
docker service create --name api --network backend ...
Теперь про ingress routing mesh - вторую магию. Когда ты опубликовал порт 8080, он слушается на каждой ноде кластера, даже если реплики сервиса там нет. Прилетел запрос на node-3, а реплики только на node-1 и node-2 - IPVS на node-3 сам перешлет запрос на узел с задачей. Практический смысл: можно повесить внешний L4-балансировщик на любой набор нод и не думать, где именно крутится сервис. Цена - лишний хоп между нодами и потеря реального IP клиента (на L4 он не виден). Если IP клиента критичен (логи, гео, антифрод) - публикуй порт в режиме host (--publish mode=host,...), он минует routing mesh, но тогда сервис слушает только на нодах со своими репликами.
Один нюанс безопасности: трафик данных overlay по умолчанию не шифруется. Если ноды в одной доверенной приватной сети - норм. Если между ними публичный сегмент - включай шифрование при создании сети: docker network create -d overlay --opt encrypted backend. Это поднимет IPSec на VXLAN, заплатив за это процессором.
Стеки: docker stack deploy из compose
docker service create удобен для одного сервиса, но реальное приложение - это пачка сервисов, сетей и томов. Их описывают в знакомом compose-файле и катят как стек. Тот же синтаксис, что и в обычном compose, плюс ключевая секция deploy - именно она оживает только в Swarm (обычный docker compose ее почти всю игнорирует).
Код: Выделить всё
services:
web:
image: nginx:1.27-alpine
ports:
- "8080:80"
networks: [front]
deploy:
replicas: 4
update_config:
parallelism: 1
delay: 10s
order: start-first
rollback_config:
parallelism: 2
restart_policy:
condition: on-failure
placement:
constraints: [node.role == worker]
networks:
front:
driver: overlay
Код: Выделить всё
docker stack deploy -c compose.yaml shop
docker stack services shop
Rolling update и откат
Обновление образа без даунтайма - то, ради чего люди и приходят в оркестрацию.
Код: Выделить всё
docker service update \
--image nginx:1.27.4-alpine \
--update-parallelism 1 \
--update-delay 10s \
--update-order start-first \
web
Код: Выделить всё
docker service rollback web
Секреты и конфиги Swarm
Пароль к БД в переменной окружения - это утечка, ждущая своего часа: его видно в docker inspect, в логах, в истории. Swarm дает secrets - первоклассный механизм. Секрет хранится в зашифрованном Raft-логе, доставляется только на ноды, где реально крутится потребляющая его задача, и монтируется в контейнер как файл в tmpfs (в памяти, /run/secrets/имя), а не в переменную и не на диск.
Код: Выделить всё
printf 'S3cr3t' | docker secret create db_password -
docker service create --name db \
--secret db_password \
-e POSTGRES_PASSWORD_FILE=/run/secrets/db_password \
postgres:16
Грабли и антипаттерны из практики
- Закрытый UDP 4789. Самый частый и самый мутный баг: кластер собран, сервисы Running, а межнодовая связь молчит. Всегда проверяй файрвол на 2377/tcp, 7946/tcp+udp, 4789/udp.
- Четное число менеджеров. 2 или 4 менеджера - худшее из решений: при сплите кластер целиком уходит в read-only. Только нечетное (1, 3, 5).
- Состояние без привязки. Запустил БД с локальным volume без constraint - после переезда задачи на другую ноду данные "пропали" (они остались на старой). Stateful-сервисы прибивай к ноде или выноси на сетевое хранилище.
- Надежда на routing mesh там, где нужен реальный IP клиента. L4-mesh прячет источник. Нужен IP - mode=host или внешний L7-прокси.
- Незашифрованный overlay через публичную сеть. По умолчанию данные идут в открытую. Между датацентрами - только --opt encrypted или VPN.
- latest в образах. В Swarm это вдвойне опасно: разные ноды могут вытянуть разные сборки одного тэга. Только конкретные версии и желательно digest.
Нужны 2-3 машины (или VM/мультипас). Делаем руками:
- На первой: docker swarm init --advertise-addr <IP>. Скопируй join-команду.
- На остальных выполни docker swarm join .... Проверь docker node ls с менеджера - все Ready.
- docker network create -d overlay app, затем docker service create --name web --replicas 3 --network app --publish 8080:80 nginx:1.27-alpine.
- Открой http://<IP-любой-ноды>:8080 - работает с любой, благодаря routing mesh. Останови docker на одной ноде - смотри docker service ps web, как Swarm пересоздает задачи.
- Создай секрет, прокинь в сервис, зайди в контейнер и убедись, что он лежит файлом в /run/secrets, а не в env.
- Обнови образ с --update-parallelism 1 --update-delay 10s и параллельно крути docker service ps web - увидишь rolling update вживую. Затем docker service rollback web.
Честно: Swarm сегодня в режиме поддержки (maintenance mode). Он по-прежнему едет вместе с Docker Engine, получает патчи безопасности, и Mirantis официально пообещала поддержку как минимум до 2030 года. Но активной разработки новых фич почти нет, а экосистема и рынок труда давно ушли в Kubernetes - его выбирает большинство enterprise-нагрузок, и управляемый k8s есть у каждого облака, тогда как управляемого Swarm нет нигде.
Почему так вышло. Swarm выигрывает в одном - простоте: поднять кластер из коробки, без отдельных компонентов, на знакомых командах. Для маленькой команды, одного-двух приложений, домашнего сервера или периметра (edge) на пяток нод это до сих пор разумный и быстрый выбор. Но как только нужны автоскейлинг по метрикам, сложные сетевые политики, операторы, богатый ecosystem (Helm, service mesh, CRD, GitOps), тонкий контроль над планированием - Swarm упирается в потолок, а Kubernetes именно для этого и сделан. K8s сложнее в эксплуатации, зато его расширяемость и сообщество несравнимы. Симптоматично, что сам Kubernetes еще в 2022-м выпилил dockershim и больше не использует Docker как среду исполнения - индустрия стандартизировалась на containerd и OCI, а оркестрацию забрал k8s.
Практический вывод: учить Swarm стоит - он отлично объясняет концепции оркестрации (желаемое состояние, реплики, rolling update, секреты, overlay) на минимуме сложности, и эти идеи один в один переносятся на Kubernetes. Запускать в Swarm новый большой прод в 2026-м - спорно: скорее всего, тебе нужен k8s. А поддерживать уже работающий Swarm-кластер - совершенно нормально, паники "все срочно мигрировать" нет.
Контрольные вопросы
- Сколько менеджеров нужно, чтобы кластер пережил падение двух из них, и почему именно нечетное число?
- Что произойдет с запросом на порт 8080, который прилетел на ноду, где нет ни одной реплики сервиса? Опиши путь пакета.
- Чем secret отличается от обычной переменной окружения с точки зрения хранения и доставки в контейнер?
- В чем разница update-order start-first и stop-first, и когда какой выбрать?
docker swarm - встроенный в движок оркестратор: менеджеры держат состояние через Raft, воркеры крутят задачи, overlay-сети по VXLAN связывают узлы, а ingress mesh и IPVS дают балансировку и публикацию портов на весь кластер. docker service и docker stack заменяют docker run и compose, добавляя реплики, rolling update, откат, секреты и конфиги. В 2026-м Swarm - простой, надежный, но "замороженный" инструмент: идеален, чтобы понять оркестрацию и закрыть небольшие задачи, но для серьезного масштаба индустрия выбрала Kubernetes - и следующие уроки логично ведут именно туда.