СОЗДАЁМ RABBITMQ КЛАСТЕР

Thank you for reading this post, don't forget to subscribe!

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.

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

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

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

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

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

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

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

Кластерные настройки

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

созда­ём очередь

созда­ём ново­го пользователя

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

оче­ре­ди сообщений

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

поль­зо­ва­те­ли

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

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

demoqueue is down

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

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

Итого

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

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