Создаём RabbitMQ кластер

Как и обещал в прошлый раз, сегодня мы попробуем какую-нибудь из продвинутых фич RabbitMQ. Например, кластеризацию.

RabbitMQ кластер это сразу несколько сервисов, у которых общие пользователи, настройки и даже очереди. Сервисы могут добавляться и удаляться на лету, располагаться на разных краях континента, но для подключённого клиента они будут выглядеть как один большой RabbitMQ сервис. Это хорошо для горизонтального мастшабирования —  когда клиентов становится так много, что одиночному брокеру уже не справиться.

Кластеризация — это не то же самое, что репликация и high availability. В первом и втором случае уход одного из узлов в оффлайн никак не повлияет на доступность данных и работу сервиса в принципе. В кластере же узлы не взаимозаменяемы. Да, пользователи и настройки действительно будут дублироваться на каждом их них, где бы тех не создавали. Но очереди сообщений — нет. Так что если какой-то хост ушёл в оффлайн, то его очереди пойдут следом.

Как создать RabbitMQ кластер

Есть несколько способов, но мы будем создавать руками. Чтобы объединить несколько узлов в кластер, нужно удивительно мало телодвижений. А именно — два:

  1. Всем узлам будущего кластера нужно задать одинаковую Erlang cookie.
  2. У всех узлов, кроме случайного первого, нужно вызвать команду  rabbitmqctl join_cluster .

Erlang cookie — это просто строка, которая в никсовых системах хранится в /var/lib/rabbitmq/.erlang.cookie. Когда cookie одинаковый, узлы понимают, что теперь они товарищи, и могут общаться. Команда  join_cluster  принимает один параметр — имя узла, с которым будем заводить кластер, и завершает союз. Обычно имя похоже на  rabbit@hostname .

Подготавливаем RabbitMQ узлы в Docker

Так как для кластера нужно хотя бы два независимых хоста, самый простой способ получить их — запустить два Docker контейнера с RabbitMQ внутри. В Docker Hub на выбор есть сразу два образа: rabbitmq и rabbitmq:management. Я буду использовать второй, потому что в нём уже есть вэб-админка.

Еще один момент. Чтобы контейнеры могли общаться между собой, они должны находиться в общей сети, иметь внятные имена, да и Erlang cookie тоже надо как-то передать. Проще всего это сделать через  docker-compose . Если создать файл конфигурации, то он сам будет присваивать имена, разбираться с сетью, передавать параметры, запускать сервисы, и т.п.

Конфигурируем docker-compose

Как всегда, конфигурация для docker-compose берется из файла docker-compose.yml. Им и займёмся:

Он чуть-чуть длиннее, чем стандартный hello world, но честно-честно, такой же элементарный. Я объясню, что в нём задано:

  1. Два сервиса (контейнера), которые после запуска превратятся в RabbitMQ узлы с именами rabbit и hamster. Хомяк (hamster) имеет очень опосредованное отношение к кроликам (rabbit), но вы не представляете, как утомительно гуглить кроличьи семейства ночью.
  2. Имена контейнерных хостов (hostname) заданы явно. Они будут фигурировать в именах RabbitMQ узлов и лучше бы им быть читабельнее.
  3. Так как RabbitMQ вэб-админка будет выглядывать из контейнера через порт 15672, я открою его и со стороны хоста. Но контейнеров два, так что второй, с хомяком, будет доступен снаружи через порт 15673.
  4. В официальный образ rabbitmq можно передать Erlang cookie в качестве переменой окружения — RABBITMQ_ERLANG_COOKIE, и тогда не нужно делать это руками через .erlang.cookie файл. Очень удобно. Свою cookie я назвал ‘mysecret’, но подошло бы что угодно.

Запускаем сервисы

docker-compose.yml готов, так что можно запускать:

Будет очень много логов, среди которых легко не заметить, что оба RabbitMQ сервиса успешно запустились. Прежде чем объединить их в кластер, стоит проверить, работает ли вэб-админка.

Порт мы уже знаем — 15672 для rabbit и 15673 для hamster, но IP адрес зависит от версии Docker и операционной системы. Это будет либо localhost, либо тот IP, который вернёт  docker-machine ip  или  boot2docker ip . Логин и пароль от админки — guest:guest.

RabbitMQ: одиночный узел

Ну что, админка от rabbit работает, и я уверен, что со второй тоже всё хорошо.

Запускаем кластер

Время делать кластер. Второй и последний шаг в создании кластера — вызов команды  rabbitmqctl join_cluster на всех брокерах, кроме первого. В нашем случае «все, кроме первого» — это hamster (или rabbit — смотря, кто больше нравится). docker-compose постарался сделать имя контейнера уникальным и добавил свой префикс, так что прежде чем зайти в него, нужно узнать настоящее имя:

cluster_hamster_1. Могло быть и хуже. Заходим:

И творим кластер:

Та-дам! Кластер готов. Проверим, изменилось ли что в админке:

RabbitMQ: кластер

Теперь rabbit говорит, что он — на самом деле это два узла: rabbit и hamster. Это действительно кластер.

Кластерные развлечения

Создадим чего-нибудь на одном из узлов. Например, на rabbit. Какую-нибудь очередь сообщений, (‘demoqueue’), и пользователя (‘rabbit’):

Rabbit: создаём очередь
Rabbit: создаём очередь
Rabbit: создаём нового пользователя
Rabbit: создаём нового пользователя

Теперь идём на админку hamster’а — 127.0.0.1:15673, и смотрим, что творится там. Оказывается, новая очередь вместе с новым пользователем при помощи богов стека TCP/IP перенеслись и сюда:

Hamster: очереди сообщений
Hamster: очереди сообщений

Возле имени очереди есть пометка, что на самом деле она хостится на узле по имени rabbit, но пользователь — самый что ни на есть настоящий.

Hamster: пользователи

Теперь попробуем еще одну штуку — остановим rabbit:

Если снова посмотреть на пользователей hamster’а, то ничего особо там не изменится — всё еще две штуки. Но вот возле demoqueue появилась пометка down.

Hamster: demoqueue is down

Это именно то, о чём я упоминал сначала. Пользователи и настройки копируются между узлами. Очереди — нет.

И еще одно наблюдение — в кластере нет понятия «главного» узла. Мы только что остановили контейнер, к которому подключались «остальные» (целая одна штука), но кластер — всё еще кластер. Все узлы равны, и их можно останавливать и запускать в любом порядке. Кроме одного исключения. Если останавливать все сервисы кластера по одному, то тот, который остановился последним, должен стартовать первым, когда мы начнём включать их назад.

Итого

Кластеризация — это очень неблагодарная задача, если пытаться написать её самому. Но создать кластер с RabbitMQ —  вообще не проблема. Нужно просто передать строку-параметр и выполнить одну команду.

Кроме создания кластера руками или через RabbitMQ плагины есть еще один способ: в Docker Hub и github добрые люди выкладывают образы, которые уже сконфигурированы для работы в кластере. Например, docker-rabbitmq-cluster. Кроме Erlang cookie параметра они принимают на вход имя RabbitMQ контейнера, с которым нужно объединиться. Так что создать целый кластер машин можно через docker-compose.yml файл и одну команду.

3 комментария для “Создаём RabbitMQ кластер

  1. Спасибо за статью.
    Подскажите, довадилось ли сталкиваться с проблемой network partition , и если сталкивались, как ее решить ?

  2. копириование очередей задаётся политикой
    rabbitmqctl set_policy ha-all «» ‘{«ha-mode»:»all»,»ha-sync-mode»:»automatic»}’

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

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