Требуется разработать простой менеджер кластера, состоящего из 2-х узлов и работающего в режиме Active / Passive (т.е. в любой момент времени работает только один узел, второй простаивает, ожидая отказа первого). Типичные сценарии использования можно найти здесь – http://git.altlinux.ru/people/enp/packages/docs-linux_ha_openvz-enp.git (пока в Te X, как только в Сизифе появится tex4ht, на heap.altlinux.ru автоматически появится html), еще можно заглянуть сюда – /Software / High Availability
Основная работа менеджера кластера заключается в изменениии состояния узла (сопровождающегося вызовом скриптов, определенных пользователем) на основании наличия/отсутствия/изменения состояния соседнего узла. Возможные состояния узла: CONNECTING, PRIMARY, SECONDARY, STANDALONE, ERROR (используется терминология DRBD7, т.к. DRBD7 – идеальный выбор для хранения данных и конфигурации кластеризуемых сервисов в случае Active / Passive).
Узлы обмениваются UDP-пакетами, в которых содержится hostname, timestamp и текущее состояние узла. Шифровать пакеты не обязательно, т.к. под внутрикластерное соединение принято отводить отдельную сеть.
Реагировать на сигнал TERM, сохранять информацию о полученном сигнале
Анализировать содержимое последнего принятого UDP-пакета и информацию о полученных сигналах, на основании чего изменять состояние узла (в т.ч. завершать работу)
Далее приведена таблица изменения состояний: по горизонтали – первоначальное состояние, по вертикали – внешняя информация (последний пришедший UDP-пакет, таймаут либо сигнал), на пересечении – новое состояние. Соответственно, ссылки на ячейки по именам выглядят как HORIZONTAL/VERTICAL.
Первоначальное состояние /
Внешняя информация
CONNECTING
PRIMARY
SECONDARY
STANDALONE
ERROR
CONNECTING
Выполняется лексикографическое сравнение hostname: если наш узел больше, то PRIMARY, если меньше, то ничего не делать, а подождать пакета PRIMARY и действовать как описано в ячейке CONNECTING/PRIMARY
SAME
ERROR или SECONDARY
PRIMARY
SAME
PRIMARY
SECONDARY
ERROR или Выполняется лексикографическое сравнение hostname: если наш узел меньше, то SECONDARY
SAME
ERROR или SECONDARY
SAME
SECONDARY
ERROR или PRIMARY
SAME
ERROR или Выполняется лексикографическое сравнение hostname: если наш узел больше, то PRIMARY
ERROR или PRIMARY
SAME
STANDALONE
SAME (можно ничего не делать, а подождать, пока STANDALONE не станет PRIMARY, а тогда действовать как описано в ячейке CONNECTING/PRIMARY)
ERROR или SAME
ERROR или SECONDARY
ERROR или Выполняется лексикографическое сравнение hostname: если наш узел больше, то PRIMARY, если меньше, то можно ничего не делать, а подождать, пока STANDALONE не станет PRIMARY, а тогда действовать как описано в ячейке CONNECTING/PRIMARY
SAME
ERROR
PRIMARY
SAME
STANDALONE
SAME
SAME
Timeout (последний UDP-пакет был получен более чем T секунд назад)
STANDALONE
STANDALONE
STANDALONE
SAME
SAME
TERM
Завершение работы
SECONDARY и завершение работы
Завершение работы
Завершение работы
Завершение работы
Для выбора нового состояния в ячейках используется принцип наименьшего количества изменений, т.е. если узел уже находится в состоянии PRIMARY, то, если есть такая возможность, именно в этом состоянии его и следует оставить (именно это означает SAME или пояснение «ничего не делать, а подождать ...»).
Если в ячейке описаны 2 варианта поведения [ ... или ... ], выбор варианта зависит от одного из двух возможных режимов работы менеджера кластера: пытаться автоматически разрешить неправильные ситуации или просто переводить узел в в состояние ERROR. Если hostname узлов оказываются лексикографически равны, то узел необходимо переводить в состояние ERROR независимо от режима работы.
Перед переходом из одного состояния в другое должен вызываться определенный пользователем скрипт, принимающий в качестве параметров старое и новое состояние. Если скрипт вернул значение, отличное от 0, необходимо перевести узел в состояние ERROR. Изменение состояния узла должно протоколироваться.
Может быть полезен специальный механизм мониторинга корректности функционирования сервисов узла в составе менеджера кластера (как это сделано в HAD) в виде периодически вызываемого пользовательского скрипта – если скрипт вернул значение, отличное от 0, необходимо также перевести узел в состояние ERROR и запротоколировать это событие.
Выше для простоты предполагалось, что между узлами кластера имеется только один линк, однако для надежного определения отказа соседнего узла необходимо иметь как минимум 2 линка на случай отказа самого линка. В этом случае состояние соседнего узла предлагается определять так: учитывать только последние UDP-пакеты с тех узлов, которые пришли не позже, чем T секунд назад, и только в случае отсутствия таковых считать, что соседний узел действительно отказал. Если состояния разные, то возможны различные стратегии: всегда переводить узел в состояние ERROR, определять приоритеты линков и учитывать только состояние того линка из оставшихся в живых, чей приоритет выше, и т.д. В любом случае необходимо протоколировать отказы линков (и вызывать соответствующий скрипт, передавая ему в качестве параметра имя линка) точно так же, как и изменение состояния узла.