
igor@0: \section{Построение отказоустойчивого кластера виртуальных машин Xen + DRBD}
igor@0: 
igor@0: \subsection{Идея}
igor@0: Компоненты:
igor@0: \begin{itemize}
igor@0: \item  \textbf{Xen} — монитор виртуальных машин (VMM, Virtual Machine Monitor) или гипервизор (hypervisor) с поддержкой паравиртуализации (para-virtualization) для процессоров x86 архитектуры, распространяющийся с открытым исходным кодом (opensource). Xen может организовать совместное безопасное исполнение нескольких виртуальных машин на одной физической системе с производительностью близкой к непосредственной (native).
igor@0: \item  \textbf{LVM} (Logical Volume Manager) — менеджер логических томов операционной системы Linux. LVM предоставляет собой дополнительный уровень абстракции между физическими/логическими дисками и файловой системой. 
igor@0: \item  \textbf{DRBD} (Distributed Replicated Block Deice) — блочное устройство, предназначенное для построения отказоустойчивых кластерных систем на операционной системе Linux. DRBD занимается полным отражением (mirroring) по сети всех операций с блочным устройством. Можно считать, что DRBD это сетевой RAID-1.
igor@0: \end{itemize}
igor@0: 
igor@0: Терминология:
igor@0: \begin{itemize}
igor@0: \item  узел — физический сервер, на котором исполняются виртуальные машины;
igor@0: \item  DRBD-устройство — дисковый раздел или логический том LVM, синхронизируемый с помощью DRBD;
igor@0: \item  домен — работающая виртуальная машина Xen;
igor@0: \item  кластер — два узла, которые имеют общие DRBD-устройства, поверх которых выполняются общие домены Xen.
igor@0: \end{itemize}
igor@0: 
igor@0: Идея заключается в том, чтобы в качестве дисковых устройств для виртуальных машин Xen, 
igor@0: использовать DRBD-устройства. DRBD устройства в свою очередь размещаются поверх LVM томов машин входящих в кластер.
igor@0: 
igor@0: DRBD отвечает за полную синхронизацию операций с дисковыми системами, выполняющимися доменами Xen.
igor@0: 
igor@0: С точки зрения внешнего наблюдателя не имеет значения, на каком из узлов кластера
igor@0: в настоящий момент выполняется виртуальная машина.
igor@0: 
igor@0: При плановом выведении узла из эксплуатации, 
igor@0: машины, работающие на нём, мигрируют на второй узел кластера.
igor@0: Это совершенно незаметно с точки зрения внешнего наблюдателя.
igor@0: 
igor@0: При внезапной остановке одного из узлов машины, 
igor@0: работавшие на нём, запускаются на втором узле.
igor@0: С точки зрения внешнего наблюдателя, работавшего с виртуальной машиной, 
igor@0: которая исполнялась на внезапно выключившемся узле, это выглядит 
igor@0: как перезагрузка машины.
igor@0: 
igor@0: Виртуальные машины могут быть произвольно распределены по узлам кластера.
igor@0: 
igor@0: \begin{center} \resizebox{10cm}{!}{\includegraphics{/var/lib/mediawiki/images/4/4b/Xen-drbd.png}}\\ \textit{}\end{center}
igor@0: 
igor@0: \subsection{Инсталляция и управление кластером}
igor@0: \subsubsection{Подготовка узлов}
igor@0: 
igor@0: Подготовка узлов может выполняться вручную или с помощью
igor@0: вспомогательных средств, таких, например, как скрипт \textit{xen-drbd-install}.
igor@0: Скрипт \textit{xen-drdb-install} предназначен для облегчения
igor@0: рутинных операций, выполняющихся при инсталляции
igor@0: нового кластера виртуализации с повышенной отказоустойчивостью,
igor@0: построенного на основе Xen и DRBD.
igor@0: 
igor@0: В результате работы \textit{xen-drbd-install}
igor@0: создаётся сценарий командного интерпретатора, 
igor@0: который выполняет такие действия:
igor@0: \begin{enumerate}
igor@0: \item  Подготовка LVM томов;
igor@0: \item  Настройка DRBD устройств;
igor@0: \item  Наполнение файловых систем доменов.
igor@0: \end{enumerate}
igor@0: 
igor@0: Полученный сценарий может быть доработан вручную, 
igor@0: а может использоваться непосредственном в том 
igor@0: виде, как его сгенерирует \textit{xen-drbd-install}.
igor@0: 
igor@0: \subsubsection{Управление узлами}
igor@0: 
igor@0: Скрипт \textit{xen-drbd} 
igor@0: обеспечивает взаимное соответсвие
igor@0: состояния DRBD-устройств и доменов Xen, использующих их.
igor@0: Он следит за тем, чтобы при выполнении таких
igor@0: операций как запуск и миграция доменов,
igor@0: используемые DRBD-устройства
igor@0: переключались основное (primary) или резервное (secondary) состояние
igor@0: в зависимости от точки запуска или направления миграции.
igor@0: 
igor@0: Он также следит за тем, чтобы один и тот же домен
igor@0: нельзя было запустить на разных узлах кластера
igor@0: несколько раз.
igor@0: 
igor@0: Кроме этого, \textit{xen-drbd} контролирует процесс запуска и останова узлов:
igor@0: он инициирует миграцию доменов с одного узла на другой в случае
igor@0: останова первого; и обратную миграцию при запуске узла.
igor@0: 
igor@0: \subsection{Взаимный контроль узлов с помощью heartbeat}
igor@0: Кластер функционален уже — без настройки и использования heartbeat — 
igor@0: однако функциональность его частична:
igor@0: при выходе из строя одного из узлов кластера
igor@0: доступными останутся только те домены, которые 
igor@0: исполнялись на нём в момент выхода из строя второго узла.
igor@0: Оставшиеся домены можно будет поднять, но только вручную.
igor@0: Нужно же, чтобы каждый из узлов имел возможность определить, 
igor@0: что его напарник пропал, и запустить все недостающие домены.
igor@0: При этом особенно важно избежать случая, когда каждый узел
igor@0: ошибочно решит, что его напарник выключился, в то время как 
igor@0: оба они будут работать, но по какой-то причине перестанут видеть друг друга.
igor@0: В этом случае может наступить опасная ситуация, касающаяся взаимной противоречивости данных на DRBD-устройствах, и названная \textit{split-brain}.
igor@0: 
igor@0: \subsection{Резервирование коммутаторов и сетевых адаптеров}
igor@0: \subsubsection{Использование аггрегированных каналов}
igor@0: Сетевые адаптеры узлов могут быть зарезервированы
igor@0: путём аггрегирования каналов,
igor@0: соединяющих узел с коммутатором.
igor@0: 
igor@0: В каждом сервере должно быть по два сетевых адаптера,
igor@0: каждый из которых подключается к коммутатору.
igor@0: Они работают как единое целое, то есть два канала
igor@0: выглядят как один аггрегированный.
igor@0: 
igor@0: При пропадении одного из соединений (это может быть связано
igor@0: с неполадками адаптера, соединительного кабеля или порта коммутатора)
igor@0: система продолжает работу на оставшемся.
igor@0: В системном журнале появляется сообщение о 
igor@0: возникшей проблеме.
igor@0: 
igor@0: Для работы аггрегированного канала необходима поддержка
igor@0: со стороны коммутатора:
igor@0: \begin{itemize}
igor@0: \item  он должен поддерживать аггрегированные каналы;
igor@0: \item  он должен быть настроен соответствующим образом.
igor@0: \end{itemize}
igor@0: 
igor@0: \subsubsection{Использование виртуального моста и STP}
igor@0: Построить аггрегированный канал на два независимых коммутатора
igor@0: нельзя (за некоторыми проприетарными исключениями).
igor@0: 
igor@0: Можно отказаться от использования аггрегированного канала
igor@0: и с некоторой потерей функциональности перейти на использование
igor@0: протокола STP и виртуального моста.
igor@0: Это позволит сделать резервирование коммутатора, однако 
igor@0: у такого решения есть недостаток:
igor@0: в отдельно взятый момент времени будет работать один из каналов.
igor@0: 
igor@0: Подключение одного узла выглядит так:
igor@0: \begin{verbatim}
igor@0: +---------------------------+
igor@0: |HOST                 peth0 |
igor@0: |                      +--+ |      +--------------+
igor@0: |               +------+  +-+------|   switch1    |
igor@0: |               |      +--+ |      +---+----------+
igor@0: |veth0+---------+----+      |          |
igor@0: |  ---+ linux bridge |      |          |
igor@0: |     +---------+----+      |          |
igor@0: |               |      +--+ |      +---+----------+
igor@0: |               +------+  +-+------+   switch2    |
igor@0: |                      +--+ |      +--------------+
igor@0: |                     peth1 |
igor@0: +---------------------------+
igor@0: \end{verbatim}
igor@0: 
igor@0: Соединения с коммутаторами происходят не напрямую
igor@0: а через виртуальных мост (linux bridge) внутри хоста.
igor@0: Этот мост поддерживает протокол STP
igor@0: и в этом контексте может рассматриваться как обычный
igor@0: коммутатор, который не может выйти из строя (точнее,
igor@0: может, но только вместе с хостом, внутри которого он работает).
igor@0: 
igor@0: Соединенние двух коммутаторов и двух узлов выглядит так
igor@0: (соединения heartbeat может и не быть, в этом случае
igor@0: в качестве heartbeat-канала будут использоваться
igor@0: основные соединения):
igor@0: \begin{verbatim}
igor@0: +---------------------------+
igor@0: |HOST1                peth0 |
igor@0: |                      +--+ |      +--------------+
igor@0: |               +------+  +-+------|   switch1    |
igor@0: |               |      +--+ |      ++--+----------+
igor@0: |veth0+---------+----+      |   +---+  |
igor@0: |  ---+ linux bridge |      |   |      |
igor@0: |     +---------+----+      |   |      |
igor@0: |               |      +--+ |   |  +---+----------+
igor@0: |               +------+  +-+---|--+   switch2    |
igor@0: |                      +--+ |   |  +---+----------+
igor@0: |                     peth1 |   |      |
igor@0: +-------+-------------------+   |      |
igor@0:         |                       |      |
igor@0:         |heartbeat              |      |
igor@0: +-------+-------------------+   |      |
igor@0: |HOST2                peth0 |   |      |
igor@0: |                      +--+ |   |      |
igor@0: |               +------+  +-+---+      |
igor@0: |               |      +--+ |          |
igor@0: |veth0+---------+----+      |          |
igor@0: |  ---+ linux bridge |      |          |
igor@0: |     +---------+----+      |          |
igor@0: |               |      +--+ |          |
igor@0: |               +------+  +-+----------+
igor@0: |                      +--+ |
igor@0: |                     peth1 |
igor@0: +---------------------------+
igor@0: \end{verbatim}
igor@0: 
igor@0: \subsubsection{Сравнение использования аггрегированных каналов и виртуального моста}
igor@0: Минусы:
igor@0: 
igor@0: \begin{enumerate}
igor@0: \item  Каналы не объединияются, а используются попеременно.
igor@0: \item  Переключение с одного канала на другой происходит достаточно долго. При настройках коммутатора по умолчанию процесс переключения может занять до минуты. Это связано с работой STP. В RSTP было бы быстрее, но RSTP Linux Bridge пока не поддерживает.
igor@0: \end{enumerate}
igor@0: 
igor@0: Плюсы:
igor@0: 
igor@0: \begin{enumerate}
igor@0: \item  От коммутаторов ничего не требуется кроме поддержки STP;
igor@0: \item  Можно подключаться к двум независимым коммутаторам;
igor@0: \item  Не требуется никакая дополнительная настройка коммутаторов.
igor@0: \end{enumerate}
igor@0: 
igor@0: \subsection{Отдельные вопросы эксплуатации xen-drbd}
igor@0: \subsubsection{Создание новых устройств DRBD}
igor@0: При синхронизации множества отдельных томов с помощью DRBD
igor@0: нужно обратить внимание на следующее:
igor@0: \begin{itemize}
igor@0: \item  Количество синхронизируемых устройств DRBD ограничено (<=255);
igor@0: \item  Для синхронизации отдельных устройств DRBD используются отдельные TCP-порты. При добавлении нового DRBD-устройства обратите внимание на то, что бы порт, который вы назначаете ему для синхронизации, уже не был занят.
igor@0: \item  Используйте внешние метадиски, поскольку в случае когда метадиск является внутренним, поведение DRBD при изменении размера логического тома может оказаться неожиданным.
igor@0: \end{itemize}
igor@0: 
igor@0: \paragraph{Множество DRBD-устройств}
igor@0: Количество синхронизируемых устройств DRBD ограничено.
igor@0: Максимальное количество используемых одновременно DRBD-устройств
igor@0: задаётся в качестве параметра \texttt{minor\_count} модуля ядра \textbf{drbd}
igor@0: при его загрузке. Этот параметр не может превышать 255.
igor@0: 
igor@0: \begin{verbatim}
igor@0: $ sudo modinfo drbd
igor@0: filename:       /lib/modules/2.6.18-3-xen-686/kernel/drivers/block/drbd.ko
igor@0: author:         Philipp Reisner <phil@linbit.com>, Lars Ellenberg <lars@linbit.com>
igor@0: description:    drbd - Distributed Replicated Block Device v8.0.0
igor@0: license:        GPL
igor@0: alias:          block-major-147-*
igor@0: vermagic:       2.6.18-3-xen-686 SMP mod_unload 686 REGPARM gcc-4.1
igor@0: depends:        cn
igor@0: parm:           trace_devs:int
igor@0: parm:           trace_type:int
igor@0: parm:           trace_level:int
igor@0: parm:           fault_count:int
igor@0: parm:           fault_rate:int
igor@0: parm:           enable_faults:int
igor@0: parm:           allow_oos:DONT USE! (bool)
igor@0: parm:           minor_count:Maximum number of drbd devices (1-255) (int)
igor@0: \end{verbatim}
igor@0: 
igor@0: \paragraph{Сетевые порты DRBD}
igor@0: Для синхронизации отдельных устройств DRBD используются отдельные TCP-порты.
igor@0: При добавлении нового DRBD-устройства обратите внимание на то, что бы порт,
igor@0: который вы назначаете ему для синхронизации, уже не был занят.
igor@0: 
igor@0: Кроме того, нужно обратить внимание на то, чтобы доступ к этим портам
igor@0: для парного узла не был ограничен брандмауэром.
igor@0: 
igor@0: В пример ниже есть строка:
igor@0: \begin{verbatim}
igor@0:  address 192.168.1.190:7792;
igor@0: \end{verbatim}
igor@0: она показывает, что синхронизация ресурса
igor@0: выполняется с узлом 192.168.1.190 и для сихнронизации используется
igor@0: порт 7792.
igor@0: 
igor@0: \paragraph{Метадиск}
igor@0: Лучше не использовать внуренний метадиск (meta-disk internal),
igor@0: особенно если вы собираетесь менять размер логического тома
igor@0: LVM и файловой системы на нём.
igor@0: 
igor@0: Нужно создать отдельный том для-
igor@0: meta-disk\rq{}ов DRBD и задать его размер
igor@0: равным 128MB x количество устройств.
igor@0: 
igor@0: В пример конфигурационного файла <tt>drbd.conf</tt>, 
igor@0: приведённом ниже, есть строка:
igor@0: \begin{verbatim}
igor@0:  meta-disk /dev/XEN/meta[1];
igor@0: \end{verbatim}
igor@0: Она говорит о том, что мета-информация
igor@0: об DRBD-устройстве, к которому относится эта строка,
igor@0: должна находится в мета-диске 1 (нумерация с нуля)
igor@0: на томе \texttt{/dev/XEN/meta}.
igor@0: Подготовка этого тома не выполняется каким-то
igor@0: особенным образом —  в данном случае это обычный
igor@0: логический том LVM, но вообще это может быть любое блочное
igor@0: устройство достаточного объёма.
igor@0: 
igor@0: Если метадиск создаётся на отдельном логическом
igor@0: томе LVM, то его можно расширять.
igor@0: Расширять метадиск нужно в том случае,
igor@0: когда количество DRBD-устройств, использующих
igor@0: его, превышает допустимое.
igor@0: Это число можно найти, разделив размер метадиска
igor@0: на объём, необходимый для каждого DRBD-устройства
igor@0: (в настоящий момент 128MB).
igor@0: 
igor@0: \paragraph{Пример секции файла drbd.conf}
igor@0: 
igor@0: \begin{verbatim}
igor@0: resource dns {
igor@0:     protocol C;
igor@0:     net {
igor@0:         allow-two-primaries;
igor@0:         after-sb-0pri discard-least-changes;
igor@0:         after-sb-1pri call-pri-lost-after-sb;
igor@0:         after-sb-2pri call-pri-lost-after-sb;
igor@0:     }
igor@0:     syncer {
igor@0:         rate 5M;
igor@0:     }
igor@0:     on dom0
igor@0:     {
igor@0:         device /dev/drbd1;
igor@0:         disk /dev/XEN/dns;
igor@0:         address 192.168.1.190:7792;
igor@0:         meta-disk /dev/XEN/meta[1];
igor@0:     }
igor@0:     on dom0m
igor@0:     {
igor@0:         device /dev/drbd1;
igor@0:         disk /dev/XEN/dns;
igor@0:         address 192.168.1.191:7792;
igor@0:         meta-disk /dev/XEN/meta[1];
igor@0:     }
igor@0: }
igor@0: \end{verbatim}
igor@0: 
igor@0: \subsubsection{Расширение диска DRBD}
igor@0: DRBD-устройство может менять свои размеры.
igor@0: Это возможно в том случае, если меняют размер (как правило, расширяются)
igor@0: устройства на которых базируется DRBD.
igor@0: Когда DRBD работает поверх логических томов LVM,
igor@0: желание расширение DRBD-устройства выглядит весьма естественно,
igor@0: поскольку изменение размеров LVM-томов
igor@0: является вполне простой и часто использующейся операцией;
igor@0: хочется, чтобы то, что работает поверх логического тома LVM
igor@0: могло отреагировать на изменение размеров.
igor@0: 
igor@0: Расширение DRBD-устройства и файловой системы, находящейся на нём, состоит из
igor@0: следующих шагов:
igor@0: \begin{enumerate}
igor@0: \item  Расширение \textit{логического тома}, на котором базируется DRBD-устройство на обоих узлах.
igor@0: \item  Отражение изменений в метадиске.
igor@0: \item  Расширение \textit{файловой системы} на primary-устройстве.
igor@0: \item  Проверка правильности выполнения.
igor@0: \end{enumerate}
igor@0: 
igor@0: Например, пусть:
igor@0: \begin{itemize}
igor@0: \item  машины называются \textit{primary} и \textit{secondary}
igor@0: \item  с помощью DRBD синхронизируется логический том /dev/TURBO/lv0
igor@0: \item  размер тома увеличивается на 2G
igor@0: \item  на томе создана файловая система ext2/ext3
igor@0: \end{itemize}
igor@0: Все указанные параметры являются необязательными
igor@0: и приведены для удобства повествования.
igor@0: 
igor@0: 1) Измените размер тома на обоих машинах.
igor@0: 
igor@0: \begin{verbatim}
igor@0: primary# lvresize -L +2048M /dev/TURBO/lv0
igor@0: secondary# lvresize -L +2048M /dev/TURBO/lv0
igor@0: \end{verbatim}
igor@0: 
igor@0: 2) Вызовите команду перестроения размера
igor@0: DRBD-устройства на обоих машинах:
igor@0: 
igor@0: \begin{verbatim}
igor@0: primary# drbdadm resize all  #(вместо all может быть указан только интересующий диск)
igor@0: secondary# drbdadm resize all
igor@0: \end{verbatim}
igor@0: 
igor@0: 3) На primary-устройстве измените размер файловой системы,
igor@0: расположенной на DRBD-устройстве:
igor@0: 
igor@0: \begin{verbatim}
igor@0: primary# ext2resize /dev/drbd0  # (или другое устройство)
igor@0: \end{verbatim}
igor@0: 
igor@0: 4) Проверьте, что изменение размера прошло успешно.
igor@0: 
igor@0: \begin{verbatim}
igor@0: primary# df -h /dev/drbd0
igor@0: \end{verbatim}
igor@0: 
igor@0: Проверка с помощью df возможна только в том случае,
igor@0: если \texttt{/dev/drbd0} смонтировано в настоящий момент.
igor@0: 
igor@0: Размер несмонтированной файловой системы можно тоже посмотреть,
igor@0: но только не в байтах, а в блоках.
igor@0: Для файловых систем ext2/ext3:
igor@0: 
igor@0: \begin{verbatim}
igor@0: %# dumpe2fs /dev/drbd0 | grep ^Block
igor@0: dumpe2fs 1.40-WIP (14-Nov-2006)
igor@0: Block count:              979933
igor@0: Block size:               1024
igor@0: Blocks per group:         8192
igor@0: \end{verbatim}
igor@0: 
igor@0: Указать домену Xen, использующему DRBD-устройство,
igor@0: что оно изменило размер, в настоящий момент,
igor@0: к сожалению, невозможно.
igor@0: 
igor@0: В будущем, сообщить об изменении конфигурации
igor@0: дискового устройства домена будет можно, предположительно,
igor@0: с помощью команды \textbf{xm block-configure}.
igor@0: 
igor@0: \subsubsection{Проверка DRBD перед стартом виртуальной машины}
igor@0: Можно сделать так, чтобы виртуальная машина
igor@0: вообще не стартовала до тех пор,
igor@0: пока соответствующее DRBD-устройство не будет переведено
igor@0: в состояние primary
igor@0: (на устройстве в состоянии secondary она может начать работать,
igor@0: но, когда внутреняя операционная система дойдёт до момента
igor@0: монтирования корневой файловой системы, загрузка прекратится; 
igor@0: в действительности это устройство вообще не используется до момента
igor@0: монтирования корневой системы, поэтому первый этап загрузки
igor@0: операционной системы домена проходит без ошибок).
igor@0: 
igor@0: Предположим, машина использует в качестве своего хранилища
igor@0: устройство /dev/drbd4. В таком случае,
igor@0: проверка будет выглядеть так:
igor@0: 
igor@0: \begin{verbatim}
igor@0: import os
igor@0: if os.system("grep '4:.*Primary/Secondary' /proc/drbd") != 0:
igor@0:     print "DRBD for this virtual machine not in primary state."
igor@0:     print "Exiting."
igor@0:     exit(1)
igor@0: \end{verbatim}
igor@0: 
igor@0: Эти строки нужно добавить в конфигурационный файл соответствующего
igor@0: домена Xen.
igor@0: 
igor@0: Пример использования:
igor@0: 
igor@0: \begin{verbatim}
igor@0: %$ sudo drbdadm secondary vpn
igor@0: 
igor@0: %$ sudo xm create -c vpn
igor@0: Using config file "/etc/xen/vpn".
igor@0: DRBD for this virtual machine not in primary state.
igor@0: Exiting.
igor@0: Error: 'str' object is not callable
igor@0: 
igor@0: %$ sudo drbdadm primary vpn
igor@0: 
igor@0: %$ sudo xm create -c vpn
igor@0: Using config file "/etc/xen/vpn".
igor@0:  4: cs:Connected st:Primary/Secondary ds:UpToDate/UpToDate C r---
igor@0: Started domain vpn
igor@0: Linux version 2.6.18-4-xen-686 (Debian 2.6.18.dfsg.1-11)
igor@0: (waldi@debian.org) (gcc version 4.1.2 20061115 (prerelease) (Debian
igor@0: 4.1.1-21)) #1 SMP Wed Feb 21 20:46:15 UTC 2007
igor@0: BIOS-provided physical RAM map:
igor@0:  Xen: 0000000000000000 - 0000000004800000 (usable)
igor@0: 0MB HIGHMEM available.
igor@0: 72MB LOWMEM available.
igor@0: .......
igor@0: \end{verbatim}
igor@0: 
igor@0: Когда DRBD-устройство \textit{vpn} было в резервном состоянии (secondary),
igor@0: создать домен не удалось.
igor@0: После того как оно было переведено
igor@0: в основное состояние (primary), домен успешно стартанул.
igor@0: 
igor@0: Если для управления доменами Xen используется скрипт \textit{xen-drbd},
igor@0: то эту проверку выполнять не нужно.
igor@0: Она автоматически выполняется скриптом.
igor@0: 
igor@0: \subsection{Дополнительная информация}
igor@0: \begin{itemize}
igor@0: \item \htmladdnormallinkfoot{Использование доменов Xen поверх DRBD-устройств}{http://xgu.ru/wiki/xen/drbd} (рус.)
igor@0: \end{itemize}




