Поддержание работоспособности серверов с помощью экстренного дублирования виртуальными копиями

Материал из DvoWiki
Перейти к: навигация, поиск

Общие соображения

Предполагается создание резервной виртуальной ситемы, дублирующей работу сервера в случае его выхода из строя. Это решение направлено на устранение задержек в работе систем при выходе из строя одного или нескольких узлов. Выбор ипользования виртуальной копии рабочего сервера связан с проблемами изоляции процессов, являющихся главными для машины которая будет производить дублирование, от процессов которые она будет дублировать. Это актуально для тех случаев когда мы не можем себе позволить по какой-либо причине использовать другую машину для нужд экстренного дублирования. Решение этой задачи включает в себя ряд подзадач, таких как:

  1. Обеспечения синхронизации данных между рабочей системой и её виртуальной копией.
  2. Поддержания идентичности сервисов рабочей системы и виртуальной копии.
  3. Настройка механизма слежения за работой сервисов (выбор и настройка системы, которая будет решать что рабочий сервер недоступен и следует включить его копию)

Создание виртуальной копии системы с использованием KVM/QEMU

Обоснование выбора виртуальной машины

Для эмуляции системы было решено использовать KVM. Данное решение было продиктовано следующими факторами:

  1. Процессоры серверов, используемых для дублирования имеют поддержку виртуализации, что является условием работы KVM.
  2. Поддержка KVM включена в стандартное ядро.
  3. Модуль kqemu виртуальной машины QEMU имеет ошибки при работе с 64 битной архитектурой.

Установка виртуального системы

Установка системы может быть востребована только в тех случаях, когда необходима дублировать только отдельные сервисы.

Дублируемая система имеет 64 битную архитектуру. Используемый дистрибутив Gentoo Linux.

Конфигурирование ядра

Необходимо установить флаги в ядре для поддержки KVM.

$cd /usr/src/linux
$make menuconfig
>>Virtualization-> Kernel-based Virtual Machine (KVM) support  

После этого можно установить необходимые пакеты.

 $emerge kvm
 $emerge sys-apps/usermode-utilities

Создание образа диска:

$kvm-img  create Gentoo_x86_64_root.img 5G
$fdisk -C 652 Gentoo_x86_64_root.img
Команда (m для справки): p
Диск Gentoo_x86_64_root.img: 0 МБ, 0 байт
255 heads, 63 sectors/track, 652 cylinders
Units = цилиндры of 16065 * 512 = 8225280 bytes
Disk identifier: 0xd947012a
                 Устр-во Загр     Начало       Конец       Блоки   Id  Система
 Gentoo_x86_64_root.img1               1         652     5237158+  83  Linux

Создаём диск для базовой системы и форматируем его с геометрией 255 головок, 63 сектора, 652 цылиндра. Размер блока 512 килобайт. Далее создаём устройство /dev/loop0:

losetup -o 32256 -f Gentoo_x86_64_root.img 

и форматируем его

mkreiserfs -j /dev/loop0 /dev/loop0
losetup -d /dev/loop0

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

Также, чтобы ядро системы запускалось из загрузчика (grub,lilo) надо записать MBR на образ. Mbr берётся из пакета syslinux.

emerge syslinux

Записывается на образ:

dd if=/usr/lib/syslinux/mbr.bin of=Gentoo_x86_64_root.img conv=notrunc

Также, для работы виртуальной системы может понадобится swap партиция. Можно конечно разбить образ, на котором предполагается разместить систему с учётом использования swap. Но в натоящее время предполагается использование отдельного образа под swap.

qemu-img  create Swap.img 1G
fdisk -C 131 -u Swap.img
Команда (m для справки): p
Диск Swap.img: 0 МБ, 0 байт
255 heads, 63 sectors/track, 131 cylinders, всего 0 секторов
Units = секторы of 1 * 512 = 512 bytes
Disk identifier: 0x2318a83a
Устр-во Загр     Начало       Конец       Блоки   Id  Система
Swap.img1              63     2104514     1052226   82  Linux своп / Solaris
mkswap Swap.img

Установка базовой системы

!!!ВАЖНО Установку отдельной системы следует проводить лишь в том случае, если нужно дублирование отдельных сервисов. В этом случае синхронизироваться будут только настройки и данные для данных сервисов. В нашем же случае подразумевается иметь полную виртуальную копию машины, которая задействуется на время выхода из строя последней. Установка базоваой системы происходит обычным для Gentoo способом. Из особенностей стоит заметить только конфигурирование ядра под виртуальную машину и конфигурирование загрузчика. Для начала примонтируем образ к системе

mount -o loop,offset=32256 -t reiserfs Gentoo_x86_64_root.img /mnt/img/

Далее действуем в соответствии с Gentoo Handbook

После установки загрузчика конфигурирование его должно происходить СНАРУЖИ от chroot системы.

$umount /mnt/img
$grub --no-floppy 
grub> device (hd0) Gentoo_x86_64_root.img
grub> geometry (hd0) 652 255 63
grub> root (hd0,0)
grub> setup (hd0)

Синхронизация ситемы донора с образом ВМ

Здесь описыается процесс синхронизации системы донора с диском расположенным на другой сетевой машине. Синхронизация происходит периодически и выполняется с использованием rsync. В нашем случае виртуализация выполняется на машине к которой с помошью iscsi монтируется дисковое пространство на другой машине. То есть мы имеем слудующую схему:

машина донор (rsync)-> машина хост <-(iscsi) машина предоставляющая дисковое пространство.

Для машины на которой бедет хостится ВМ iscsi устройство видится как обычное блочное устройство, с той лишь разницей, что при использовании (например при монтировании его в две разные точки) его двумя разными процессами могут произойти ошибки в файловой системе. Синхронизация выполняется посредством следующего скрипта:

#!/bin/bash                                                                                                                        

ISCSI_ROOT_DEV="/dev/sdb"
ISCSI_MOUNT_POINT="/mnt/img"
ISCSI_DEV_FOR_VIRT_MASHINE="/dev/sdc"

PID_FILE="/var/run/rsync_ftp.pid"
EXCLUDE_FILE="./EXCLUDE_PATERNS"

#FIND if anybody already use this iscsi device                                                                                     
VIRT_MASHINE_PID=$(ps -aef | grep "$ISCSI_DEV_FOR_VIRT_MASHINE" | grep -v grep | awk '{print $2}')
#For begining let's mount iscusi device to directory                                                                               
IS_MOUNT=`mount | grep $ISCSI_ROOT_DEV | grep -v grep | awk '{print $2}'`
if  -z $VIRT_MASHINE_PID ; then
   if [ "$IS_MOUNT" = "on" ]; then
       echo -n "ISCSI device already mount, exit"
       echo ""
       echo -n "Data can be inconsisternt"
       echo ""
       exit 1
   else
 `mount $ISCSI_ROOT_DEV $ISCSI_MOUNT_POINT`
       echo "mount iscsi device $ISCSI_ROOT_DEV to $ISCSI_MOUNT_POINT and exit with code $?"
       echo ""
       if [ $? -ne 0 ]; then
           echo "Some problems from mount"
           echo ""
           exit 1
       fi
   fi
else
   echo "Device alredy in use by $VIRT_MASHINE_PID. Seems by virtual mashine. In this case we mast stop to worck."
   echo ""
   exit 1
fi

echo "Search for Exclude file: $EXCLUDE_FILE"
echo ""
if  -f "$EXCLUDE_FILE" ; then
   echo "Done."
   echo ""
else
   echo "Exclude file not exist i'm now will create new one"
   echo ""
   $(touch ./$EXCLUDE_FILE)
fi

echo "worck" > $PID_FILE

LIST="bin root home opt tmp var boot etc lib sbin usr lib64 lib32 dev"
#LIST="tmp"                                                                                                                        

for d in $LIST; do
   rsync --verbose  --progress -o -g --recursive --times --perms --links --delete --rsh=ssh --exclude-from="./$EXCLUDE_FILE" root\
@tratata.com:/$d $ISCSI_MOUNT_POINT/
done


#Ufter hard worck let's unmount iscsi device                                                                                       
IS_MOUNT=`mount | grep $ISCSI_ROOT_DEV | awk '{print $2}'`
if [ "$IS_MOUNT" = "on" ]; then
   UMOUNT=$(umount $ISCSI_ROOT_DEV)
   if [ $? -ne 0 ]; then
       echo "Some trubles during unmounting"
   fi
   echo -n "UMOUNT ISCSI device "
   echo ""
else
   echo "Somthing wrong device already ummount"
   echo ""
fi

$(rm $PID_FILE)
exit 0

ЗАМЕЧАНИЕ Перед началом использования может быть необходимо скопировать на диск куда будет происходить синхронизация каталог dev из третьего stag-а. Без этого некоторые устройства могут не создаться что приведёт к тому что ядро просто не загрузится.

Запуск виртуальной машины на основе копии системы

Когда у нас есть полная копия данных можно приступать к запуску виртуальной машины. Для начала нам надо поправить файлы, которые отвечают за IP адрес ВМ, имя хоста и расположение устройств.

/etc/fstab
/etc/conf.d/net
/etc/conf.d/hostname

Для того чтобы виртуальная машина смогла проинициализировать необходимое оборудование, нужно включить в ядре поддержку этого оборудования. Особое внимание здесь стоит уделить включению драйвера для сетевой карты rtl8139, так как эта карта эмулируется виртуальной машиной. Также понадобится ядро эмулируемой системы, находящееся в одной папке со скриптом, здесь оно представленно как bzI-2.6.26.5. Запуск системы производится следующим скриптом

#!/bin/bash
#
User=$USER
MAC="52:54:00:12:34:56" #незабудьте поменять мак если у вас много виртуальных машин
VIRT_IP="1.1.1.1" #ADRESS OF YOUR VIRT MACHINE
PROGRAM="kvm"
KVM_PID=/var/run/run_.pid
TAP_NAME_FILE=/var/run/tap_.store
DEV_TO_LOAD="/dev/sdc"

RSYNC_PID_FILE="/var/run/rsync_ober.pid"

PID=`ps -aef | grep "$PROGRAM" | grep -v grep | awk '{print $2}'`
echo $PROGRAM
echo $PID



function if_rsync_worck(){
   if [ -f "$RSYNC_PID_FILE" ]; then
       echo "RSYNC is running, please stop rsync and umount iscsi device"
       echo ""
       exit 1
   fi
   return 0
}

case "$1" in
 start)
       if_rsync_worck
   if [ ! -z $PID ]
   then
   echo -n "Daemon KVM is running"
   echo ""
else 
   echo -n "Starting KVM: "
   echo ""
   TAP=$(tunctl -b -u $User)
   /sbin/ifconfig $TAP up
   echo 1 > /proc/sys/net/ipv4/ip_forward
   route add -host $VIRT_IP dev $TAP
   arp  -i eth0 -s $VIRT_IP $MAC pub
   echo 1 > /proc/sys/net/ipv4/conf/tap0/proxy_arp

   #PROG_BIN="kvm  -kernel ./bzI-2.6.26.5 -append \"console=tty0 console=ttyS0 root=/dev/sda1  rw\" -nographic  -hda /dev/sdc1  -m 1000 -smp 2 - serial stdio -net nic,macaddr=$MAC,model=rtl8139 -net tap,ifname=$TAP,script=no"
   #echo $PROG_BIN
   #$PROG_BIN #> /dev/null 2>&1 & 
   kvm  -kernel ./bzI-2.6.26.5 -append "console=tty0 console=ttyS0 root=/dev/sda1  rw" -nographic  -hda $DEV_TO_LOAD -m 1000 -smp 2 -serial stdio - net nic,macaddr=$MAC,model=rtl8139 -net tap,ifname=$TAP,script=no > /dev/null 2>&1 &
    echo $! > $KVM_PID
    echo $TAP > $TAP_NAME_FILE
    echo "Daemon KVM started. PID:$!"
    echo ""
 fi
   ;;
 stop)
   echo -n "Shutting down KVM: "
   echo ""
   STORED_PID=`cat $KVM_PID`
   for i in $PID; do
   if [ "$i" = "$STORED_PID" ]; then
       kill -9 $i
   fi
   rm -f $KVM_PID
   done
   TAP=`cat $TAP_NAME_FILE`
   for i in $TAP;do
       sleep 1s 
   tunctl -d $i
   rm -f $TAP_NAME_FILE
   done
   arp -i eth0 -d $VIRT_IP
   echo -n "Daemon KVM is down."
 echo ""
   ;;
 restart)
   $0 stop
   $0 start

   ;;
 status)
   STATUS=`ps -aef | grep "$PROGRAM" | grep -v grep | awk '{print $2}' | head -1`
   if [ ! -z $STATUS ]
   then
   echo -n "Daemon KVM is running"
   echo ""
   for i in $PID; do
   echo PID:$i
   done
   echo ""
   else 
   echo -n "Daemon KVM is down."
   echo ""
   fi
   ;;
 *)
   echo "Usage: start_stop.sh {start|stop|restart|status}"
   exit 1
     
esac
exit 0

Для проверки работоспособности системы может понадобится интерактивный режим взаимодействия, для этого пригодится следующий скрипт, запускающий виртуальную машину с выводом на консоль:

#!/bin/bash
set -x
User=$USER
MAC="52:54:00:12:34:56"
VIRT_IP="1.1.1.1"
TAP=$(tunctl -b -u $User)
/sbin/ifconfig $TAP up
echo 1 > /proc/sys/net/ipv4/ip_forward
route add -host $VIRT_IP dev $TAP
arp  -i eth0 -s $VIRT_IP $MAC pub
echo 1 > /proc/sys/net/ipv4/conf/tap0/proxy_arp
kvm  -kernel ./ftp_dvo_ru_bzI-2.6.26.8 -append "console=tty0 console=ttyS0 root=/dev/sda  rw" -nographic  -hda /dev/sdb  -m 1000 -smp 2 -serial stdio -net nic,macaddr=$MAC,model=rtl8139 -net tap,ifname=$TAP,script=no
tunctl -d $TAP
arp -i eth0 -d $VIRT_IP
exit 0

Включение виртуальной системы в сеть

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

  1. использование машины хоста как комутатора, между виртуальной машиной и внешней сетью (при этом виртуальная машина находится в своей подсети)
  2. использование технологии моста (bridging), когда сетевой интерфейс хоста представляет из себя мост, в который заведены интерфейсы виртуальных машин
  3. добавление пути маршрутизации на машине хосте.

Первые два метода имеют свои недостатки. Так, для первого метода может оказаться, что подсеть ипользуемая виртуальной машиной, уже используется в таблице маршрутеризации сети. Во втором случае интерфейс хостовой машины переводится в promiscas mode, что при сильной загрузке может негативно сказаться на производительности. Также в при попытке натроить мост на нашем серверном оборудовании наблюдались некоторые сбои в работе драйвера сетевой карты. Ссылки на эти два метода приводятся в конце статьи.

Добавление маршрута, для виртуальной машины

Допустим виртуальную машину необходимо запустить в подсети 192.168.2.0/255.255.255.0 и адрес виртуальной машины 192.168.2.29. Сначала, создаётся tap интерфейс, представляющий собой тунель канального уровня между виртуальной машиной и хостом.

tunctl -u username
ifconfig tap0 up

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

echo 1 > /proc/sys/net/ipv4/ip_forward
route add -host 192.168.2.29 dev tap0

Теперь необходимо сделать так, чтобы виртуальная машина идентифицировалаь в сети, для этого настроим arp таблицу машины хоста.

arp  -i eth0 -s 192.168.2.29 52:54:00:12:34:56 pub

Таким образом, интерфейс eth0 хоста будет принимать пакеты предназначенные для виртуальной машины. Далее необходимо настроисть arp proxy интерфейса tap0. Это делается для того чтобы на arp виртуальной машины запрос возвращался мак адрес машины хоста.

echo 1 > /proc/sys/net/ipv4/conf/tap0/proxy_arp

Осталось только настроить сеть в виртуальной машине.

После окончания работы необходимо убрать за собой мусор в виде tap интерфейса и записей в arp и route таблицах:

tunctl -d tap0
arp -i eth0 -d 192.168.2.29

Удаление интерфейса tap0 автоматом приводит к удалению маршрута из таблицы маршрутеризации. Второй командой удаляем запись из arp таблицы.

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

#!/bin/bash
set -x
User=$USER
MAC="52:54:00:12:34:56"
TAP=$(sudo tunctl -b -u $User)
/sbin/ifconfig $TAP up
echo 1 > /proc/sys/net/ipv4/ip_forward
route add -host 192.168.2.29 dev $TAP
arp  -i eth0 -s 192.168.2.29 $MAC pub
echo 1 > /proc/sys/net/ipv4/conf/tap0/proxy_arp
#команда для запуска виртуальной машины находится здесь
kvm -hda ./FreeBSD.cow  -m 485  -net nic,macaddr=$MAC,model=rtl8139 -net tap,ifname=$TAP,script=no
tunctl -d $TAP
arp -i eth0 -d 192.168.2.29

ВАЖНО при эмуляции сетевого интерфейса NE2000, возникали сбои в работе драйвера ne2k_pci при появлнии нагрузки на сетевом интерфейсе. Поэтому было решено прописывать эмулируемое железо в опциях запуска виртуальной машины (-net nic,macaddr=$MAC,model=rtl8139 ).

Ссылки

  1. HOWTO Gentoo KVM
  2. HOWTO Gentoo Qemu
  3. Qemu и локальная сеть (c использованием NAT)