HA Cluster

статья устарела и более не актуальна, не ломайте себе мозг :-)

Задача - построить кластер высокой доступности состоящий из двух нод, используя Heartbeat для контроля кластера. В качестве распределенного сетевого хранилища будем использовать GlusterFS, хотя можно применить и DRBD. Что бы не замачиваться с отдельными сервисами, все задачи разместим в виртуальной машине (машинах) VirtualBox и будем запускать их на нодах. Все делается на ОС ALTLinux branch p5.

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

В случае отказа ноды DELL2 сработает механизм Heartbeat и виртуальная машина vpc1 запустится на ноде DELL1, благо файловая система /gfs у них общая.

В случае если наоборот выйдет из строя нода DELL1, то виртуальная машина mserver запустится на ноде DELL2.

Теперь попробуем все это реализовать в конфигах.

1. Распределенное сетевое файловое хранилище.

Во всех статьях, которые описывают создание HA-кластеров описывают настройку DRBD, обычно блочное устройство работает в режиме master-slave, т.е. на основной рабочей ноде смонтирована файловая система, а на вторую ноду просто реплицируются данные. В случае отказа основной ноды блочное устройство делается мастером на резервной ноде, где и монтируется файловая система. Получается, что одна нода всегда будет простаивать, что не очень разумно. В режиме же master-master, который появился в новых версиях DRBD запускать кластер пока опасно, больно уж сырая технология. К тому же проведя несколько опытов, даже в режиме master-slave, иногда были проблемы, т.е. система не переключалась на резервную ноду, статус DRBD выдалава secondary-unnknown или как-то так, что меня насторожили и пока я отказался от использования DRBD. Попытки были как на ALTLinux так и на Debian.

В итоге я применил GlusterFS для создания хранилища, хотя многие кричат, что она совсем еще сырая и использовать в продакшене ее нельзя. Я попробовал, пока все работает, сейчас тестирую работу кластера, пока не наблюдал ни brain split ни других проблем.

У меня на серверах по два сетевых интерфейса так вот интерфейсы eth0 у меня смотрят в ЛВС, а интерфейсы eth1 каждого сервера соединены крос-овером, таким образом мы имеем соединение между серверами 1 Gbps. Вот настройки этих интерфейсов:

на сервере dell1

[root@dell1 eth1]# cat /etc/net/ifaces/eth1/options

TYPE=eth
BOOTPROTO=static

[root@dell1r eth1]# cat /etc/net/ifaces/eth1/ipv4address

192.168.100.1/29

на сервере dell2 файл options такой же, а файл ipv4address:

192.168.100.2/29

Маска /29 так как мне необходимо иметь доступ к GlusterFS из виртуальных машин, у которых организуется bridge с интерфейсом eth1 хост системы.

Может быть не хуже было бы объеденить интерфейсы eth0 и eth1 на каждом из серверов в бондинг, но у меня в данное время в этом месте нет нормальных коммутаторов.

На каждом из серверов /var/export вынесен в отдельный раздел одинакового размера.

На обоих серверах файлик сервера выглядит одинаково.

[root@dell1 glusterfs]# cat /etc/glusterfs/server.vol

server.vol
volume posix
type storage/posix
option directory /var/export
end-volume
volume locks
type features/locks
subvolumes posix
end-volume
volume brick
type performance/io-threads
option thread-count 8
subvolumes locks
end-volume
volume server
type protocol/server
option transport-type tcp
option auth.addr.brick.allow *
subvolumes brick
end-volume

Конфиг клиенской части на сервере dell1

[root@dell1 glusterfs]# cat /etc/glusterfs/client.vol

volume remote1
type protocol/client
option transport-type tcp
option remote-host 192.168.100.1
option remote-subvolume brick
end-volume
volume remote2
type protocol/client
option transport-type tcp
option remote-host 192.168.100.2
option remote-subvolume brick
end-volume
volume replicate
type cluster/replicate
subvolumes remote1 remote2
end-volume
volume writebehind
type performance/write-behind
option window-size 1MB
subvolumes replicate
end-volume
volume cache
type performance/io-cache
option cache-size 512MB
subvolumes writebehind

Конфиг клиентской части на сервере dell2

[root@dell2 ~]# cat /etc/glusterfs/client.vol

volume remote1
type protocol/client
option transport-type tcp
option remote-host 192.168.100.2
option remote-subvolume brick
end-volume
volume remote2
type protocol/client
option transport-type tcp
option remote-host 192.168.100.1
option remote-subvolume brick
end-volume
volume replicate
type cluster/replicate
subvolumes remote1 remote2
end-volume
volume writebehind
type performance/write-behind
option window-size 1MB
subvolumes replicate
end-volume
volume cache
type performance/io-cache
option cache-size 512MB
subvolumes writebehind

Тут можно поиграться с параметрами cache-size и windows-size, я пока не игрался :-)

Запускаем service glusterfsd start на обоих нодах. Если все удачно, настраиваем автозапуск:

chkconfig glusterfsd on

Далее делаем директории /gfs и монтируем через fstab на каждой ноде

/etc/glusterfs/client.vol  /gfs  glusterfs  defaults  0  0

Казалось бы все красиво, ан нет, фиг оно монтируется при загрузке, потому-что сети еще не видит и демон glusterfsd еще не запущен, поэтому я пока склепал вот такой скрипт:

[root@dell1 init.d]# cat /etc/init.d/gfsm

#!/bin/sh
#
# chkconfig: 35 36 64
# description: GlusterFS
#
WITHOUT_RC_COMPAT=1
LOCKFILE=/var/lock/subsys/gfsm
# source function library
. /etc/init.d/functions
RETVAL=0
start()
{
    umount /gfs
    mount /gfs
    success
}
stop()
{
    umount /gfs
    success
}
case "$1" in
    start)
    start
    ;;
    stop)
    stop
    ;;
    status)
    exit
    ;;
    *)
    msg_usage "${0##*/} {start|stop}"
        RETVAL=1
esac
exit $RETVAL

# chkconfig --add gfsm

# chkconfig gfsm on

# service gfsm start

Вот все и с смонтировалось и при загрузке автоматом монтируется тоже.

Проводим несколько тестов, копируя файлики на /gfs и с нее. У меня скорость работы FS снижалась максимум до 20 мегабайт в секунду.

И так когда GlusterFS работает, продолжаем ...

2. VirtualBox

Для виртуальной машины делаем пользователя vbox от которого и будут запускаться наши виртуальные машины, важно что бы у пользователя были одинаковые UID и GID на обоих серверах. Пользователь vbox должен входить в группу vboxusers. Устанавливаем VirtualBox, я использую VirtualBox версии 3.2 с сайта разработчика (уже от Oracle). Запускаем морду Virtualbox от пользователя vbox. Запускаем используя X11 Forvawding, на серверах иксы не нужны.

Делаем виртуальную машину, образ которой располагаем в /gfs в данном случае машина называется mserver

Обязательно включаем vrdp сервер.

т

Обратите внимание, что если машин несколько, то порт каждой машины должен быть разный.

В зависимости от железа влюкчаем AMD-V или Intel-VT, и другие фишки, выключаем звук и USB. Сеть настраиваем в режиме бриджа, я использую

два виртуальных интерфейса, что бы одним видеть ЛВС, а другим достучаться до GluterFS, ведь на ней можно положить не только образы виртуальных машин, но

и любые другие данные.

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

Теперь можно запустить гостевую машину на одной из нод и установить туда операционную систему.

3. Heartbeat

Устанавливаем пакет (у меня heartbeat-2.1.3-alt3). Делаем простой скриптик, что бы сгенерировать файлы авторизации:

[root@dell1 ha.d]# cat genkey

( echo -ne "auth 1\n1 sha1 "; \
dd if=/dev/urandom bs=512 count=1 | openssl md5 ) \
> /etc/ha.d/authkeys
chmod 0600 /etc/ha.d/authkeys

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

Главный конфигурационный файл, на первой ноде выглядит так :

[root@dell1 ha.d]# cat /etc/ha.d/ha.cf

ucast eth1 192.168.100.2
auto_failback on
node dell1.domain.com dell2.domain.com

На второй ноде так:

[root@dell2 ha.d]# cat /etc/ha.d/ha.cf

ucast eth1 192.168.100.1
auto_failback on
node dell1.domain.com dell2.domain.com

Теперь файл с ресурсами

[root@dell1 ha.d]# cat /etc/ha.d/haresources

dell1.domain.com vbox::mserver
dell2.domain.com vbox::vpc1

Как видно в нормальном режиме Heartbeat запускает виртуальную машину mserver на ноде dell1 и машину vpc1 на ноде dell2

Останов и запуск управляется скриптом (одинаковый на обоих нодах):

[root@dell1 ha.d]# cat /etc/ha.d/resource.d/vbox

#!/bin/sh
case $2 in
    start)    /opt/vbsinglem $1 start
    ;;
    stop)     /opt/vbsinglem $1 stop
    ;;
    status)
    exit 0
    ;;
    *)        echo err
    ;;
esac

Этот скрипт принимает от heartbeat два параметра, в $1 будет имя машины с которой нужно что-то делать, а в $2 будет команда, в нашем случае это только stop или start. Далее эту же команду скрипт передает другому сценарию /opt/vbsinglem который умеет, запускать и останавливать отдельные виртуальные машины.

Вот этот скрипт:

[root@dell1 ha.d]# cat /opt/vbsinglem

#!/bin/sh
# (c) Yury Konovalov aka Speccyfan (2:453/53)
VBOXDIR="/etc/vbox"
VM_USER="vbox"
VM="$1"
# source function library
. /etc/init.d/functions
RETVAL=0
export PATH="${PATH:+$PATH:}/bin:/usr/bin:/usr/sbin:/sbin:/opt/VirtualBox"
if [ -f $VBOXDIR/vbox.cfg ]; then
    . $VBOXDIR/vbox.cfg
else
    echo "ERROR: $VBOXDIR/vbox.cfg does not exist. Exiting."
    exit 1
fi
SU="su - $VM_USER -c"
VBOXMANAGE="VBoxManage -nologo"
TIMER=/tmp/vboxtimer.$VM
# Check for running machines every few seconds; return when all machines are
# down
wait_for_closing_machines() {
    RUNNING_MACHINES=`$SU "$VBOXMANAGE list runningvms" | grep $VM | wc -l`
    if [ $RUNNING_MACHINES != 0 ]; then
        sleep 5
        T=`cat $TIMER`
        let T="$T+1"
        echo $T > $TIMER
        #echo $T
        if [ $T = "60" ]; then
    #killing machines
    echo \"acpipowerbutton\" timeout
    echo Forcing \"poweroff\" ...
    echo 0 > $TIMER
            $SU "$VBOXMANAGE controlvm \"$VM\" poweroff"
        fi
        wait_for_closing_machines
    fi
}
start()
{
        if [ $VM != "" ]; then
                $SU "$VBOXMANAGE startvm \"$VM\" -type vrdp"
        fi
    success
}
stop()
{
        $SU "$VBOXMANAGE controlvm \"$VM\" acpipowerbutton"
        echo 0 > $TIMER
        wait_for_closing_machines
        success
}
status()
{
    runvm=`$SU "VBoxManage list runningvms"| grep $VM| wc -l`
    if [ $runvm != "0" ]; then
    echo Virtual machine $VM is started
    else
    echo Virtual machine $VM is stoped
    fi
    return 0
}
case "$2" in
    start)
        start
    ;;
    stop)
        stop
    ;;
    status)
    status
    ;;
    *)
        msg_usage "${0##*/} machine_name {start|stop|status}"
        RETVAL=1
esac

Скрипт переделывался из скрипта написанного мной ранее для запуска машин через init.d поэтому может кое-что можно из него и выкинуть...

Вот собственно и все. Запускаем сначала первой машине service heartbeat start, затем это же на второй. Видим, одна машина должна запуститься на одной ноде, вторая на второй, в случае отказа одной из нод, машина должна запуститься на второй. Если сделать service heartbeat stop на одной ноде, то машина запущенная на ней должна остановиться, затем запуститься на второй.

не забываем так же добавить heartbeat в автозапуск

chkconfig heartbeat on

К виртуальным машинам можно подключиться например так rdesktop dell1.domain.com:3392 если машина запущена на первой ноде и dell2.comain.com:3392 если первая нода отказала и машина запущена на второй ноде. Если машин две и более, то нужно подключаться по разным портам (которые ранее настроены в виртуалбоксе).

Замечания при использовании win2k3 в качестве гостевой ОС

Т.к. выключение машины реализовано через эмуляцию нажатия на кнопку питания (acpipowerbutton), то на системах с windows 2003 необходимо сделать следующие настройки:

1. Пуск - Панель управления – Электропитание – Дополнительно - При нажатии кнопки выключения компьютера. Поставить - "Завершение работы".

2. Пуск – Выполнить - gpedit.msc. Конфигурация компьютера - Конфигурация Windows -Параметры безопасности - Локальные политики - Параметры безопасности - Завершение работы: Разрешить завершение работы системы без выполнения входа в систему. Установить "включено". Сразу предупреждаю, это небезопасно, т.к. кто-то может подключиться к главной консоли VM и выключить ее. Нужно ограничивать доступ посредством iptables или авторизацией на vrdp в VirtualBox. Но описывать эти нюансы не буду.

3. Пуск – Выполнить gpedit.msc. Локальный компьютер - Конфигурация компьютера - Административные шаблоны – Система - Отображать диалог слежения за завершением работы. Установить "отключено".

Но тем не менее, если в данный момент эту VM используют пользователи, система работу не завершит. Придется завершать их сессии. Данная проблема пока полностью не решена.

Еще замечание

Эту информацию вы используете на свой страх и риск. Я не несу ответственности если вы пытаясь повторить конфигурацию потеряете важные данные.