Как использовать ZeroMQ с Docker

В прошлый раз мы написали аж три примера клиент-сервер Node.js приложений, в которых компоненты общаются между собой через ZeroMQ. Примеры простые, показывают основную идею, но работают всё-таки на localhost, и поэтому слабо пересекаются с реальностью, в которой ZeroMQ обычно работает. И тогда я подумал, а почему бы не раскидать клиента и сервера по Docker-контейнерам? В задачу добавятся новые вопросы, она станет больше похожа на правду, и главное, это отличный шанс откатать сразу несколько инструментов, которые обычно используются вместе.

Итак, задача на сегодня: взять fire-and-forget паттерн из прошлого поста и доработать его до контейнерного приложения.

План

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

И клиент:

Сервер отправляет сообщение «Ping» раз в две секунды, а клиент с благодарностью получает. Не совсем шедевр, но для примера подойдёт. С ходу, вот, что придётся доработать:

  • Первая очевидная проблема — статически заданный IP адрес. 127.0.0.1  внутри контейнера — это прямой билет в одиночество. Даже если я соглашусь оставить 3000 порт и скажу серверу «слушать» на всех сетевых интерфейсах ( tcp://*:3000 ), всё равно остаётся клиент, которого нужно куда-то отправить. Другими словами, адрес нужно параметризировать.
  • Чтобы положить что-то в контейнер, нужно сначала сделать из него образ. А чтобы сделать образ, нужен Dockerfile. В нашем случае — два. Сами они себя не сделают, так что придётся заняться.
  • Чтобы передать клиенту адрес сервера, этот адрес нужно сначала как-то узнать. А он — динамический. С другой стороны, если дать контейнерам внятные имена и подключить их к пользовательской сети, то про IP можно забыть и общаться просто по имени. Волею богов контейнеризации, есть docker-compose, который и имя задаст, и к сети подключит. Конечно, всё это можно было бы сделать и руками, но зачем?

Итак, приступим.

Параметризируем клиентский адрес для подключения

Контейнера, Docker и docker-compose очень легко передают друг другу переменные среды (environmental variables). С другой стороны, прочитать эти переменные из Node.js — вообще не проблема. Всё это выглядит как отличный способ передать адрес сервера клиенту. Добавим переменную, какой-нибудь   console.log для отладки, и готово:

На всякий случай сделаем то же самое для сервера. Вдруг пригодится:

И… всё. Приложение готово.

Создаём Dockerfile

Если кто забыл, Dockerfile описывает шаги, как создать новый образ. Прежде чем мы начнём колдовать, стоит раскидать клиента и сервера по отдельным папкам, чтобы они не мешались друг у друга под ногами. Например, так:

Структура папок

Вообще Dockerfile будет вполне себе тривиальным: берём образ с node, кладём в него файлы приложения, устанавливаем зависимости, открываем порт и запускаем клиента/сервера на старте. Для сервера у меня вышло вот что:

Установка зависимостей ( RUN ... ) — единственный относительно сложный шаг. Во-первых, я хочу уже в самом контейнере переустановить все NPM модули. Для этого я удаляю существующие (первая строка RUN) и устанавливаю их заново (четвертая). Затем, под линуксом у ZeroMQ есть зависимость — libzmq-dev , но так просто её не поставить, потому что node образ настолько пустой внутри, что   apt-get  не в курсе, откуда устанавливать пакеты, так что его самого надо сначала обновить. Для этого — вторая и третья строка. Попробуем всё собрать и запустить:

Вроде работает. Чтобы сделать клиентский Dockerfile, нужно взять серверный и заменить server.js на client.js. Пусть это будет домашним заданием.

Собираем docker-compose.yml файл

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

Вообще ничего сложного. Кладём конфигурацию в корень проекта и запускаем:

Ха! Древняя чёрная магия всё еще действует. Что действительно круто: если мне захочется добавить еще клиентов и серверов, чтобы посмотреть, как они будут взаимодействовать, я просто добавлю пару строк в docker-compose.yml и перезапущу его.

Мораль Итог Капитан Очевидность

Мы быстро прошлись по тому, как сконвертировать «обычное» ZeroMQ приложение в контейнерное. Всего-то нужно было параметризировать адреса подключения, создать Dockerfile для клиента и сервера и конфигурацию для docker-compose. Можно было бы обойтись и без последнего, но тогда бы пришлось руками создавать и удалять сеть, подключать к ней контейнеры, а это выливается к длиннющие shell команды и вообще скучно. Посмотреть код примера целиком можно тут.

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

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