Сегодня я немного расскажу о том, какие есть мысли по поводу фейловера в tarantool/cartridge. Сначала пару слов про то, что такое cartridge: это кусок lua-кода, который работает внутри tarantool и объединяет тарантулы друг с другом в один условный «кластер». Это происходит за счет двух вещей:

  • каждый тарантул знает сетевые адреса всех других тарантулов;
  • тарантулы регулярно «пингуют» друг друга через UDP, чтобы понять кто жив, а кто нет. Тут я намеренно немного упрощаю, алгоритм пинга сложнее чем просто request-response, но это для разбора не сильно принципиально. Если интересно — погуглите алгоритм SWIM.

Внутри кластера все обычно разделяется на тарантулы «с состоянием» (master/replica) и «без состояния» (router). Тарантулы «с состоянием» ответственны за хранение данных, а тарантулы «без состояния» — за маршрутизацию запросов.

Вот как это выглядит на картинке:



Клиент делает запросы к любому из активных роутеров, а те перенаправляют запросы на тот из стораджей, который сейчас является активным мастером. На картинке эти пути показаны стрелочками.

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

Сначала поговорим о том, как узлы узнают адреса друг друга. Для этого у каждого из них на диске лежит yaml-файл с топологией кластера, то есть с информацией о сетевых адресах всех членов, и кто из них кем является (с состоянием или без). Плюс потенциально дополнительной настройки, но пока это тоже оставим в стороне. Конфигурационные файлы содержат настройки всего кластера целиком, и одинаковы для каждого тарантула. Если в них вносятся изменения — то вносятся синхронно для всех тарантулов.

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

Сценарий — отказ узлов, переключение


В ситуации когда отказывает роутер, все более или менее просто: клиенту достаточно пойти в любой другой активный роутер, и он доставит запрос до нужного стораджа. Но как быть, если упал, например, мастер одного из стораджей?

Прямо сейчас у нас для такого случая реализован «наивный» алгоритм, который опирается на UDP ping. Если реплика в течение небольшого промежутка времени не «видит» откликов от мастера на ping, она считает, что мастер упал, и сама становится мастером, переключаясь в read-write режим из read-only. Роутеры действуют точно так же: если они не видят некоторое время отклика на ping от мастера, они переключают трафик на реплику.

Это работает относительно неплохо в простых случаях, кроме ситуации «split brain», когда половина узлов оказывается отделено от другой какой-то сетевой проблемой:



В этой ситуации роутеры увидят, что «другая половина» кластера недоступна, и будут считать свою половину основной, и получится, что в системе одновременно два мастера. Это первый важный кейс, который нужно решить.

Сценарий — редактирование конфигурации при сбоях


Еще один важный сценарий — это замена вышедшего из строя тарантула в кластере на новый, или добавление в кластер узлов, когда кто-то из реплик или роутеров недоступен.

При штатной работе, когда все в кластере доступны, мы можем подключиться через API к любому узлу, попросить его отредактировать конфигурацию, и, как я уже говорил выше, узел «раскатает» новую конфигурацию на весь кластер.

Но когда кто-то недоступен, применять новую конфигурацию нельзя, потому что когда эти узлы снова станут доступны, будет непонятно, кто из них кластера обладает правильной конфигурацией, а кто — нет. Еще недоступность узлов друг для друга может означать, что между ними есть split brain. И редактировать конфигурацию просто небезопасно, потому что можно по ошибке ее отредактировать по-разному в разных половинках.

По этим причинам мы сейчас запрещаем редактировать конфигурацию через API, когда кто-то недоступен. Поправить ее можно только на диске, через текстовые файлы (вручную). Тут вы должны хорошо понимать, что вы делаете и быть очень внимательными: автоматика уже ничем не поможет.

Это делает эксплуатацию неудобной, и это второй кейс, который нужно решить.

Сценарий — стабильный фейловер


Еще одна проблема наивной модели фейловера в том, что переключение с мастера на реплику в случае отказа мастера нигде не записывается. Все узлы принимают решение о переключении самостоятельно, и когда мастер оживет, на него снова переключится трафик.

Это может как быть, так и не быть проблемой. Мастер перед включением обратно «догонит» с реплики транзакционные логи, поэтому большого отставания по данным скорее всего не случится. Проблема будет только в том случае, когда есть неполадки в сети, и есть потеря пакетов: тогда скорее всего будет периодическое «мигание» мастера (flapping).

Решение — «сильный» координатор (etcd/consul/tarantool)


Для того чтобы избежать проблем со split brain, и дать возможность редактировать конфигурацию при частичной недоступности кластера, нам нужен сильный координатор, который устойчив к сегментации сети. Координатор должно быть можно распределить по 3-м датацентрам, чтобы при отказе любого из них сохранять работоспособность.

Сейчас есть 2 популярных координатора, основанных на RAFT, которые для этого используют — etcd и consul. Когда в tarantool появится синхронная репликация, его тоже можно будет для этого использовать.



В такой схеме инсталляции тарантула разделены по двум датацентрам, и подключаются к своей локальной инсталляции etcd. Экземпляр etcd в третьем датацентре служит как арбитр для того, чтобы в случае отказа одного из датацентров, точно сказать кто из них остался в большинстве.

Управление конфигурацией с сильным координатором


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

В случае с сильным координатором все проще: мы можем хранить конфигурацию на координаторе, а каждый экземпляр тарантула будет содержать у себя на файловой системе кэш этой конфигурации. При успешном подключении к координатору он будет обновлять свою копию конфигурации на ту, что есть в координаторе.

Редактирование конфигурации тоже становится проще: это можно сделать через API любого тарантула. Он возьмет блокировку в координаторе, заменит нужные значения в конфигурации, дождется пока все узлы ее применят, и отпустит блокировку. Ну или в крайнем случае можно будет отредактировать конфигурацию в etcd вручную, и она применится ко всему кластеру.

Редактировать конфигурацию можно будет даже если какие-то тарантулы недоступны. Главное, чтобы было доступно большинство узлов координатора.

Фейловер с сильным координатором


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

Алгоритм фейловера меняется вот как:

  1. Один из тарантулов в кластере выигрывает роль «главного» при помощи механизма блокировок в координаторе.
  2. Если главный тарантул видит через UDP-пинг, что кто-то из мастеров умер, он начинает процесс фейловера.
  3. Дальше он принимает решение о том, кто должен быть новым мастером.
  4. Пишет нового мастера в координатор.
  5. Все тарантулы в кластере получают уведомление об изменении конфигурации в координаторе и меняют свои роли соответственно, переходя в read-only или read-write.
  6. Если главный тарантул падает или оказывается отрезан по сети от других датацентров, он теряет роль главного, и через координатор проходят перевыборы.

С координатором также возможна защита от flapping-а. В координаторе можно фиксировать всю историю переключений, и если в течение последних X минут было переключение мастера на реплику, то обратное переключение делается только явно админом.

Еще один важный момент — это так называемый «Fencing». Тарантулы, которые оказываются отрезаны от других датацентров (либо подключены к координатору, потерявшему большинство), должны предполагать, что скорее всего оставшаяся часть кластера, к которой потерян доступ, имеет большинство. И значит в течение определенного времени все отрезанные от большинства узлы должны перейти в read-only. 

Проблема с недоступностью координатора


Пока мы обсуждали подходы к работе с координатором, мы получили просьбу сделать так, чтобы если координатор падает, но все тарантулы целые, не переводить весь кластер в read only.

Сначала казалось, что сделать это не очень реально, но потом мы вспомнили, что кластер сам следит за доступностью других узлов через UDP-пинги. Это означает, что мы можем ориентироваться на них и не запускать перевыборы мастера внутри репликасета, если через UDP-пинги видно, что весь репликасет жив.

Такой подход поможет меньше беспокоиться о доступности координатора, особенно если нужно его например перезагрузить для обновления.

Планы на реализацию


Сейчас мы собираем обратную связь и приступаем к реализации. Если вам есть что сказать — пишите в комментарии или в личку.

План примерно вот такой:

  • Сделать поддержку etcd в tarantool [готово]
  • Фейловер с использованием etcd как координатора, с фиксацией состояния [готово]
  • Фейловер с использованием тарантула как координатора, с фиксацией состояния [готово]
  • Хранение конфигурации в etcd [в процессе]
  • Написание CLI-инструментов для ремонта кластера [в процессе]
  • Хранение конфигурации в тарантуле
  • Управление кластером при недоступности части кластера
  • Fencing
  • Защита от flapping–а
  • Фейловер с использованием consul как координатора
  • Хранение конфигурации в consul

В будущем мы почти наверняка будем полностью отказываться от работы кластера без сильного координатора. Скорее всего это совпадет с реализацией синхронной репликации в тарантуле на базе RAFT.

Благодарности


Спасибо разработчикам из Почты Mail.ru и админам за предоставленный фидбэк, критику и тестирование.