Disclaimer
Я не являюсь ни ярым фанатом Docker'а, ни его противником. Этой технологии нужно отдать должное, ведь так или иначе - она совершила революцию в IT. Пользуюсь ли я им? Определенно, даже мой блог давно крутится в Докере. Считаю ли я что его нужно использовать везде - нет.
Но Docker это уже давно не просто пакер для софта. И если к самому Докеру, как упаковщику приложений вопросов не так уж много (хотя что может быть ужаснее этих bash-oneline'еров в Dockerfiles?), то вот к инфраструктуре, которую вокруг него строят у меня вопросов миллион.
И именно о проблемах, связанных с одной из таких новомодных технологий пойдет речь. Мне пришлось использовать Docker Swarm Mode с момента его релиза в версии 1.12 (октябрь 2016-го), и на момент написания этой статьи я могу "похвастаться" тем, что уже полгода наш production работает на нём.
Сделал ли он мою работу проще? Да, многие вещи здесь решаются до безобразия легко. Но стал ли я спокойнее спать по ночам? Отнюдь. И, как заметил один мой коллега, грош цена технологии, которую нельзя не трогать хотя бы месяц, чтобы всё работало надёжно и стабильно.
Замечу, что ниже приведены те, и только те проблемы, с которыми я столкнулся лично. Возможно у вас что-то пройдет лучше. Возможно, если вы только запускаете кластер - вы не столкнетесь с большинством из них. Я лишь описываю свой опыт и хочу предостеречь тех, кто хочет испытать новую технологию.
Те самые причины
Overlay network partitioning
- Дата: 2016.12.09
- Версия: 1.12.x
- Обсуждение: issues/25266 + issues/2161 + issues/23855
Это проблема стала первой головной болью в работе над проектом. Я использовал самый первый релиз ветки 1.12 и был готов к тому, что с ним не всё будет гладко. Так и вышло. Проблема заключалась в том, что сервисы на одной ноде и в одной подсети видели друг друга отлично. Но попытки обратиться к другому сервису в той же подсети но на другой ноде в какой-то момент переставали быть успешными. Сервисы не видели друг друга и всё вставало. Единственное решение на тот момент: удалить все сервисы (потому что отключить сервис от сети было невозможно), удалить сеть, создать её заново и заново создать все сервисы. Не самая приятная и безболезненная процедура.
Нужно отдать должное, что проблему исправили примерно за 2 недели в одном из release candidates версии 1.13 и с тех пор в таком ужасном виде она не повторялась. Но...
Multiple overlay networks
- Дата: 2016.12.20
- Версии: 1.12.x - 17.03.x
Если вы используете Swarm Mode - вы используете overlay networks. Это его базис и без них использование технологии не имеет смысла. И, безусловно, когда я только начал работать с этим - я тут же разделил все тестовые окружения, все субпроекты и все микросервисы в отдельные подсети.
Каково же было моё удивление, когда балансировщик, торчащий одновременно в blue и green окружения, переставал видеть то одно из них, то другое. Но если периодически возникающую Gateway timeout
на ранних стадиях проекта еще можно было как-то вытерпеть, то когда начал отваливаться контейнер базы данных, так же подключенный к двум overlay-сетям одновременно - я понял, что нужно что-то срочно менять. Чтобы решить эту проблему мне на 2 месяца пришлось забыть о безопасности и логическом разделении окружений и засунуть всё в одну overlay-сеть. В таком виде это и работало до релиза 17.03, где это вроде как исправили и на данный момент порядка 10 сетей у меня сосуществуют без проблем.
context deadline exceeded
- Дата: 2017.02.07
- Версии: 1.12 - 1.13
- Обсуждение: issues/23782 + issues/24407 + issues/25432
Выглядит это так: любое действие над сервисом, нодой или любой другой сущностью приводит к ошибке Error response from daemon: rpc error: code = 4 desc = context deadline exceeded
. По-сути управляющий сервис основного менеджера уходит в дедлок, а управляющий сервис резервного менеджера не может перехватить инициативу, т.к. ждет подтверждения от основного. Я потратил несколько часов на попытки вывести менеджеров из такого состояния, но они не увенчались успехом. В конце концов мне пришлось полностью пересоздать кластер с нуля и заново задеплоить все сервисы.
Скорее всего эта проблема связана с потерей кворума у менеджер нод и чаще всего проявляется в тех кластерах, где менеджеров всего два. В моем случае, увеличение количества менеджеров с 3 до 5 помогло полностью избавиться от проблемы.
sandbox does not exist for container
- Дата: 2017.03.02
- Версии: 1.13.x - 17.03.x
- Обсуждение: issues/31562 + issues/31698
С данной проблемой можно было столкнуться, когда по каким-либо причинам необходимо было удалить сервис и пересоздать его - например, чтобы подключить его к другой overlay-сети. Вы делаете docker service rm
, потом - docker service create ...
, и видите в логах следующее:
time="2017-04-12T09:22:42.470156578Z" level=error msg="fatal task error" error="task: non-zero exit (1)" module="node/agent/taskmanager" node.id=phdqmmtqot0fi2qkyxpppz0tl service.id=qfwfmtkeo9t9e58tstl94chqd task.id=wl22e7gs0lrarae904bn1kv7f
time="2017-04-12T09:22:43.242218228Z" level=warning msg="failed to deactivate service binding for container green-mycoolapp.1.ofbvxy6w6zvawv1g92k4rvpxh" error="network sandbox does not exist for container green-mycoolapp.1.ofbvxy6w6zvawv1g92k4rvpxh" module="node/agent" node.id=phdqmmtqot0fi2qkyxpppz0tl
time="2017-04-12T09:23:09.905115654Z" level=error msg="remove task failed" error="removal of container green-mycoolapp.1.y0dq252rvysa8bujus1xowhwe is already in progress" module="node/agent" node.id=phdqmmtqot0fi2qkyxpppz0tl task.id=y0dq252rvysa8bujus1xowhwe
time="2017-04-12T09:23:09.906448299Z" level=error msg="error removing 0e89db687587e5379e453fe8b7049e467b042343443b8f7e28a4ec8f4b43a32f: No such container: 0e89db687587e5379e453fe8b7049e467b042343443b8f7e28a4ec8f4b43a32f"
При этом сервис с другим именем создается без проблем.
forced certificate renewal
- Дата: 2017.04.03
- Версия: 17.03.0-ce .. 17.04.0-ce
- Обсуждение: issues/31803
Допустим, у вас есть рабочий кластер на версии 17.03.0-ce
и вы решили обновить его до 17.04.0-ce
. Представьте, что вы обновили одну из manager-нод. И получили вот такую ошибку:
Apr 11 09:37:16 dmgr-01 dockerd[646]: time="2017-04-11T09:37:16.624218451Z" level=info msg="forced certificate renewal" module="node/tls" node.id=bdaa7r9e0p7uw3w4svbh0k12z node.role=swarm-manager
Apr 11 09:37:16 dmgr-01 dockerd[646]: time="2017-04-11T09:37:16.651542734Z" level=info msg="forced certificate renewal" module="node/tls" node.id=bdaa7r9e0p7uw3w4svbh0k12z node.role=swarm-manager
Apr 11 09:37:16 dmgr-01 dockerd[646]: time="2017-04-11T09:37:16.677872457Z" level=info msg="forced certificate renewal" module="node/tls" node.id=bdaa7r9e0p7uw3w4svbh0k12z node.role=swarm-manager
Сообщение повторяется в логе циклично, по несколько раз в секунду. Ни откатиться назад, ни обновиться дальше без того, чтобы развалить кластер - невозможно.
GELF problems
- Дата: 2017.03.20
- Версия: 17.03.0-ce
- Обсуждение: issues/31942
Docker поддерживает различные варианты сбора логов. По-умолчанию используется json-file и если не забыть про его параметры max-size
и max-file
, то ни с какими проблемами вы не столкнётесь (по-умолчанию эти параметры не заданы и если логов много - у вас может внезапно закончиться место). Но ведь нам интересно собирать логи, аггрегировать и анализировать их? Для этого есть logging drivers, позволяющие отправлять строки куда-нибудь в syslog
, gelf
, fluentd
и других форматах. Казалось бы, что может пойти не так?
К сожалению - может. Достаточно вашему, например, gelf
-endpoint'у упасть и какое-то время не принимать сообщения, то dockerd
начнёт складывать их в память до тех пор, пока не упадёт. А если вы пишете хотя бы 20-30 тысяч строк в минуту - это произойдет невероятно быстро. Что уж говорить о том, что если падает демон на мастер-ноде, это ведёт к leadership election, перестроению кластера и 502
-ым ошибкам на десяток секунд.
В последних версиях была введена опция негарантированной доставки логов, что частично решает эту проблему. Но всё же, система, которая не предусматривает стабильную работу во время проблем с сетью - очень странная система.
Service Discovery Stuck
- Дата: 2017.05.15
- Версия: 17.04.0-ce
- Обсуждение: issues/30134
Представьте, что вы поменяли конфигурацию вашего стека. Или даже просто передеплоили контейнер. Что может пойти не так?
root@balancer:/# echo > /dev/tcp/myservice/80
root@balancer:/# echo > /dev/tcp/myservice/80
bash: connect: Connection refused
bash: /dev/tcp/myservice/80: Connection refused
root@balancer:/# echo > /dev/tcp/myservice/80
root@balancer:/# echo > /dev/tcp/myservice/80
bash: connect: Connection refused
bash: /dev/tcp/myservice/80: Connection refused
root@balancer:/# echo > /dev/tcp/myservice/80
root@balancer:/# echo > /dev/tcp/myservice/80
bash: connect: Connection refused
bash: /dev/tcp/myservice/80: Connection refused
Что здесь происходит, спросите вы? Встроенный service-discovery сошёл с ума и балансирует запросы между новым контейнером и уже не существующим. Ни последующие обновления сервиса, ни даже его пересоздание - не помогают решить проблему. Опытным путем было выяснено, что помогает перезагрузка ноды, где был запущен проблеммный сервис. Ноды. Целиком.
Pulling from private registries
- Дата: 2017.05.18
- Версия: 17.03.0-ce .. 17.05.0-ce
- Обсуждение: issues/31534
С самого начала использования Docker в Swarm Mode мы использовали сервисы с имиджами из приватных репозиториев, а вернее - прямо с Docker.io. Да, примерно в 13-ой версии был баг, из-за которого кроме передачи параметра --with-registry-auth
необходимо было пройти по всем нодам в кластере и сделать docker login
, однако после этих манипуляций всё работало и ничто не предвещало беды.
Однако когда мы решили попробовать новый функционал, а вернее docker stack deploy
- мы столкнулись с тем, что он категорически не работает с приватными репозиториями и не скачивает имиджи. Я перепробовал все возможные варианты, проверил что на всех нодах сделан docker login
, пробовал с ключом --with-registry-auth
и без него - никаких успехов я не достиг.
root@dmgr-01:~# docker stack ps --no-trunc mycoolapp
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
kf9wjfgib16whpgdksr15ftnk mycoolapp_mycoolapp2.1 registry.hub.docker.com/alekseysmyrnov/dockercloud-quickstart-python node1 Ready Assigned less than a second ago
v6xocjb0tobg8xlum50pphcit \_ mycoolapp_mycoolapp2.1 registry.hub.docker.com/alekseysmyrnov/dockercloud-quickstart-python node1 Shutdown Rejected less than a second ago "No such image: registry.hub.docker.com/alekseysmyrnov/dockercloud-quickstart-python:latest"
В логах же только информация об отсутствии доступа:
May 31 10:49:13 node1 dockerd[21939]: time="2017-05-31T10:49:13.318212282+02:00" level=error msg="Not continuing with pull after error: errors:\ndenied: requested access to the resource is denied\nunauthorized: authentication required\n"
May 31 10:49:13 node1 dockerd[21939]: time="2017-05-31T10:49:13.318267734+02:00" level=info msg="Ignoring extra error returned from registry: unauthorized: authentication required"
May 31 10:49:13 node1 dockerd[21939]: time="2017-05-31T10:49:13.318322422+02:00" level=info msg="Translating \"denied: requested access to the resource is denied\" to \"repository registry.hub.docker.com/alekseysmyrnov/dockercloud-quickstart-python not found: does not exist or no pull access\""
May 31 10:49:13 node1 dockerd[21939]: time="2017-05-31T10:49:13.318379105+02:00" level=error msg="pulling image failed" error="repository registry.hub.docker.com/alekseysmyrnov/dockercloud-quickstart-python not found: does not exist or no pull access" module="node/agent/taskmanager" node.id=mbq71ibxlna3ptqgfaovpmq5d service.id=xcn9h60zlr7b83z0otpt2ju0t task.id=pva4ak3tnclu3a3rnlh7akbvv
May 31 10:49:13 node1 dockerd[21939]: time="2017-05-31T10:49:13.319201194+02:00" level=error msg="fatal task error" error="No such image: registry.hub.docker.com/alekseysmyrnov/dockercloud-quickstart-python:latest" module="node/agent/taskmanager" node.id=mbq71ibxlna3ptqgfaovpmq5d service.id=xcn9h60zlr7b83z0otpt2ju0t task.id=pva4ak3tnclu3a3rnlh7akbvv
Нужно заметить, что этот же самый имидж успешно запускается в том же сворме на той же ноде через docker service create
.
Swarm unstable under (heavy) load
- Дата: всегда
- Версия: 1.13 .. 17.05.0-ce
- Обсуждение: issues/33589 + issues/32841
Тут мне даже нечего добавить к обсуждению на GitHub, я могу лишь подтвердить, что чем выше у вас будет нагрузка - тем чаще вы будете встречаться с тем, что иногда вам в ответ прилетает 50x
. Даже при размещении всех сервисов на одной ноде (я специально экспериментировал) между сервисами может произойти Request timeout
, Hostname not found
или еще какое-нибудь безобразие.
Port publishing
- Дата: 2017.05.30
- Версия: 17.05.0-ce
- Обсуждение: issues/33430
Как и многие другие проблемы - эта проявилась после обновления с предыдущей версии и меня заверили, что на чистой инсталляции она не воспроизводится.
Выглядит это так:
- У вас был сервис, слушающий определенный порт на всех нодах (ingress
режим)
- Вы обновляете ваш swarm
- У вас больше нет сервиса, слушающего этот порт
Причем не помогает ни удаление сервиса и его запуск заново (с теми же параметрами), ни перезагрузка нод, ни даже цикличный перезапуск нод управляющих (да, и такое я делал от безысходности). Есть мнение, что причина связана с тем, что где-то под капотом движок запомнил неправильные значения, связанные с сетью. Хотя в логах нет никаких ошибок. Более того, в режиме прослушивания порта на одном хосте (в режиме docker-proxy
) всё отлично работает.
Решение: удалить старый сервис и запустить новый с параметрами docker service create ... --mode global --publish mode=host,target=X,published=Y,protocol=Z
. Да, теперь у вас N запущенных тасков вместо одного и вы больше не используете одну из фич, ради которой вообще взялись за Docker Swarm Mode.
Strange failover behavior
- Дата: 2017.06.01
- Версия: 17.05.0-ce
- Обсуждение: issues/24557 + issues/25976
Допустим, ваша инфраструктура развернута на DigitalOcean. И сегодня вы испытываете проблемы с сетью (у DigitalOcean так бывает), такие, что допустим теряется 10% пакетов. Что будет делать оркестратор? Правильно, выведет ноду из кластера:
Jun 1 13:35:21 dmgr-01 dockerd[655]: time="2017-06-01T13:35:21.043977730Z" level=info msg="me
mberlist: Suspect dwrk-webapp-green-01-17fb0e89123c has failed, no acks received"
Jun 1 13:35:23 dmgr-01 dockerd[655]: time="2017-06-01T13:35:23.112903450Z" level=info msg="me
mberlist: Marking dwrk-webapp-green-01-17fb0e89123c as failed, suspect timeout reached"
Jun 1 13:35:27 dmgr-01 dockerd[655]: time="2017-06-01T13:35:27.058392487Z" level=warning msg=
"memberlist: Refuting a suspect message (from: dmgr-01-0adf16014854)"
Сразу возникает первый вопрос: почему 90% пакетов между нодами ходит, но нода уже выведена из кластера? Непонятно. Более того, этот порог никак не настраивается и менеджер даже не пытается связаться с воркером еще раз. Он выводит ноду из кластера, overlay-сеть становится недоступна и балансировщик перестает видеть сервисы запущенные на этой ноде.
Предположим, что эта проблема решается избыточностью нод. При выводе проблемной ноды из кластера сервисы должны безболезненно переехать на другую ноду. И так и есть, swarm попробует запустить сервисы где-нибудь еще. Но здесь начинается самое интересное: процессы запущенные на failed-ноде будут продолжать работать дальше. Ровно до тех пор, пока связь с этой нодой не станет идеальной и она не вернется в кластер. А вот когда она вернется - оркестратор убьёт сервисы запущенные на ней.
Я вижу тут две логические проблемы:
- Если у вас volumes поверх glusterfs, например, то сервис продолжит писать на этот volume. Ловите split-brain или какие угодно неожиданные результаты.
- Если сервисы не были убиты, когда нода была выведена из кластера, то зачем этот рестарт по возвращению? От чего он может спасти?
Ответов у меня, к сожалению, нет.
manifest verification failed
- Дата: 2017.06.05
- Версия: 17.05.0-ce
Допустим у вас есть Swarm из ~10 нод. Вы добавляете еще 3. Какого результата вы ожидаете? Логично предположить, что все глобальные сервисы автоматически поднимутся на новых нодах (при условии правильных constraints
конечно). Хорошее предположение, но оно конечно не верно:
Jun 5 15:20:17 node5 dockerd[26154]: time="2017-06-05T15:20:17.232615299+02:00" level=error msg="manifest verification failed for digest sha256:130fcb4909b77a8ceb3f46c09ddb0a74901e8a3e3190ea2fcfabaa520d8daa22"
Jun 5 15:20:17 node5 dockerd[26154]: time="2017-06-05T15:20:17.232718092+02:00" level=info msg="Attempting next endpoint for pull after error: manifest verification failed for digest sha256:130fcb4909b77a8ceb3f46c09ddb0a74901e8a3e3190ea2fcfabaa520d8daa22"
Судя по всему - это еще одна проблема связанная с private registries и опцией --with-registry-auth
. Нужно делать docker service rm ...
+ docker service create ...
.
Выводы
На момент написания данной статьи проект имеет больше чем 2500 issues только в главном репозитории и это количество постоянно растёт. Несмотря на огромное коммьюнити и на то, что вам даже ответят, выглядит это так, будто у проекта просто не хватает сил на исправления и доработки всего, что было в нём намечено. Мне кажется что это в первую очередь связано с тем, что если Docker как упаковщик ПО интересен всем, то Docker Swarm Mode как оркестратор не так уж необходим рынку. Тем более, когда есть более крупные, надежные, а главное - production-ready игроки. Особенно странно выглядит всё вышесказанное учитывая, что Docker идёт по пути разделения Community Edition и Enterprise Edition - мне кажется что для этого они еще не готовы.
Скорее всего мне придется мигрировать этот проект, потому что дальнейшее использование Swarm Mode практически невозможно в силу обстоятельств описанных выше. К моему глубочайшему сожалению, это очень интересная, но очень сырая технология.
Что делать?
Если вам всё же приходится использовать Docker Swarm Mode по каким-либо причинам (требование заказчика, необходимость ультра-быстрого старта или вы просто не хотите разбираться с другими решениями), вот несколько советов:
- Придерживайтесь KISS. Где можно не делать несколько сетей - не делайте. Где можно использовать одну ноду вместо десяти - используйте.
- Забудьте про встроенный балансировщик, он не работает так как вам нужно.
- Держите базу данных как можно ближе к сервисам её использующим. А еще лучше не держите базу в Docker.
- Если у вас CI/CD - первым делом настройте уборку мусора, место на дисках закончится в самый неподходящий момент.
- И еще раз посмотрите в сторону других решений.
PS: и да, не используйте DigitalOcean для более-менее серьезных проектов. Или по-крайней мере не надейтесь на надежную связность между виртуальными серверами.