Docker Swarm: чем отличается от compose и как не запустить всё дважды
Разбор перехода с docker-compose на Swarm: почему контейнеры оказываются запущены дважды, как распределять сервисы между manager и worker и какие команды для чего.
miki/docker-swarm-chem-otlichaetsya-ot-compose-i-kak-ne-zapustit- · v1
Разбор перехода с docker-compose на Swarm: почему контейнеры оказываются запущены дважды, как распределять сервисы между manager и worker и какие команды для чего.
Типичный вопрос после перехода на Swarm: «место на диске не освободилось, контейнеры появились на втором сервере, docker-compose теперь не нужен?»
Короткий ответ: всё запущено дважды. Старые контейнеры от docker-compose никуда не делись, а рядом появились сервисы Swarm. Они не видят друг друга и едят ресурсы одновременно.
Список непоследовательный — читай то, что нужно.
docker compose up | docker stack deploy | |
|---|---|---|
| Что создаёт | контейнеры | сервисы |
| Где запускает | только на этой машине | на любом узле кластера |
| Чем смотреть | docker ps | docker service ls |
| При падении | лежит, пока не поднимешь | перезапускается сам |
| Масштабирование | вручную | replicas: N |
Главное: docker ps показывает и контейнеры Swarm, и обычные. Поэтому по одному docker ps нельзя понять, что чем управляется — и именно отсюда ощущение, что «всё как-то странно».
Разобраться
Сначала смотрим сервисы кластера:
Потом — все контейнеры на машине:
У контейнеров Swarm имена вида стек_сервис.1.длинныйидентификатор. У обычных — простые, из compose-файла. Это самый быстрый способ их различить.
docker service ls- Ты можешь назвать, какие контейнеры на машине от Swarm, а какие нет
- Понимаешь, почему остановленный контейнер Swarm поднимается сам
Когда сервис уже живёт в Swarm, старый контейнер надо гасить тем же инструментом, которым поднимали:
Не пытайся гасить через docker stop контейнер Swarm-сервиса — кластер тут же создаст новый. Нужно docker service scale имя=0 или docker stack rm.
docker ps --format "{{.Names}}"- Каждый сервис запущен ровно один раз
- Старые compose-контейнеры остановлены тем же инструментом, которым поднимались
В кластере есть узлы двух ролей. Manager держит состояние кластера и принимает решения, worker — просто запускает задачи.
Рабочее правило распределения:
| Что | Куда | Почему |
|---|---|---|
| Инфраструктура кластера, панель управления, прокси | Manager | без них кластером не поуправляешь |
| Базы данных | Manager | стабильность важнее гибкости, данные привязаны к тому узла |
| Кэш (Redis и подобные) | Worker | лёгкие, перезапуск безболезненен |
| Бэкенды | Worker | тяжёлые по CPU, их много |
| Фронтенды, статика | Worker | лёгкие, легко масштабируются |
Закрепляется в compose через ограничения размещения:
Критично для баз данных: если сервис с томом переедет на другой узел, том за ним не поедет — он привязан к машине. Получишь пустую базу и очень неприятный вечер.
Работа со Swarm
Тот же файл, что и для compose, но часть полей работает иначе:
- раздел
deployчитается только в Swarm, compose его игнорирует; buildв Swarm не работает — образ должен быть уже собран и доступен всем узлам;depends_onигнорируется — порядок запуска не гарантируется.
docker stack deploy -c docker-compose.yml myapp- Стек виден в docker stack ls
- Все сервисы в состоянии N/N
Если docker service ls показывает 0/1, смотри историю задач:
Колонка CURRENT STATE и ERROR обычно говорят всё. Типичные состояния:
- Rejected — узел отказался: нет образа, не сошлись ограничения размещения, нет ресурсов
- Failed — контейнер запустился и упал с ненулевым кодом
- Complete — контейнер вышел с кодом 0. При
restart_policy: on-failureкластер его НЕ поднимет, и сервис так и останется 0/1
Логи сервиса целиком: docker service logs имя --tail 50
docker service ps имя-сервиса --no-trunc- Ты знаешь, чем отличаются Rejected, Failed и Complete
- Умеешь смотреть логи сервиса, а не контейнера
Заметь: docker stack deploy с тем же именем стека не создаёт второй стек, а обновляет существующий. Это и есть штатный способ выкатки.
Чтобы узел вошёл в кластер, нужен join-токен. Их два вида, и разница между ними огромная:
Manager-токен — это полный контроль над кластером. Кто его получил, тот может запускать что угодно на любом узле и читать все секреты Swarm. По последствиям это хуже утечки пароля от одной базы.
Отсюда три правила:
- Токены не коммитятся. Ни в скрипты автоматизации, ни в документацию, ни «временно, чтобы не забыть».
- Если токен где-то засветился — ротируй:
docker swarm join-token --rotate manager. Старый сразу перестаёт работать, уже вошедшие узлы не вылетают. - Новые узлы — всегда worker, если нет явной причины делать их manager.
Проверь прямо сейчас: grep -r "SWMTKN" . по своим репозиториям и скриптам.

