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 , Lars Ellenberg 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: В пример конфигурационного файла drbd.conf, 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}