\section{Построение отказоустойчивого кластера виртуальных машин Xen + DRBD}

\subsection{Идея}
Компоненты:
\begin{itemize}
\item  \textbf{Xen} — монитор виртуальных машин (VMM, Virtual Machine Monitor) или гипервизор (hypervisor) с поддержкой паравиртуализации (para-virtualization) для процессоров x86 архитектуры, распространяющийся с открытым исходным кодом (opensource). Xen может организовать совместное безопасное исполнение нескольких виртуальных машин на одной физической системе с производительностью близкой к непосредственной (native).
\item  \textbf{LVM} (Logical Volume Manager) — менеджер логических томов операционной системы Linux. LVM предоставляет собой дополнительный уровень абстракции между физическими/логическими дисками и файловой системой. 
\item  \textbf{DRBD} (Distributed Replicated Block Deice) — блочное устройство, предназначенное для построения отказоустойчивых кластерных систем на операционной системе Linux. DRBD занимается полным отражением (mirroring) по сети всех операций с блочным устройством. Можно считать, что DRBD это сетевой RAID-1.
\end{itemize}

Терминология:
\begin{itemize}
\item  узел — физический сервер, на котором исполняются виртуальные машины;
\item  DRBD-устройство — дисковый раздел или логический том LVM, синхронизируемый с помощью DRBD;
\item  домен — работающая виртуальная машина Xen;
\item  кластер — два узла, которые имеют общие DRBD-устройства, поверх которых выполняются общие домены Xen.
\end{itemize}

Идея заключается в том, чтобы в качестве дисковых устройств для виртуальных машин Xen, 
использовать DRBD-устройства. DRBD устройства в свою очередь размещаются поверх LVM томов машин входящих в кластер.

DRBD отвечает за полную синхронизацию операций с дисковыми системами, выполняющимися доменами Xen.

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

При плановом выведении узла из эксплуатации, 
машины, работающие на нём, мигрируют на второй узел кластера.
Это совершенно незаметно с точки зрения внешнего наблюдателя.

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

Виртуальные машины могут быть произвольно распределены по узлам кластера.

\begin{center} \resizebox{10cm}{!}{\includegraphics{/var/lib/mediawiki/images/4/4b/Xen-drbd.png}}\\ \textit{}\end{center}

\subsection{Инсталляция и управление кластером}
\subsubsection{Подготовка узлов}

Подготовка узлов может выполняться вручную или с помощью
вспомогательных средств, таких, например, как скрипт \textit{xen-drbd-install}.
Скрипт \textit{xen-drdb-install} предназначен для облегчения
рутинных операций, выполняющихся при инсталляции
нового кластера виртуализации с повышенной отказоустойчивостью,
построенного на основе Xen и DRBD.

В результате работы \textit{xen-drbd-install}
создаётся сценарий командного интерпретатора, 
который выполняет такие действия:
\begin{enumerate}
\item  Подготовка LVM томов;
\item  Настройка DRBD устройств;
\item  Наполнение файловых систем доменов.
\end{enumerate}

Полученный сценарий может быть доработан вручную, 
а может использоваться непосредственном в том 
виде, как его сгенерирует \textit{xen-drbd-install}.

\subsubsection{Управление узлами}

Скрипт \textit{xen-drbd} 
обеспечивает взаимное соответсвие
состояния DRBD-устройств и доменов Xen, использующих их.
Он следит за тем, чтобы при выполнении таких
операций как запуск и миграция доменов,
используемые DRBD-устройства
переключались основное (primary) или резервное (secondary) состояние
в зависимости от точки запуска или направления миграции.

Он также следит за тем, чтобы один и тот же домен
нельзя было запустить на разных узлах кластера
несколько раз.

Кроме этого, \textit{xen-drbd} контролирует процесс запуска и останова узлов:
он инициирует миграцию доменов с одного узла на другой в случае
останова первого; и обратную миграцию при запуске узла.

\subsection{Взаимный контроль узлов с помощью heartbeat}
Кластер функционален уже — без настройки и использования heartbeat — 
однако функциональность его частична:
при выходе из строя одного из узлов кластера
доступными останутся только те домены, которые 
исполнялись на нём в момент выхода из строя второго узла.
Оставшиеся домены можно будет поднять, но только вручную.
Нужно же, чтобы каждый из узлов имел возможность определить, 
что его напарник пропал, и запустить все недостающие домены.
При этом особенно важно избежать случая, когда каждый узел
ошибочно решит, что его напарник выключился, в то время как 
оба они будут работать, но по какой-то причине перестанут видеть друг друга.
В этом случае может наступить опасная ситуация, касающаяся взаимной противоречивости данных на DRBD-устройствах, и названная \textit{split-brain}.

\subsection{Резервирование коммутаторов и сетевых адаптеров}
\subsubsection{Использование аггрегированных каналов}
Сетевые адаптеры узлов могут быть зарезервированы
путём аггрегирования каналов,
соединяющих узел с коммутатором.

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

При пропадении одного из соединений (это может быть связано
с неполадками адаптера, соединительного кабеля или порта коммутатора)
система продолжает работу на оставшемся.
В системном журнале появляется сообщение о 
возникшей проблеме.

Для работы аггрегированного канала необходима поддержка
со стороны коммутатора:
\begin{itemize}
\item  он должен поддерживать аггрегированные каналы;
\item  он должен быть настроен соответствующим образом.
\end{itemize}

\subsubsection{Использование виртуального моста и STP}
Построить аггрегированный канал на два независимых коммутатора
нельзя (за некоторыми проприетарными исключениями).

Можно отказаться от использования аггрегированного канала
и с некоторой потерей функциональности перейти на использование
протокола STP и виртуального моста.
Это позволит сделать резервирование коммутатора, однако 
у такого решения есть недостаток:
в отдельно взятый момент времени будет работать один из каналов.

Подключение одного узла выглядит так:
\begin{verbatim}
+---------------------------+
|HOST                 peth0 |
|                      +--+ |      +--------------+
|               +------+  +-+------|   switch1    |
|               |      +--+ |      +---+----------+
|veth0+---------+----+      |          |
|  ---+ linux bridge |      |          |
|     +---------+----+      |          |
|               |      +--+ |      +---+----------+
|               +------+  +-+------+   switch2    |
|                      +--+ |      +--------------+
|                     peth1 |
+---------------------------+
\end{verbatim}

Соединения с коммутаторами происходят не напрямую
а через виртуальных мост (linux bridge) внутри хоста.
Этот мост поддерживает протокол STP
и в этом контексте может рассматриваться как обычный
коммутатор, который не может выйти из строя (точнее,
может, но только вместе с хостом, внутри которого он работает).

Соединенние двух коммутаторов и двух узлов выглядит так
(соединения heartbeat может и не быть, в этом случае
в качестве heartbeat-канала будут использоваться
основные соединения):
\begin{verbatim}
+---------------------------+
|HOST1                peth0 |
|                      +--+ |      +--------------+
|               +------+  +-+------|   switch1    |
|               |      +--+ |      ++--+----------+
|veth0+---------+----+      |   +---+  |
|  ---+ linux bridge |      |   |      |
|     +---------+----+      |   |      |
|               |      +--+ |   |  +---+----------+
|               +------+  +-+---|--+   switch2    |
|                      +--+ |   |  +---+----------+
|                     peth1 |   |      |
+-------+-------------------+   |      |
        |                       |      |
        |heartbeat              |      |
+-------+-------------------+   |      |
|HOST2                peth0 |   |      |
|                      +--+ |   |      |
|               +------+  +-+---+      |
|               |      +--+ |          |
|veth0+---------+----+      |          |
|  ---+ linux bridge |      |          |
|     +---------+----+      |          |
|               |      +--+ |          |
|               +------+  +-+----------+
|                      +--+ |
|                     peth1 |
+---------------------------+
\end{verbatim}

\subsubsection{Сравнение использования аггрегированных каналов и виртуального моста}
Минусы:

\begin{enumerate}
\item  Каналы не объединияются, а используются попеременно.
\item  Переключение с одного канала на другой происходит достаточно долго. При настройках коммутатора по умолчанию процесс переключения может занять до минуты. Это связано с работой STP. В RSTP было бы быстрее, но RSTP Linux Bridge пока не поддерживает.
\end{enumerate}

Плюсы:

\begin{enumerate}
\item  От коммутаторов ничего не требуется кроме поддержки STP;
\item  Можно подключаться к двум независимым коммутаторам;
\item  Не требуется никакая дополнительная настройка коммутаторов.
\end{enumerate}

\subsection{Отдельные вопросы эксплуатации xen-drbd}
\subsubsection{Создание новых устройств DRBD}
При синхронизации множества отдельных томов с помощью DRBD
нужно обратить внимание на следующее:
\begin{itemize}
\item  Количество синхронизируемых устройств DRBD ограничено (<=255);
\item  Для синхронизации отдельных устройств DRBD используются отдельные TCP-порты. При добавлении нового DRBD-устройства обратите внимание на то, что бы порт, который вы назначаете ему для синхронизации, уже не был занят.
\item  Используйте внешние метадиски, поскольку в случае когда метадиск является внутренним, поведение DRBD при изменении размера логического тома может оказаться неожиданным.
\end{itemize}

\paragraph{Множество DRBD-устройств}
Количество синхронизируемых устройств DRBD ограничено.
Максимальное количество используемых одновременно DRBD-устройств
задаётся в качестве параметра \texttt{minor\_count} модуля ядра \textbf{drbd}
при его загрузке. Этот параметр не может превышать 255.

\begin{verbatim}
$ sudo modinfo drbd
filename:       /lib/modules/2.6.18-3-xen-686/kernel/drivers/block/drbd.ko
author:         Philipp Reisner <phil@linbit.com>, Lars Ellenberg <lars@linbit.com>
description:    drbd - Distributed Replicated Block Device v8.0.0
license:        GPL
alias:          block-major-147-*
vermagic:       2.6.18-3-xen-686 SMP mod_unload 686 REGPARM gcc-4.1
depends:        cn
parm:           trace_devs:int
parm:           trace_type:int
parm:           trace_level:int
parm:           fault_count:int
parm:           fault_rate:int
parm:           enable_faults:int
parm:           allow_oos:DONT USE! (bool)
parm:           minor_count:Maximum number of drbd devices (1-255) (int)
\end{verbatim}

\paragraph{Сетевые порты DRBD}
Для синхронизации отдельных устройств DRBD используются отдельные TCP-порты.
При добавлении нового DRBD-устройства обратите внимание на то, что бы порт,
который вы назначаете ему для синхронизации, уже не был занят.

Кроме того, нужно обратить внимание на то, чтобы доступ к этим портам
для парного узла не был ограничен брандмауэром.

В пример ниже есть строка:
\begin{verbatim}
 address 192.168.1.190:7792;
\end{verbatim}
она показывает, что синхронизация ресурса
выполняется с узлом 192.168.1.190 и для сихнронизации используется
порт 7792.

\paragraph{Метадиск}
Лучше не использовать внуренний метадиск (meta-disk internal),
особенно если вы собираетесь менять размер логического тома
LVM и файловой системы на нём.

Нужно создать отдельный том для-
meta-disk\rq{}ов DRBD и задать его размер
равным 128MB x количество устройств.

В пример конфигурационного файла <tt>drbd.conf</tt>, 
приведённом ниже, есть строка:
\begin{verbatim}
 meta-disk /dev/XEN/meta[1];
\end{verbatim}
Она говорит о том, что мета-информация
об DRBD-устройстве, к которому относится эта строка,
должна находится в мета-диске 1 (нумерация с нуля)
на томе \texttt{/dev/XEN/meta}.
Подготовка этого тома не выполняется каким-то
особенным образом —  в данном случае это обычный
логический том LVM, но вообще это может быть любое блочное
устройство достаточного объёма.

Если метадиск создаётся на отдельном логическом
томе LVM, то его можно расширять.
Расширять метадиск нужно в том случае,
когда количество DRBD-устройств, использующих
его, превышает допустимое.
Это число можно найти, разделив размер метадиска
на объём, необходимый для каждого DRBD-устройства
(в настоящий момент 128MB).

\paragraph{Пример секции файла drbd.conf}

\begin{verbatim}
resource dns {
    protocol C;
    net {
        allow-two-primaries;
        after-sb-0pri discard-least-changes;
        after-sb-1pri call-pri-lost-after-sb;
        after-sb-2pri call-pri-lost-after-sb;
    }
    syncer {
        rate 5M;
    }
    on dom0
    {
        device /dev/drbd1;
        disk /dev/XEN/dns;
        address 192.168.1.190:7792;
        meta-disk /dev/XEN/meta[1];
    }
    on dom0m
    {
        device /dev/drbd1;
        disk /dev/XEN/dns;
        address 192.168.1.191:7792;
        meta-disk /dev/XEN/meta[1];
    }
}
\end{verbatim}

\subsubsection{Расширение диска DRBD}
DRBD-устройство может менять свои размеры.
Это возможно в том случае, если меняют размер (как правило, расширяются)
устройства на которых базируется DRBD.
Когда DRBD работает поверх логических томов LVM,
желание расширение DRBD-устройства выглядит весьма естественно,
поскольку изменение размеров LVM-томов
является вполне простой и часто использующейся операцией;
хочется, чтобы то, что работает поверх логического тома LVM
могло отреагировать на изменение размеров.

Расширение DRBD-устройства и файловой системы, находящейся на нём, состоит из
следующих шагов:
\begin{enumerate}
\item  Расширение \textit{логического тома}, на котором базируется DRBD-устройство на обоих узлах.
\item  Отражение изменений в метадиске.
\item  Расширение \textit{файловой системы} на primary-устройстве.
\item  Проверка правильности выполнения.
\end{enumerate}

Например, пусть:
\begin{itemize}
\item  машины называются \textit{primary} и \textit{secondary}
\item  с помощью DRBD синхронизируется логический том /dev/TURBO/lv0
\item  размер тома увеличивается на 2G
\item  на томе создана файловая система ext2/ext3
\end{itemize}
Все указанные параметры являются необязательными
и приведены для удобства повествования.

1) Измените размер тома на обоих машинах.

\begin{verbatim}
primary# lvresize -L +2048M /dev/TURBO/lv0
secondary# lvresize -L +2048M /dev/TURBO/lv0
\end{verbatim}

2) Вызовите команду перестроения размера
DRBD-устройства на обоих машинах:

\begin{verbatim}
primary# drbdadm resize all  #(вместо all может быть указан только интересующий диск)
secondary# drbdadm resize all
\end{verbatim}

3) На primary-устройстве измените размер файловой системы,
расположенной на DRBD-устройстве:

\begin{verbatim}
primary# ext2resize /dev/drbd0  # (или другое устройство)
\end{verbatim}

4) Проверьте, что изменение размера прошло успешно.

\begin{verbatim}
primary# df -h /dev/drbd0
\end{verbatim}

Проверка с помощью df возможна только в том случае,
если \texttt{/dev/drbd0} смонтировано в настоящий момент.

Размер несмонтированной файловой системы можно тоже посмотреть,
но только не в байтах, а в блоках.
Для файловых систем ext2/ext3:

\begin{verbatim}
%# dumpe2fs /dev/drbd0 | grep ^Block
dumpe2fs 1.40-WIP (14-Nov-2006)
Block count:              979933
Block size:               1024
Blocks per group:         8192
\end{verbatim}

Указать домену Xen, использующему DRBD-устройство,
что оно изменило размер, в настоящий момент,
к сожалению, невозможно.

В будущем, сообщить об изменении конфигурации
дискового устройства домена будет можно, предположительно,
с помощью команды \textbf{xm block-configure}.

\subsubsection{Проверка DRBD перед стартом виртуальной машины}
Можно сделать так, чтобы виртуальная машина
вообще не стартовала до тех пор,
пока соответствующее DRBD-устройство не будет переведено
в состояние primary
(на устройстве в состоянии secondary она может начать работать,
но, когда внутреняя операционная система дойдёт до момента
монтирования корневой файловой системы, загрузка прекратится; 
в действительности это устройство вообще не используется до момента
монтирования корневой системы, поэтому первый этап загрузки
операционной системы домена проходит без ошибок).

Предположим, машина использует в качестве своего хранилища
устройство /dev/drbd4. В таком случае,
проверка будет выглядеть так:

\begin{verbatim}
import os
if os.system("grep '4:.*Primary/Secondary' /proc/drbd") != 0:
    print "DRBD for this virtual machine not in primary state."
    print "Exiting."
    exit(1)
\end{verbatim}

Эти строки нужно добавить в конфигурационный файл соответствующего
домена Xen.

Пример использования:

\begin{verbatim}
%$ sudo drbdadm secondary vpn

%$ sudo xm create -c vpn
Using config file "/etc/xen/vpn".
DRBD for this virtual machine not in primary state.
Exiting.
Error: 'str' object is not callable

%$ sudo drbdadm primary vpn

%$ sudo xm create -c vpn
Using config file "/etc/xen/vpn".
 4: cs:Connected st:Primary/Secondary ds:UpToDate/UpToDate C r---
Started domain vpn
Linux version 2.6.18-4-xen-686 (Debian 2.6.18.dfsg.1-11)
(waldi@debian.org) (gcc version 4.1.2 20061115 (prerelease) (Debian
4.1.1-21)) #1 SMP Wed Feb 21 20:46:15 UTC 2007
BIOS-provided physical RAM map:
 Xen: 0000000000000000 - 0000000004800000 (usable)
0MB HIGHMEM available.
72MB LOWMEM available.
.......
\end{verbatim}

Когда DRBD-устройство \textit{vpn} было в резервном состоянии (secondary),
создать домен не удалось.
После того как оно было переведено
в основное состояние (primary), домен успешно стартанул.

Если для управления доменами Xen используется скрипт \textit{xen-drbd},
то эту проверку выполнять не нужно.
Она автоматически выполняется скриптом.

\subsection{Дополнительная информация}
\begin{itemize}
\item \htmladdnormallinkfoot{Использование доменов Xen поверх DRBD-устройств}{http://xgu.ru/wiki/xen/drbd} (рус.)
\end{itemize}
