Содержание
Технология контейнеризации с помощью Docker стала настоящей революцией в мире разработки и эксплуатации программного обеспечения. Она позволила решить одну из самых острых проблем современных инфраструктур — переносимость приложений. Однако, когда речь заходит о десятках, сотнях или даже тысячах контейнеров, ручное управление превращается в кошмар: масштабирование требует сложных скриптов, сбой одного узла может остановить целый сервис, а деплой новых версий становится источником простоев и нестабильности. Именно здесь на сцену выходит оркестрация — автоматизированный процесс координации жизненного цикла контейнеров, их распределения, балансировки, восстановления и обновления.
Среди множества решений для оркестрации контейнеров особое место занимает Docker Swarm Mode. Встроенный непосредственно в Docker Engine начиная с версии 1.12, он не требует установки сторонних компонентов, таких как etcd или Consul, и управляется уже привычными командами вроде docker service. Это делает Swarm идеальным выбором для команд, которые хотят быстро перейти от локального запуска контейнеров к полноценной распределённой инфраструктуре.
Swarm особенно подходит для небольших и средних кластеров — вплоть до 50 узлов — где доминируют stateless-приложения: веб-серверы, REST API, микросервисы, фоновые обработчики. В то время как Kubernetes предлагает гибкость и мощность для сложных сценариев (например, Canary-деплои, сложные сетевые политики или кластеры свыше 100 узлов), Swarm остаётся оптимальным решением для тех, кто ценит простоту, скорость настройки и минимальные накладные расходы на обслуживание.
В этой статье мы детально разберём, как построить полноценный кластер Docker Swarm на свежей операционной системе Ubuntu 24.04 Noble Numbat, развернуть в нём веб-сервер и API-сервис, настроить безопасную передачу конфигураций и секретов, а также продемонстрируем взаимодействие между компонентами. Погружайтесь — вас ждёт практическое путешествие в мир распределённых систем.
Подготовка инфраструктуры: основа надёжного кластера
Прежде чем приступить к инициализации кластера, необходимо тщательно подготовить каждую ноду — будь то будущий менеджер или воркер. Docker Swarm опирается на чёткое разделение ролей и строгие требования к сетевой и системной конфигурации.
В архитектуре Swarm различают два типа узлов:
- Менеджеры (managers) — управляющие ноды, отвечающие за принятие решений, хранение состояния кластера и обработку команд от администратора. Они используют алгоритм Raft Consensus для обеспечения согласованности данных даже при отказе части узлов. Для отказоустойчивости рекомендуется использовать нечётное количество менеджеров — минимум 3, чтобы кластер оставался работоспособным при потере одной ноды.
- Воркеры (workers) — исполнительные узлы, на которых фактически запускаются контейнеры. Они получают задачи от менеджеров и не участвуют в управлении состоянием кластера.
Важно понимать: любой менеджер по умолчанию также является воркером. Это означает, что на нём могут запускаться задачи, если только вы явно не переведёте его в режим «только управление» с помощью команды docker node update --availability drain.
Сетевые требования кластера Swarm строги и обязательны. Ниже перечислены ключевые порты, которые должны быть открыты во внутренней сети между всеми узлами:
- 2377/tcp — для присоединения новых узлов и управления кластером (только между менеджерами и для команды
join). - 7946/tcp и 7946/udp — для обнаружения узлов (node discovery) и распределённого управления overlay-сетями.
- 4789/udp — для VXLAN-туннелей, через которые транспортируется трафик в overlay-сетях между контейнерами на разных нодах.
Если используется файрвол (например, ufw или nftables), убедитесь, что эти порты открыты. В продакшене также рекомендуется ограничить доступ к порту 2377 только доверенными IP-адресами, чтобы предотвратить несанкционированное присоединение узлов.
Теперь перейдём к подготовке серверов. На каждом узле кластера (и менеджерах, и воркерах) выполните следующие шаги:
sudo apt update && sudo apt upgrade -y
Эта команда обновляет индекс пакетов и устанавливает последние обновления безопасности и функциональности. Затем установите Docker Engine из официального репозитория Ubuntu:
sudo apt install -y docker.io
На момент выхода Ubuntu 24.04 пакет docker.io предоставляет версию Docker Engine 26.1.3 или выше — этого достаточно для работы Swarm.
Разрешите автозапуск Docker при загрузке системы и запустите службу:
sudo systemctl enable --now docker
Проверьте корректность установки, запустив тестовый контейнер:
sudo docker run --rm hello-world
Если вы видите сообщение:
Hello from Docker! This message shows that your installation appears to be working correctly.
— значит, всё в порядке.
Рекомендации для production-сред:
- Аппаратные требования: минимум 2 CPU ядра и 2 ГБ оперативной памяти на узел. Для менеджеров рекомендуется добавить ещё 1 ГБ RAM для стабильной работы Raft-лога.
- Сетевые адреса: используйте статические IP-адреса на всех нодах. Это предотвратит сбои при перезапуске DHCP-сервера.
- Swap: отключите подкачку памяти с помощью команды
sudo swapoff -aи закомментируйте соответствующую строку в/etc/fstab. Это улучшает предсказуемость производительности контейнеров. - Синхронизация времени: установите и запустите NTP-клиент, например,
chronyилиsystemd-timesyncd, чтобы избежать проблем с TLS-сертификатами и логами.
Перед созданием кластера убедитесь, что на всех узлах:
- установлена одинаковая версия Docker (проверяется командой
sudo docker version); - открыты необходимые порты;
- время синхронизировано;
- сетевая связность между узлами подтверждена (например, с помощью
pingилиtelnet).
Инициализация первого менеджера: рождение кластера
Теперь пришло время дать жизнь нашему кластеру. Выберите один из серверов — он станет лидирующим менеджером (Leader). На нём выполните команду инициализации:
sudo docker swarm init --advertise-addr 192.168.1.100
Замените 192.168.1.100 на реальный внутренний IP-адрес этого сервера. Параметр --advertise-addr критически важен: он указывает, по какому адресу другие узлы будут подключаться к этому менеджеру. Без него Docker может выбрать неправильный интерфейс (например, loopback или публичный IP), что приведёт к ошибкам связи.
После выполнения команды вы увидите примерно такой вывод:
Swarm initialized: current node (d4n5g...) is now a manager. To add a worker to this swarm, run the following command: docker swarm join --token SWMTKN-1-59fl8... 192.168.1.100:2377
Это означает, что кластер создан, и текущая нода теперь является менеджером. Обратите внимание на токен присоединения — это уникальная строка, необходимая для аутентификации новых узлов.
Присоединение воркеров: расширение вычислительной мощности
На каждой ноде, которую вы хотите сделать воркером, выполните команду, полученную на предыдущем шаге:
sudo docker swarm join --token SWMTKN-1-59fl8... 192.168.1.100:2377
Токен SWMTKN-1-... действителен только для роли воркера. Он гарантирует, что присоединяющийся узел прошёл аутентификацию и авторизацию на уровне кластера. Все коммуникации между узлами защищены TLS-шифрованием, сгенерированным автоматически при инициализации кластера.
Если вы потеряли токен, его всегда можно восстановить на любом менеджере командой:
sudo docker swarm join-token worker
Эта команда покажет актуальную команду для присоединения воркера, включая полный токен и адрес лидера.
Диагностика состояния: проверка работоспособности кластера
Чтобы убедиться, что кластер функционирует корректно, выполните на менеджере:
sudo docker node ls
Вы получите список всех узлов:
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION d4n5g... * manager1 Ready Active Leader 26.1.3 x8qk2... worker1 Ready Active 26.1.3 p3mz7... worker2 Ready Active 26.1.3
Разберём, что означают статусы:
- ID — уникальный идентификатор узла.
- * — текущая нода, с которой выполняется команда.
- STATUS: Ready — узел в рабочем состоянии и может принимать задачи.
- AVAILABILITY: Active — узел доступен для размещения задач. Другие варианты:
Drain(задачи не назначаются, но работающие остаются) иPause. - MANAGER STATUS:
Leader— активный менеджер, принимающий решения;Reachable— резервный менеджер; пусто — воркер.
Если все узлы в состоянии Ready и Active, кластер готов к работе.
Укрепление управления: добавление дополнительных менеджеров
Для повышения отказоустойчивости добавьте ещё хотя бы двух менеджеров. Это позволит кластеру пережить отказ одного узла без потери управления.
На лидере получите токен для менеджеров:
sudo docker swarm join-token manager
Вы увидите команду вида:
docker swarm join --token SWMTKN-1-abcdef... 192.168.1.100:2377
Выполните её на каждой новой ноде, которую вы хотите сделать менеджером:
sudo docker swarm join --token SWMTKN-1-abcdef... 192.168.1.100:2377
После этого проверьте список узлов — новые менеджеры появятся с пометкой Reachable. Убедитесь, что общее число менеджеров нечётное (3, 5, 7...) для корректной работы Raft.
Безопасность на первом месте: ротация токенов
Если вы подозреваете, что токен был скомпрометирован (например, попал в логи или был опубликован), его необходимо повернуть (rotate). Это создаст новый токен и сделает старый недействительным.
Для воркеров:
sudo docker swarm join-token --rotate worker
Для менеджеров:
sudo docker swarm join-token --rotate manager
После ротации новые узлы смогут присоединиться только с новым токеном. Существующие узлы продолжат работу без изменений — их аутентификация основана на TLS-сертификатах, а не на токенах.
Развёртывание приложений: сервисы как единое целое
В Docker Swarm основной единицей развёртывания является сервис (service). Сервис описывает желаемое состояние: какой образ использовать, сколько реплик запустить, какие сети подключить, какие порты опубликовать. Docker Swarm автоматически распределяет задачи (tasks) — экземпляры контейнеров — по доступным узлам, обеспечивая отказоустойчивость и балансировку нагрузки.
Каждый сервис имеет встроенный DNS-резолвер: другие сервисы могут обращаться к нему по имени, и Swarm автоматически распределит запросы между всеми его репликами.
Запуск веб-сервера: Nginx под управлением Swarm
Начнём с простого, но важного компонента — веб-сервера. Создадим сервис на основе образа nginx:alpine с тремя репликами:
sudo docker service create --name web \ --replicas 3 \ -p 80:80 \ --network ingress \ nginx:alpine
Разберём параметры:
--name web— задаёт имя сервиса, по которому к нему можно будет обратиться из других сервисов.--replicas 3— указывает, что нужно запустить три идентичные задачи (контейнера).-p 80:80— публикует порт 80 на всех узлах кластера. Это означает, что Nginx будет доступен по IP любого узла (менеджера или воркера).--network ingress— подключает сервис к специальной встроенной overlay-сети для входящего трафика.
Проверим статус задач:
sudo docker service ps web
И протестируем доступность:
curl http://localhost
Вы должны увидеть HTML-код стандартной страницы Nginx.
Создание изолированной сети: overlay для внутреннего трафика
Для безопасного взаимодействия между сервисами (например, между веб-сервером и API) создадим собственную overlay-сеть:
sudo docker network create -d overlay \ --subnet 10.13.0.0/24 \ --attachable \ my-net
Пояснение:
-d overlay— указывает драйвер сети, поддерживающий межузловое взаимодействие.--subnet— задаёт фиксированный диапазон IP-адресов для этой сети (необязательно, но полезно для предсказуемости).--attachable— позволяет запускать временные контейнеры в этой сети (например, для отладки командойdocker run).
Эта сеть видна только внутри кластера и изолирована от внешнего мира и от других overlay-сетей.
Запуск API-сервиса: Python-микросервис в своей среде
Теперь развернём простое Flask-приложение в сети my-net:
sudo docker service create --name api \ --replicas 2 \ --network my-net \ python:3.11-slim \ sh -c "pip install --no-cache-dir flask && echo 'from flask import Flask; app=Flask(__name__); @app.route(\"/\") def home(): return \"Hello Swarm!\"' > app.py && python -u app.py"
Этот многострочный вызов выполняет следующее:
- Устанавливает Flask без кэширования (
--no-cache-dirэкономит место в образе). - Создаёт простой Python-скрипт, который при запросе к корню возвращает
Hello Swarm!. - Запускает сервер Flask в режиме без буферизации вывода (
python -u), чтобы логи отображались в реальном времени.
Обратите внимание: порт 5000 (по умолчанию у Flask) не опубликован наружу. Сервис доступен только внутри сети my-net по DNS-имени api.
Тестирование взаимодействия: отладка связи между сервисами
Для проверки связи запустим временный контейнер в сети my-net:
sudo docker run -it --rm \ --network my-net \ alpine sh
Внутри контейнера выполните:
wget -O- api:5000
Вы должны увидеть ответ:
Hello Swarm!
Это доказывает, что:
- Сервис
apiуспешно разрешается по DNS-имени. - Трафик между сервисами проходит через overlay-сеть.
- Swarm автоматически балансирует запросы между всеми репликами API.
Внешние пользователи по-прежнему не видят API — он изолирован, что соответствует принципам безопасности.
Управление данными: конфигурации и секреты в Swarm
Жёстко закодированные конфигурации и пароли в образах — антипаттерн. Docker Swarm предоставляет два механизма для безопасного и декларативного управления данными:
- Конфиги (configs) — для несекретных данных: Nginx-конфиги, JSON-настройки, сертификаты CA и т.д.
- Секреты (secrets) — для конфиденциальной информации: пароли, API-ключи, токены.
Оба типа данных хранятся в зашифрованном виде в Raft-логе менеджеров и передаются только на те узлы, где запущены связанные задачи. Они монтируются в контейнер как файлы в tmpfs — то есть никогда не попадают на диск воркера и не видны в логах.
Конфигурация Nginx: оптимизация через централизованный config
Создадим конфиг для Nginx на менеджере:
echo "worker_processes auto;" | sudo docker config create nginx-conf -
Здесь:
docker config create— команда для создания объекта конфигурации в кластере.nginx-conf— имя конфига.-— указывает, что данные поступают из stdin.
Теперь этот конфиг можно подключить к любому сервису без пересборки образа.
Секреты безопасности: защита учётных данных
Создадим секрет с паролем базы данных:
echo "qwerty123" | sudo docker secret create db_password -
Этот пароль будет:
- зашифрован алгоритмом AES-256-GCM;
- доступен только сервисам, которым он явно назначен;
- смонтирован в контейнер как файл
/run/secrets/db_passс правами0400(только владелец, только чтение).
Команда docker secret inspect db_password покажет метаданные, но никогда не содержимое.
Обновление сервиса: применение конфигураций и секретов
Теперь обновим сервис web, чтобы он использовал наш конфиг и секрет:
sudo docker service update \ --config-add source=nginx-conf,target=/etc/nginx/nginx.conf \ --secret-add source=db_password,target=/run/secrets/db_pass \ web
Swarm выполнит постепенное обновление (rolling update): по одной реплике будет остановлена старая версия и запущена новая с подключёнными данными. Это обеспечивает нулевой простой (zero downtime).
Финальная проверка: заглянем внутрь контейнера
Найдём ID контейнера одной из реплик сервиса web:
sudo docker ps --filter name=web -q
Проверим наличие конфига:
sudo docker exec ls -l /etc/nginx/nginx.conf
Ожидаемый вывод:
-r--r--r-- 1 root root 20 May 15 10:00 /etc/nginx/nginx.conf
Проверим содержимое секрета:
sudo docker exec cat /run/secrets/db_pass
Результат:
qwerty123
Всё работает! Конфиг и секрет доставлены, права корректны, данные доступны приложению.
Заключение: путь к надёжной и безопасной инфраструктуре
Docker Swarm — это не просто инструмент, а философия: простота, встроенность, предсказуемость. На Ubuntu 24.04 вы можете развернуть отказоустойчивый кластер за считанные минуты, использовать встроенные механизмы безопасности для управления секретами, а также легко масштабировать и обновлять приложения без простоев. Для небольших команд и средних проектов Swarm остаётся непревзойдённым по соотношению «функциональность/сложность».
Помните: секреты не предназначены для больших объёмов данных (более 500 КБ) — для этого используйте внешние тома. Также всегда следите за версиями Docker, ротируйте токены при компрометации и ограничивайте сетевой доступ к управляющим портам.
С этого момента ваша инфраструктура — не набор машин, а единый оркестр, где каждый контейнер играет свою партию под управлением Docker Swarm.


