Я конечно надеюсь, что вы набрели на эту статью случайно, а не по необходимости. Лично я пишу эту статью просто для тестирования что произойдёт.

swarm-failover-01-initial

Для теста я просто выключу хост c-stream-9-vm9 и посмотрю, что произойдёт.

docker node ls
ojbmchzvipwmlwsssx0ctrxyq *   c-stream-9-vm4   Ready     Active         Leader           25.0.3
sho740q9bdze4xcspm7mzv9m9     c-stream-9-vm8   Ready     Active         Reachable        25.0.3
m8hrgh3jdlqlzijvhjdig4lx2     c-stream-9-vm9   Ready     Active         Unreachable      25.0.3

Команда docker node ls ожидаемо показала, что хост c-stream-9-vm9 недоступен. Теперь я проверю что там с контейнерами сервиса web.

swarm-failover-02-node-down.svg

docker service ps web
42iq7g9uwvqk   web.1       nginx:latest   c-stream-9-vm4   Running         Running 2 minutes ago
8yhysu9vqh00   web.2       nginx:latest   c-stream-9-vm8   Running         Running 2 minutes ago
oqv42koox4pe   web.3       nginx:latest   c-stream-9-vm4   Running         Running 9 seconds ago
zig22fjbiaqc    \_ web.3   nginx:latest   c-stream-9-vm9   Shutdown        Running 2 minutes ago

А вот тут возможно для кого-то неожиданный исход. Так как контейнер стал недоступным вместе с хостом Swarm это обнаружил и понял, что сейчас активны только две реплики из трёх, и понял, что нужно создать недостающую третью. В итоге на хосте c-stream-9-vm4 теперь размещаются 2 контейнера.

swarm-failover-03-auto-recovery.svg

Теперь я включу хост c-stream-9-vm9 и проверю что произойдёт.

docker node ls
ojbmchzvipwmlwsssx0ctrxyq *   c-stream-9-vm4   Ready     Active         Leader           25.0.3
sho740q9bdze4xcspm7mzv9m9     c-stream-9-vm8   Ready     Active         Reachable        28.3.3
m8hrgh3jdlqlzijvhjdig4lx2     c-stream-9-vm9   Ready     Active         Reachable        25.0.3

Тут ничего необычного, хост стал доступен. Теперь проверю что там с контейнерами.

docker service ps web
42iq7g9uwvqk   web.1       nginx:latest   c-stream-9-vm4   Running         Running 2 minutes ago
8yhysu9vqh00   web.2       nginx:latest   c-stream-9-vm8   Running         Running 2 minutes ago
oqv42koox4pe   web.3       nginx:latest   c-stream-9-vm4   Running         Running 9 seconds ago
zig22fjbiaqc    \_ web.3   nginx:latest   c-stream-9-vm9   Shutdown        Running 2 minutes ago

А вот тут не совсем очевидный вывод. Да хост стал доступен, но вот контейнер не переехал на восстановленный хост.

swarm-failover-04-node-recovered.svg

Почему контейнер не вернулся на восстановленный хост?

Разберём подробно, почему контейнер (“task”) не “переехал” обратно на восстановившийся хост, и почему это — нормальное, осознанное поведение Swarm.

Сам Swarm работает немного по-другому. Swarm не хранит “привязку” контейнера к конкретному хосту как обязательную. Его главная задача — это поддержание реплики. Когда я отключил хост c-stream-9-vm9 он понял, что реплика 2/3 после чего создал недостающую реплику, статус теперь 3/3. После этого ситуация считается решенной и даже после включения хоста реплика остаётся 3/3, соответственно ничего делать и не нужно.

swarm-failover-05-explanation.svg

Можно ли заставить Swarm “переселить” контейнеры обратно?

Да, но только вручную или при обновлении сервиса:

  • Способ 1 — форсировать обновление
    docker service update --force web
    docker service ps web
    
    ID             NAME        IMAGE          NODE             DESIRED STATE   CURRENT STATE             ERROR     PORTS
    4kvxwxglflhd   web.1       nginx:latest   c-stream-9-vm9   Running         Running 17 seconds ago
    26xg6bxcmp30   web.2       nginx:latest   c-stream-9-vm8   Running         Running 13 seconds ago
    0qbkd8pr2c6o   web.3       nginx:latest   c-stream-9-vm4   Running         Running 8 seconds ago
    
  • Способ 2 — удалить старый контейнер вручную, Swarm создаст новый — возможно уже на свободной vm9
    docker rm <container>