Требуется разработать простой менеджер кластера, состоящего из 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 и текущее состояние узла. Шифровать пакеты не обязательно, т.к. под внутрикластерное соединение принято отводить отдельную сеть.
Менеджер кластера должен параллельно выполнять следующие операции (реализовать в виде threads?):
Далее приведена таблица изменения состояний: по горизонтали – первоначальное состояние, по вертикали – внешняя информация (последний пришедший 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 | STANDALONE | STANDALONE | STANDALONE | SAME | SAME |
TERM | Завершение работы | SECONDARY и завершение работы | Завершение работы | Завершение работы | Завершение работы |
Для выбора нового состояния в ячейках используется принцип наименьшего количества изменений, т.е. если узел уже находится в состоянии PRIMARY, то, если есть такая возможность, именно в этом состоянии его и следует оставить (именно это означает SAME или пояснение «ничего не делать, а подождать ...»).
Если в ячейке описаны 2 варианта поведения [ ... или ... ], выбор варианта зависит от одного из двух возможных режимов работы менеджера кластера: пытаться автоматически разрешить неправильные ситуации или просто переводить узел в в состояние ERROR. Если hostname узлов оказываются лексикографически равны, то узел необходимо переводить в состояние ERROR независимо от режима работы.
Перед переходом из одного состояния в другое должен вызываться определенный пользователем скрипт, принимающий в качестве параметров старое и новое состояние. Если скрипт вернул значение, отличное от 0, необходимо перевести узел в состояние ERROR.
Я не вижу необходимости в специальном механизме мониторинга корректности функционирования сервисов узла в составе менеджера кластера (как это сделано в HAD). Этот механизм может быть внешним скриптом (или вообще monit задействовать), который будет по возможности не рапортовать о проблемах, а пытаться их устранить (перезапустить умершие сервисы), а если это совсем никак невозможно – отправлять менеджеру кластера сигнал TERM или вообще останавливать узел.
Выше предполагалось, что между узлами кластера имеется только один линк, однако оба узла смотрят еще и во внешнюю сеть, которую тоже можно использовать для коммуникации между узлами. Это может быть полезным, если внутрикластерное соединение по каким-либо причинам перестанет работать, и оба узла перейдут в состояние STANDALONE. Но в этом случае появится новая проблема: какому линку доверять больше, если показания разойдутся? Возможный способ решения проблемы: всю внешнюю информацию, кроме Timeout, анализировать только для одного доверенного линка (но сравнивать с другими и просто предупреждать, если есть разница в показаниях – а при падении этого линка передавать статус доверенного следующему – это должно настраиваться), Timeout же считать достоверным только если все линки в этом сойдутся.