Часть первая

Продолжаем


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

В прошлый раз мы закончили на том, что началась синхронизация DRBD.
Если мы в качестве Primary сервера для обоих ресурсов выбрали один и тот же сервер, то после завершения синхронизации должны в /proc/drbd увидеть примерно такую картину:
# cat /proc/drbd
version: 8.4.3 (api:1/proto:86-101)
GIT-hash: 89a294209144b68adb3ee85a73221f964d3ee515 build by root@debian-service, 2013-04-30 07:43:49
 0: cs:Connected ro:Secondary/Primary ds:UpToDate/UpToDate B r-----
    ns:0 nr:190397036 dw:190397036 dr:1400144904 al:0 bm:4942 lo:0 pe:0 ua:0 ap:0 ep:1 wo:d oos:0
 1: cs:Connected ro:Secondary/Primary ds:UpToDate/UpToDate B r-----
    ns:0 nr:720487828 dw:720485956 dr:34275816 al:0 bm:3749 lo:468 pe:0 ua:0 ap:0 ep:1 wo:d oos:0

Самое интересное поле тут ds:UpToDate/UpToDate, означающее что и локальная и удаленная копия актуальны.

После этого переведем ресурсы в secondary режим — дальше ими будет управлять кластер:
# drbdadm secondary VM_STORAGE_1
# drbdadm secondary VM_STORAGE_2

Pacemaker


Итак, менеджер кластера.

Если коротко, то это мозг всей системы, который управляет абстракциями, называемыми ресурсами.
Ресурсом кластера может быть, в принципе, что угодно: IP-адреса, файловые системы, DRBD-устройства, программы-службы и так далее. Довольно просто создать свой ресурс, что мне и пришлось сделать для управления iSCSI таргетами и LUN-ами, об этом далее.

Установим:
# apt-get install pacemaker

Corosync

Pacemaker использует инфраструктуру Corosync для взаимодействия между узлами кластера, поэтому для начала нужно будет настроить её.

Corosync имеет достаточно широкий функционал и несколько режимов для поддержки связи между нодами (unicast, multicast, broadcast), имеет подде��жку RRP (Redundant Ring Protocol), которая позволяет использовать несколько разных путей для общения между нодами кластера для минимизации риска получить Split-brain, то есть ситуации, когда связь между нодами полностью пропадает, и они обе считают что сосед умер. В результате обе ноды переходят в рабочий режим и начинается хаос :)

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

Приступим к настройке

Для начала нужно сгенерировать ключ авторизации:
# corosync-keygen

Его нужно положить под именем /etc/corosync/authkey на оба сервера.

Далее создадим конфиг, он должен быть идентичен на обоих нодах:

/etc/corosync/corosync.conf
compatibility: none

totem {
    version: 2
    
    secauth: on
    threads: 3
    
    rrp_mode: active
    
    transport: udpu
    
    interface {
        member {
            memberaddr: 10.1.0.100
        }

        member {
            memberaddr: 10.1.0.200
        }

        ringnumber: 0
        bindnetaddr: 10.1.0.0
        mcastport: 5405
        ttl: 1
    }
    
    interface {
        member {
            memberaddr: 192.168.123.100
        }

        member {
            memberaddr: 192.168.123.200
        }

        ringnumber: 1
        bindnetaddr: 192.168.123.0
        mcastport: 5407
        ttl: 1
    }
}

amf {
    mode: disabled
}

service {
    ver:       1
    name:      pacemaker
}

aisexec {
    user:   root
    group:  root
}

logging {
    syslog_priority: warning
    
    fileline: off
    to_stderr: yes
    to_logfile: no
    to_syslog: yes
    syslog_facility: daemon
    debug: off
    timestamp: on
    
    logger_subsys {
        subsys: AMF
        debug: off
        tags: enter|leave|trace1|trace2|trace3|trace4|trace6
    }
}

Тут мы описываем два кольца для связи — внутреннее (через порты репликации) и внешнее (через коммутаторы), выбираем протокол udpu (UDP Unicast) и указываем IP-адреса нод в каждом кольце. У меня еще была идея соединить ноды нуль-модемным кабелем, поднять PPP-соединение и пустить через него третье кольцо, но здравый смысл вовремя подсказал что и так сойдёт.

Всё, можно запускать Pacemaker (он запустит Corosync предварительно).
# /etc/init.d/pacemaker start

Вся настройка Pacemaker происходит через утилиту crm, причем запускать её можно на любом сервере в кластере — он автоматически обновит конфигурацию на всех нодах после изменения.

Посмотрим текущий статус:
# crm status
============
Last updated: Mon Jan 20 15:33:29 2014
Last change: Fri Jan 17 18:30:48 2014 via cibadmin on server1
Stack: openais
Current DC: server1 - partition WITHOUT quorum
Version: 1.1.7-ee0730e13d124c3d58f00016c3376a1de5323cff
2 Nodes configured, 2 expected votes
0 Resources configured.
============

Online: [ server1 server2 ]

Если всё примерно так, то связь установлена и ноды друг друга видят.

Теперь нам понадобятся ресурсы для управления SCST.
Я в свое время нашел их где-то на просторах интернета, модифицировал под свои нужды и выложил на Github.

Оттуда нам понадобятся два файла:
  • SCSTLun — управляет созданием устройств
  • SCSTTarget — управляет созданием iSCSI таргетов

Это, по сути, обычные bash-скрипты, реализующие несложный API Pacemaker.
Положить их следует в /usr/lib/ocf/resource.d/heartbeat, чтобы менеджер кластера их увидел.

Далее запускаем crm и входим в режим настройки:
# crm
crm(live)# configure
crm(live)configure# edit

Открывается текстовый редактор (обычно — nano) и можно приступить к описанию ресурсов и их взаимодействия.

Приведу пример конфигурации:
node server1
node server2

primitive DRBD_VM_STORAGE_1 ocf:linbit:drbd \
        params drbd_resource="VM_STORAGE_1" drbdconf="/etc/drbd.conf" \
        op monitor interval="29" role="Master" \
        op monitor interval="31" role="Slave"
primitive DRBD_VM_STORAGE_2 ocf:linbit:drbd \
        params drbd_resource="VM_STORAGE_2" drbdconf="/etc/drbd.conf" \
        op monitor interval="29" role="Master" \
        op monitor interval="31" role="Slave"

primitive IP_iSCSI_1_1 ocf:heartbeat:IPaddr2 \
        params ip="10.1.24.10" cidr_netmask="24" nic="int1.24" \
        op monitor interval="10s"
primitive IP_iSCSI_1_2 ocf:heartbeat:IPaddr2 \
        params ip="10.1.25.10" cidr_netmask="24" nic="int2.25" \
        op monitor interval="10s"
primitive IP_iSCSI_1_3 ocf:heartbeat:IPaddr2 \
        params ip="10.1.26.10" cidr_netmask="24" nic="int3.26" \
        op monitor interval="10s"
primitive IP_iSCSI_1_4 ocf:heartbeat:IPaddr2 \
        params ip="10.1.27.10" cidr_netmask="24" nic="int4.27" \
        op monitor interval="10s"
primitive IP_iSCSI_1_5 ocf:heartbeat:IPaddr2 \
        params ip="10.1.28.10" cidr_netmask="24" nic="int5.28" \
        op monitor interval="10s"
primitive IP_iSCSI_1_6 ocf:heartbeat:IPaddr2 \
        params ip="10.1.29.10" cidr_netmask="24" nic="int6.29" \
        op monitor interval="10s"

primitive IP_iSCSI_2_1 ocf:heartbeat:IPaddr2 \
        params ip="10.1.24.20" cidr_netmask="24" nic="int1.24" \
        op monitor interval="10s"
primitive IP_iSCSI_2_2 ocf:heartbeat:IPaddr2 \
        params ip="10.1.25.20" cidr_netmask="24" nic="int2.25" \
        op monitor interval="10s"
primitive IP_iSCSI_2_3 ocf:heartbeat:IPaddr2 \
        params ip="10.1.26.20" cidr_netmask="24" nic="int3.26" \
        op monitor interval="10s"
primitive IP_iSCSI_2_4 ocf:heartbeat:IPaddr2 \
        params ip="10.1.27.20" cidr_netmask="24" nic="int4.27" \
        op monitor interval="10s"
primitive IP_iSCSI_2_5 ocf:heartbeat:IPaddr2 \
        params ip="10.1.28.20" cidr_netmask="24" nic="int5.28" \
        op monitor interval="10s"
primitive IP_iSCSI_2_6 ocf:heartbeat:IPaddr2 \
        params ip="10.1.29.20" cidr_netmask="24" nic="int6.29" \
        op monitor interval="10s"

primitive ISCSI_LUN_VM_STORAGE_1 ocf:heartbeat:SCSTLun \
        params iqn="iqn.2011-04.ru.domain:VM_STORAGE_1" device_name="VM_STORAGE_1" \
        lun="0" path="/dev/drbd0" handler="vdisk_fileio"
primitive ISCSI_LUN_VM_STORAGE_2 ocf:heartbeat:SCSTLun \
        params iqn="iqn.2011-04.ru.domain:VM_STORAGE_2" device_name="VM_STORAGE_2" \
        lun="0" path="/dev/drbd1" handler="vdisk_fileio"

primitive ISCSI_TGT_VM_STORAGE_1 ocf:heartbeat:SCSTTarget \
        params iqn="iqn.2011-04.ru.domain:VM_STORAGE_1" \
        portals="10.1.24.10 10.1.25.10 10.1.26.10 10.1.27.10 10.1.28.10 10.1.29.10" \
        tgtoptions="InitialR2T=No ImmediateData=Yes MaxRecvDataSegmentLength=1048576 MaxXmitDataSegmentLength=1048576 MaxBurstLength=1048576 FirstBurstLength=524284 MaxOutstandingR2T=32 HeaderDigest=CRC32C DataDigest=CRC32C QueuedCommands=32 io_grouping_type=never" \
        op monitor interval="10s" timeout="60s"
primitive ISCSI_TGT_VM_STORAGE_2 ocf:heartbeat:SCSTTarget \
        params iqn="iqn.2011-04.ru.domain:VM_STORAGE_2" \
        portals="10.1.24.20 10.1.25.20 10.1.26.20 10.1.27.20 10.1.28.20 10.1.29.20" \
        tgtoptions="InitialR2T=No ImmediateData=Yes MaxRecvDataSegmentLength=1048576 MaxXmitDataSegmentLength=1048576 MaxBurstLength=1048576 FirstBurstLength=524284 MaxOutstandingR2T=32 HeaderDigest=CRC32C DataDigest=CRC32C QueuedCommands=32 io_grouping_type=never" \
        op monitor interval="10s" timeout="60s"

group GROUP_ISCSI_1 IP_iSCSI_1_1 IP_iSCSI_1_2 IP_iSCSI_1_3 IP_iSCSI_1_4 \
            IP_iSCSI_1_5 IP_iSCSI_1_6 ISCSI_TGT_VM_STORAGE_1 ISCSI_LUN_VM_STORAGE_1
group GROUP_ISCSI_2 IP_iSCSI_2_1 IP_iSCSI_2_2 IP_iSCSI_2_3 IP_iSCSI_2_4 \
            IP_iSCSI_2_5 IP_iSCSI_2_6 ISCSI_TGT_VM_STORAGE_2 ISCSI_LUN_VM_STORAGE_2

ms MS_DRBD_VM_STORAGE_1 DRBD_VM_STORAGE_1 \
        meta master-max="1" master-node-max="1" clone-max="2" clone-node-max="1" \
        notify="true" target-role="Master"
ms MS_DRBD_VM_STORAGE_2 DRBD_VM_STORAGE_2 \
        meta master-max="1" master-node-max="1" clone-max="2" clone-node-max="1" \
        notify="true" target-role="Master"

location PREFER-1 MS_DRBD_VM_STORAGE_1 50: server1
location PREFER-2 MS_DRBD_VM_STORAGE_2 50: server2

colocation COLOC_ALL_1 inf: GROUP_ISCSI_1 MS_DRBD_VM_STORAGE_1:Master
colocation COLOC_ALL_2 inf: GROUP_ISCSI_2 MS_DRBD_VM_STORAGE_2:Master

order ORDER_ALL_1 inf: MS_DRBD_VM_STORAGE_1:promote GROUP_ISCSI_1:start
order ORDER_ALL_2 inf: MS_DRBD_VM_STORAGE_2:promote GROUP_ISCSI_2:start

property $id="cib-bootstrap-options" \
        dc-version="1.1.7-ee0730e13d124c3d58f00016c3376a1de5323cff" \
        cluster-infrastructure="openais" \
        expected-quorum-votes="2" \
        stonith-enabled="false" \
        no-quorum-policy="ignore" \
        default-action-timeout="240" \
        last-lrm-refresh="1367942459"
rsc_defaults $id="rsc-options" \
        resource-stickiness="100"

Общие настройки кластера

Они находятся в самом низу. Из важных тут no-quorum-policy=«ignore» и expected-quorum-votes=«2» — у нас кластер из 2 серверов и кворума тут быть не может ну никак, поэтому игнорируем его.

Ресурсы

Обычно ресурс может иметь два состояния — включен или выключен, Started/Stopped.
Например, ocf:heartbeat:IPaddr2 поднимает на интерфейсах IP-адреса и снимает их, а также рассылает gratious arp для обновления arp-таблиц. Этому ресурсу мы указываем IP адрес, маску и интерфейс.

Еще есть специальные ресурсы, например DRBD (ocf:linbit:drbd), которые имеют режимы Master/Slave.
При переходе ноды в активный режим менеджер кластера будет переводить ресурс в режим мастер и наоборот. DRBD при этом будет переключаться из Secondary в Primary. Для него мы указываем имя ресурса и путь до конфига DRBD (наверное, его можно опустить, точно не помню).

Далее идут наши самописные ресурсы.
Для ocf:heartbeat:SCSTLun мы указываем IQN таргета, в который он будет добавлен, имя устройства, номер LUN-а (у таргета обязательно должен быть LUN 0, иначе у некоторых инициаторов сносит крышу), путь до экспортируемого устройства и обработчик (handler).

На обработчике нужно остановится подробнее — это способ, которым SCST будет работать с нашим устройством.

Из интересных это:
  • disk — по сути это просто прямой проброс SCSI команд от инициатора к SCSI устройству, самый простой режим, но работает только с реальными SCSI устройствами, нам не подходит, т.к. экспортируем DRBD-устройство
  • vdisk_blockio — открывает устройство как блочное, обходя page-cache операционной системы. Используется, если не нужно кэшировать ввод-вывод
  • vdisk_fileio — открывает устройство как файл, позволяя использовать page-cache операционной системы, самый производительный режим, его и выберем

У vdisk_fileio есть важный параметр, влияющий на скорость — nv_cache=1, он прописан жестко в SCSTLun.
Этот параметр говорит SCST игнорировать команды инициатора для сброса кэша на устройство. Потенциально, это может привести к потере данных в случае аварийного отключения хранилища т.к. инициатор будет думать, что данные на диске, а они еще в памяти. Так что используйте на свой страх и риск.

Далее идёт ресурс ocf:heartbeat:SCSTTarget, которому мы присваиваем IQN, portals — это список IP-адресов, через которые будет доступен этот таргет, tgtoptions — опции iSCSI, про них можно много где почитать.

Директивы, ответственные за поведение кластера при запуске и остановке ресурсов:
  • group объединяет ресурсы в группу для работы с ними как с единым целым. Ресурсы в группе запускаются последовательно
  • location указывает на какой ноде мы по умолчанию хотим видеть этот ресурс
  • colocation задает какие ресурсы должны находиться вместе на одной ноде
  • order сообщает менеджеру кластера порядок запуска ресурсов

После настройки ресурсов выходим из редактора и применяем изменения:
crm(live)configure# commit
crm(live)configure# exit

После этого можно посмотреть текущую ситуацию:
# crm status
============
Last updated: Mon Jan 20 17:04:04 2014
Last change: Thu Jul 25 13:59:27 2013 via crm_resource on server1
Stack: openais
Current DC: server1 - partition with quorum
Version: 1.1.7-ee0730e13d124c3d58f00016c3376a1de5323cff
2 Nodes configured, 2 expected votes
20 Resources configured.
============

Online: [ server1 server2 ]

 Resource Group: GROUP_ISCSI_1
     IP_iSCSI_1_1       (ocf::heartbeat:IPaddr2):       Stopped
     IP_iSCSI_1_2       (ocf::heartbeat:IPaddr2):       Stopped
     IP_iSCSI_1_3       (ocf::heartbeat:IPaddr2):       Stopped
     IP_iSCSI_1_4       (ocf::heartbeat:IPaddr2):       Stopped
     IP_iSCSI_1_5       (ocf::heartbeat:IPaddr2):       Stopped
     IP_iSCSI_1_6       (ocf::heartbeat:IPaddr2):       Stopped
     ISCSI_TGT_VM_STORAGE_1    (ocf::heartbeat:SCSTTarget):    Stopped
     ISCSI_LUN_VM_STORAGE_1    (ocf::heartbeat:SCSTLun):       Stopped
 Resource Group: GROUP_ISCSI_2
     IP_iSCSI_2_1       (ocf::heartbeat:IPaddr2):       Stopped
     IP_iSCSI_2_2       (ocf::heartbeat:IPaddr2):       Stopped
     IP_iSCSI_2_3       (ocf::heartbeat:IPaddr2):       Stopped
     IP_iSCSI_2_4       (ocf::heartbeat:IPaddr2):       Stopped
     IP_iSCSI_2_5       (ocf::heartbeat:IPaddr2):       Stopped
     IP_iSCSI_2_6       (ocf::heartbeat:IPaddr2):       Stopped
     ISCSI_TGT_VM_STORAGE_2    (ocf::heartbeat:SCSTTarget):    Stopped
     ISCSI_LUN_VM_STORAGE_2    (ocf::heartbeat:SCSTLun):       Stopped
 Master/Slave Set: MS_DRBD_VM_STORAGE_1 [DRBD_VM_STORAGE_1]
     Slaves: [ server1 server2 ]
 Master/Slave Set: MS_DRBD_VM_STORAGE_2 [DRBD_VM_STORAGE_2]
     Slaves: [ server1 server2 ]

Мы видим, что ресурсы в неактивном состоянии, DRBD в режиме Slave (Secondary).

Теперь можно попробовать их активировать:
# crm resource start MS_DRBD_VM_STORAGE_1
# crm resource start MS_DRBD_VM_STORAGE_2

Стартуя этот ресурс мы вызываем в том числе и запуск других ресурсов т.к. они указаны у нас как зависимые (colocation), причем запускаться они будут в строго определенном порядке (order): сначала DRBD устройства перейдут в режим Primary, затем поднимутся IP-адреса, создадутся LUN-ы и в конце уже создадутся iSCSI таргеты.

Смотрим результат:
# crm status
============
Last updated: Tue Jan 21 11:54:46 2014
Last change: Thu Jul 25 13:59:27 2013 via crm_resource on server1
Stack: openais
Current DC: server1 - partition with quorum
Version: 1.1.7-ee0730e13d124c3d58f00016c3376a1de5323cff
2 Nodes configured, 2 expected votes
20 Resources configured.
============

Online: [ server1 server2 ]

 Resource Group: GROUP_ISCSI_1
     IP_iSCSI_1_1       (ocf::heartbeat:IPaddr2):       Started server1
     IP_iSCSI_1_2       (ocf::heartbeat:IPaddr2):       Started server1
     IP_iSCSI_1_3       (ocf::heartbeat:IPaddr2):       Started server1
     IP_iSCSI_1_4       (ocf::heartbeat:IPaddr2):       Started server1
     IP_iSCSI_1_5       (ocf::heartbeat:IPaddr2):       Started server1
     IP_iSCSI_1_6       (ocf::heartbeat:IPaddr2):       Started server1
     ISCSI_TGT_VM_STORAGE_1    (ocf::heartbeat:SCSTTarget):    Started server1
     ISCSI_LUN_VM_STORAGE_1    (ocf::heartbeat:SCSTLun):       Started server1
 Resource Group: GROUP_ISCSI_2
     IP_iSCSI_2_1       (ocf::heartbeat:IPaddr2):       Started server2
     IP_iSCSI_2_2       (ocf::heartbeat:IPaddr2):       Started server2
     IP_iSCSI_2_3       (ocf::heartbeat:IPaddr2):       Started server2
     IP_iSCSI_2_4       (ocf::heartbeat:IPaddr2):       Started server2
     IP_iSCSI_2_5       (ocf::heartbeat:IPaddr2):       Started server2
     IP_iSCSI_2_6       (ocf::heartbeat:IPaddr2):       Started server2
     ISCSI_TGT_VM_STORAGE_2    (ocf::heartbeat:SCSTTarget):    Started server2
     ISCSI_LUN_VM_STORAGE_2    (ocf::heartbeat:SCSTLun):       Started server2
 Master/Slave Set: MS_DRBD_VM_STORAGE_1 [DRBD_VM_STORAGE_1]
     Masters: [ server1 ]
     Slaves: [ server2 ]
 Master/Slave Set: MS_DRBD_VM_STORAGE_2 [DRBD_VM_STORAGE_2]
     Masters: [ server2 ]
     Slaves: [ server1 ]

Если всё так, то можно себя поздравить — кластер запущен!
Каждая группа ресурсов запущена на своем сервере, как это указано в конфиге директивой location.

Для подтверждения можно посмотреть лог ядра — dmesg — туда DRBD и SCST выводят свою диагностику.

Конец второй части


В третьей, заключительной, части я покажу как настроить сервера ESXi на оптимальную работу с этим кластером.