Требуется разработать простой менеджер кластера, состоящего из 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 и текущее состояние узла. Шифровать пакеты не обязательно, т.к. под внутрикластерное соединение принято отводить отдельную сеть.
Менеджер кластера должен выполнять следующие операции (реализовать связанные с I / O? с помощью http://monkey.org/~provos/libevent/ ?):
Далее приведена таблица изменения состояний: по горизонтали – первоначальное состояние, по вертикали – внешняя информация (последний пришедший 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, определять приоритеты линков и учитывать только состояние того линка из оставшихся в живых, чей приоритет выше, и т.д. В любом случае необходимо протоколировать отказы линков (и вызывать соответствующий скрипт, передавая ему в качестве параметра имя линка) точно так же, как и изменение состояния узла.