среда, 16 февраля 2011 г.

Непрерывная репликация ZFS во FreeBSD

Сегодня речь пойдет о возможностях непрерывной репликации ZFS между хостами FreeBSD.





Цели

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

Первоначальная задумка выглядела следующим образом:


* Основной сервер виртуализации
* Сервер горячей замены (по своим характеристикам близкий, но не идентичный основному)
* Коммерческое ПО для виртуализации, не предполагалось, только OpenSource.

Задача абсолютной бесперебойной работы не ставилась, основной критерий -
быстрое (в пределах  5 минут) время восстановления IT сервисов в случае отказа
основного сервера, с минимальной потерей пользовательских данных. Другими
словами, если основной сервер выходит из строя, то все данные пользователей,
сохраненные за несколько минут до отказа, не должны быть утеряны.

Вариант с общим NAS, разделяемый между серверами виртуализации, по ряду причин,
в том числе и финансовых, так же не рассматривался.

Таким образом, данную конфигурацию можно кратко охарактеризовать, как среда
виртуализации начального уровня с элементами отказоустойчивости.


Средства

В качестве хост системы выступает FreeBSD 8.1 с гипервизором VirtualBox OSS.

Я не буду подробно останавливаться на процессе установки и настройки
VirtualBox, скажу только то, что оба сервера и основной и резервный имеют
идентичное ПО, а все конфигурации и образы виртуальных серверов расположены
внутри ZFS с точкой монтирования /var/IMAGES.

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

Очевидным является тот факт, что чем чаще происходит синхронизация, тем лучше.
Так же очевидно что rsync, не смотря на все его прелести, не способен
обеспечить синхронизацию заданными выше временными рамками.
Беглый поиск выявляет упоминания о коммерческих реализациях zfs репликации а
так же стандартных возможностях ZFS создания,  и что самое важное, обмена ими
между хостами посредством zfs send/receive.
Именно zfs send/receive будет использован для непрерывной репликации в режиме
близкому к реальному времени.

Репликацией должен заниматься скрипт, запускаемый из cron, интервал запуска 1 минута.

Скрипт запускается на резервном сервере и должен:

* Создать новый снапшот на основном сервере.
* Все снапшоты, задействованные в процессе репликации, должны иметь некую
отличительную особенность (префикс в своем имени).
* Выявить самый свежий общий снапшот, относительно которого, можно провести
инкрементальную репликацию. В случае его отсутствия провести полную репликации.
* Провести репликацию через send/receive
* Удалить неиспользуемые снапшоты.
* Запротоколировать все свои действия.

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

* запускать через скрипт команды на удаленном сервере от имени рута ( а значит
хранить или пароль или ключ рута) не слишком хорошая идея.
* ZFS на FreeBSD до сих пор не дружит с ZFS Delegated Administration

Учитывая эти два факта, следует завести на основном сервере на
привилегированного пользователя и выполнять команды zfs через sudo , разрешив
ему необходимый минимум.

В файле /usr/local/etc/sudoers.d/zfs_replication определяем для пользователя следующие разрешения

   synczfs      ALL = NOPASSWD : /sbin/zfs snapshot *, /sbin/zfs send *,/sbin/zfs list *,/sbin/zfs destroy *

А теперь непосредственно скрипт /root/zfs-nrt-replication.sh

   #!/bin/sh
   REMOTE_HOST="master.local"
   FS="/var/IMAGES"
   REMOTE_POOL="tank0"
   LOCAL_POOL="tank0"
   SNAPSHOTMARK="SYNC-"


   SSHKEY="/root/syncimages_id"
   SSHFLAGS="-i $SSHKEY -o CheckHostIP=no -o StrictHostKeyChecking=no \
   -o IdentitiesOnly=yes -o BatchMode=yes -o  GlobalKnownHostsFile=/dev/null -l synczfs"
   SSHCMD="/usr/bin/ssh $SSHFLAGS $REMOTE_HOST sudo "
   INCREMENTAL=""

   (

   echo "Snapshoting remote FS"

   $SSHCMD "zfs snapshot -r $REMOTE_POOL$FS@$SNAPSHOTMARK`uuidgen`"

   RECENT_COMMON_SNAPSHOT=""

   for R in `$SSHCMD zfs list -H -o name -s creation -t snapshot -r $REMOTE_POOL$FS | grep $REMOTE_POOL$FS@`
   do
      for L in `zfs list -H -o name -s creation -t snapshot -r $LOCAL_POOL$FS | grep $LOCAL_POOL$FS@`
      do
          if [ "${R##*@}" = "${L##*@}" ]; then
             RECENT_COMMON_SNAPSHOT=${R##*@}
             INCREMENTAL="-I $REMOTE_POOL$FS@$RECENT_COMMON_SNAPSHOT"
          fi
      done
      REMOTE_LATEST_SNAPSHOT=${R##*@}
   done

   echo "Syncronizing remote FS to local"

   echo "zfs send -R $INCREMENTAL $REMOTE_POOL$FS@$REMOTE_LATEST_SNAPSHOT"

   $SSHCMD "zfs send -R $INCREMENTAL $REMOTE_POOL$FS@$REMOTE_LATEST_SNAPSHOT" | \
   /usr/local/bin/pipemeter --autooff --blocksize 16M --interval 60 --log | zfs receive -dF $LOCAL_POOL

   if [ $? -eq 0 ]; then
      echo "Cleaning useless remote snapshots"
      $SSHCMD "zfs list -r -t snapshot -o name -H $REMOTE_POOL$FS  | grep \"$SNAPSHOTMARK\" | \
      egrep -v  \"@($RECENT_COMMON_SNAPSHOT|$REMOTE_LATEST_SNAPSHOT)\" | xargs -I% sudo zfs destroy %"
   fi

   echo "Done."
   ) 2>&1 | logger -s -p ftp.info -t SYNCZFS

Сам скрипт запускается на резервном сервере из cron следующим образом

   */1 * * * * root  /usr/sbin/daemon -p /var/run/zfs-sync.pid -f /root/zfs-nrt-replication.sh > /dev/null

Итак, на резервном сервере скрипт запускается каждую минуту, но если он еще не
закончил свое выполнение с прошлого запуска, то попытка пропускается.  Весь
вывод скрипта направляется в лог-файл /var/log/xferlog. Для более наглядной
оценки скорости используется дополнительное ПО pipemeter, в результате чего
в лог файл, с минутным интервалом, добавляется информация об объеме
передаваемых данных, а так же скорости обмена.

Дополнительно, можно настроить автоматический снапшотинг, чтобы иметь версии
виртуальных машин различной степени давности (часовой, дневной, недельной и
т.д.). Эти версии так же будут реплицироваться на резервный сервер.

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

1 комментарий: