Локальный Docker реестр в Swarm

container stackВ одном из прошлых постов про проверку состояния контейнеров в Docker ближе к концу поста мне удалось запустить Swarm сервис из локально собранного образа. Ну как удалось.. Собрал и запустил. Но что меня удивило, Docker в Swarm режиме мне это позволил. Всё-таки в нём могло быть больше, чем один хост, а образ я создавал только на первом. Что если бы Swarm запустил сервис на ноде, где образа не было? Или отмасштабировал? Он же не стал бы автоматически копировать образы между хостами, так ведь? Или всё-таки стал бы?

Попробуем-ка сегодня мы отмасштабировать сервис на базе кастомного образа и посмотрим, получится ли. Спойлер: без локального реестра образов получится так себе.

Попытка 1. Просто используем локально собранный образ

Создаём Swarm

Как обычно, на моей машине установлен docker-machine и VirtualBox, поэтому создавать новый кластер можно с закрытыми глазами и правой рукой, привязанной к креслу. Во-первых, создадим хосты для Swarm менеджера и его работяг:

Легкотня. Затем инициализируем кластер на мастере через команду docker swarm init и запускаем произведённую им ‘join’ команду на остальных хостах:

Кластер готов. Сейчас мы быстренько добавим ему в помощники сервис для визуализации контейнеров и приступим, собственно, к делу.

Устанавливаем сервис визуализации

Когда имеешь дело с несколькими хостами и контейнерами, визуальный фидбэк может оказаться очень даже полезным, так что не будет лишним установить его прямо сейчас. Это достаточно просто сделать: подключаем локальный Docker клиент к Docker Engine master хоста, и запускаем сервис.

Проверяем порт 8080 на master‘е, и таки да, там всё красочно и прекрасно. Можно переходить к основной части нашего балета.

visualization service

Создаём кастомный образ для опытов

Хотя абсолютно любой Dockerfile нам бы подошёл, я всё-таки решил скопипастить уже готовый из поста про проверки состояния контейнеров. Всё-таки он уже один раз отработал, так что хоть какой-то неопределённости будет меньше. Сам Dockerfile:

И его товарищ — server.js:

Так как наш локальный докер клиент всё ещё подключён к master, можно просто запустить команду сборки образа и она очутится прямо в Swarm. По крайней мере на одной его машине.

А теперь основная часть: делаем сервис и масштабируем его по кластеру.

Запускаем и масштабиуем сервис

Запустить сервис из кастомного образа на машине Swarm, на которой он к тому же и собирался, — просто. Это уже получилось однажды, получится и сейчас.

Кстати, в прошлый раз я не вчитывался, но вывод из service create действительно предупредил, что образ лежит только на локальной машине, поэтому в любом другом месте кластера может случиться неожиданность. Я уже начинаю догадываться, чем закончится эксперимент.

Сервис визуализации подтверждает, что команда всё-таки сработала:

One instance of node server

Ну что, момент истины: отмасштабируется ли он?

Бздыщ! Отмасштабировался. Но как? Проверим-ка, что нам скажут таски сервиса:

Ха! Он положил все реплики сервиса на master хост — единственное место, где лежал кастомный образ. Что прикольно, в списке задач много тех, которые пытались положить контейнер на worker хосты, но упали с ошибкой No such image: server:latest. Нет на них образа и всё тут. Так что предположение, что Swarm может копировать образы между хостами — не подтвердилось. Будем пробовать что-то ещё.

Удаляем наш сервис и двигаемся к следующей попытке.

Попытка 2. Используем локальный Docker реестр образов

Вполне очевидный альтернативный подход — использовать какое-то подобие Docker Hub, но внутри Swarm — локальный реестр. Если он будет доступен внутри кластера, то наш кастомный образ можно запушить (docker push) в него, и создавать сервисы со ссылкой на внутренний реестр.

Вообще, создание собственного реестра вполне тривиально. Это просто запуск контейнера, вроде docker run -d -p5000:5000 registry:latest. Но есть загвоздка: HTTP-реестры доступны только по localhost, так что если мы хотим, чтобы к нему могли достучаться все хосты кластера, нужно использовать HTTPS и все связанные SSL сертификатами замуты (Update: на самом деле нет. Как подсказали умные люди, localhost тоже шарится внутри кластерных машин, так что если мы не собираемся пускать в реестр людей и роботов извне, то можно остановиться и на HTTP. Я до сих пор в шоке — для меня localhost всегда был чем-то сугубо интимным для каждого конкретного хоста). Пользоваться   insecure-registries настройкой в Docker Engine мне сегодня не хочется, так что будем колдовать с самописными SSL сертификатами.

В общем, план такой:

  1. Создаём SSL сертификат для адреса, например, myregistry.com, за которым будет скрываться наш реестр.
  2. Убеждаем Docker доверять этому сертификату.
  3. Добавляем myregistry.com в /etc/hosts на master и worker-* ноды, чтобы они знали, о чём идёт речь.
  4. Создаём реестр в HTTPS режиме.
  5. Пушим наш server:latest образ в реестр.
  6. Создаём сервис из образа в локальном реестре.

Шагов много, так что начнём с первого.

Создаём SSL сертификат

К счастью, на никсовых системах это просто:

На все вопросы, которые сертификат спрашивал по ходу дела, я отвечал пустой строкой. Ну, кроме Common Name — тот важен. В него я ввёл адрес реестра: myregistry.com.

Убеждаем Docker верить сертификату

Это тоже был бы простой шаг, но когда я пишу (или перевожу) посты в час ночи, наиболее простые решения часто проходят мимо, а остаются такие, как это. На каждом хосте кластера мне потребовалось три шага, чтобы положить сертификат в хранилище Docker Engine и тем самым убедить его в нашей благонадёжности:

  • Копируем registry.crt файл на Swarm хост,
  • создаём папку для сертификата в хранилище,
  • ложим в неё registry.crt.

После этих трёх шагов master нам точно станет доверять, но ещё остаётся worker-1 и worker-2 , так что придётся повторить. Дважды.

Добавляем myregistry.com в /etc/hosts

Реестр будет на master ноде, его айпишка — 192.168.99.100, так что она и пойдёт в /etc/hosts кластерных машин. Например, для мастера это будет выглядеть вот так:

Создаём HTTPS реестр

Реестр сервису потребуется доступ к файлам сертификата, так что их нужно будет скопировать в master. Можно было бы конечно использовать docker secret вместо этого, но ай.

А теперь, собственно, реестр:

Вот так-то. У нас появился ещё один цветной квадратик на кластерной карте:

Локальный Docker реестр в Swarm

Пушим образ в локальный реестр

Можно было бы привязать уже существующий server:latest образ в нашему реестру, но это примерно такой же объём нажатий клавиш, что и для создания нового образа, так что лучше соберём новый:

Тэг образа начинается с адреса реестра, так что Docker сразу догадается, куда его нужно отправить:

Теоретически, наш образ теперь прячется в Swarm реестре. Сейчас мы это проверим.

Создаём и масштабируем сервис

Момент истины:

Карта кластера:

replicated node server

Муа-ха-ха! Писание не соврало, и из локального реестра worker машины смогли скачать себе образ без проблем. Подход работает.

Мораль

Использовать кастомные Docker образы в Swarm чуть сложнее, чем в одиночном Docker. Если только мы не раскидали свой образ по всем хостам облака, что даже звучит сложно, с запуском и масштабированием сервиса на его основе будут сюрпризы. Проблема исчезает, если использовать свой собственный локальный реестр. Если не бояться SSL, то разворачивается он относительно просто даже с самописными сертификатами.

Если же образы будут создаваться и использоваться только внутри кластера, то можно даже не заморачиваться SSL и использовать простой HTTP реестр и обращаться к нему по localhost со всех нодов кластера. Это тоже работает.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *