UMGUM.COM (лучше) 

DRBD ( Зеркалирование дисковых устройств файловой системы. )

1 августа 2010  (обновлено 15 августа 2016)

OS: Debian Linux.

И так, мы имеем:

Два компьютера с одинаковым набором дисковых устройств. В том случае, если дисковые устройства не одинаковы - fdisk, cfdisk или LVM.
Четыре, шесть, восемь и так далее, количество кратное двум, компьютеров с одинаковым набором дисковых устройств.

Суть подхода в использовании "сетевого" RAID-10, с использованием протокола TCP/IP. Маршрутизируемый протокол даст нам возможность подключать в нашу структуру дисковые подсистемы удалённых компьютеров, а RAID-10 обеспечит высокий уровень сохранности данных. Разумеется, RAID-10 уменьшит эффективный объем нашей дисковой системы в два раза, но, надёжность требует иной раз и не такого.

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

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


На данном этапе нам необходимо определится с тем, какие из дисковых подсистем будут иметь статус первичных и будут зеркалироваться со вторичными дисковыми подсистемами на удалённых компьютерах. Чтение и запись будут осуществляться именно на первичных файловых системах. Блочные устройства, доступные для чтения и записи будут именно на компьютерах с первичными файловыми системами.

Инсталлируем DRBD на всех компьютерах входящих в нашу схему:

# aptitude search drbd

....
v   drbd8-module-source
v   drbd8-modules
....
p   drbd8-source        - RAID 1 over tcp/ip for Linux module source
p   drbd8-utils         - RAID 1 over tcp/ip for Linux utilities
p   drbdlinks           - Manages symlinks into a shared DRBD partition
....

Я полагаю, что оптимальным было бы загрузить исходные коды модуля DRBD и скомпилировать их с последующей установкой модуля встроенными средствами операционной системы:

# aptitude install drbd8-utils drbd8-module-source

Если операционная система свежеустановленная, то запрос на загрузку (подразумевается, что и на последующую установку) потянет за собой зависимости в виде библиотек и утилит сборки пакетов из исходных кодов, в частности и module-assistant.

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

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

# module-assistant auto-install drbd8

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

Пробуем инициировать установленный модуль:

# modprobe drbd

Смотрим файлы журналов и находим там сообщения, говорящие нам об успешной инициализации DRBD в системе:

....
[ 5563.051708] drbd: initialised.
[ 5563.055251] drbd: registered as block device major 147
[ 5563.057000] drbd: minor_table @ 0xdef02140
....

В директории "/etc/init.d" появился скрипт "drdb" инициализации DRBD при запуске операционной системы и корректного завершения работы сервиса при остановке операционной системы.

В директории "/etc" расположился конфигурационный файл drbd.conf, сделаем его резервную копию:

# cp /etc/drbd.conf /etc/drbd.conf.original

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

# ls /dev | grep sd

....
sda
sda1
....

Вызываем скрипт создания "жёстких" ссылок для разделов. Далее работать будем именно через ссылки типа "/dev/odskX.X":

# /etc/custom/hdd/dev/check-dev.sh

После отработки скрипта в директории "/dev" появятся символические ссылки для нас:

# ls -l /dev | grep odsk

....
lrwxrwxrwx 1 root root    9 odsk0.0 -> /dev/sda1
lrwxrwxrwx 1 root root    9 odsk0.1 -> /dev/sdb1
....
lrwxrwxrwx 1 root root    9 odsk0.X -> /dev/sdX1
....

Корректируем, а лучше переписываем конфигурационный файл "/etc/drbd.conf" до приблизительно следующего состояния на всех компьютерах участвующих в схеме синхронизации:

global {
  # отключаем функцию on-line статистики использования DRBD на сайте разработчиков
  usage-count no;
}

common {
  # выбираем протокол "C", как наиболее надёжный в зеркалировании, хотя и самый медленный
  protocol C;

  syncer {
    # указываем на лимит полосы пропускной способности используемой одним блоком DRBD (в MByte/s)
    rate 20M;
  }

  startup {
    # указываем DRBD ждать соединения с удалёнными ресурсами неограниченно долго
    wfc-timeout 0;
    degr-wfc-timeout 120;
  }

}

# описываем конфигурацию одного из блочных устройств, таких может и должно быть много (иначе зачем мы все это затевали бы)
resource drbd0 {

  handlers {
    local-io-error "command line script";
  }

  disk {
    # указываем в случае ошибки ввода-вывода локального диска вызвать описанный ранее скрипт реакции на указанную ошибку
    # on-io-error call-local-io-error;
    # в простейшем случае вместо вызова скрипта или в дополнение к нему - отключить диск
    on-io-error detach;
  }

  net {
    ping-timeout 20;
    # включаем аутентификацию между членами группы виртуального блочного устройства
    cram-hmac-alg sha1;
    shared-secret "strongPassword";
  }

  # именуем блочное устройство
  device      /dev/drbd0;
  # указываем на способ и место хранения информации об устройстве DRBD (в данном случае - прямо на дисках, задействованных в схеме DRBD устройства)
  meta-disk   internal;

  on node0.storage.local {
    # указываем на локальный ресурс члена блочного устройства
    disk        /dev/odsk0.0;
    # указываем IP адрес (к сожалению символически имена не принимаются) и порт к которому будет привязан этот член группы блочного устройства, для синхронизации каждого устройства используется индивидуальный порт, необходимо обеспечить отсутствие конфликта на интерфейсах
    address     10.10.2.21:7788;
  }

  on node1.storage.local {
    disk        /dev/odsk1.0;
    address     10.10.2.22:7788;
  }
}

При именовании DRBD устройств можно, конечно, проявить фантазию, но - не рекомендую. Имена /dev/drbdX, начинающееся с нуля наиболее универсальны и понятны. К тому, же в выводе DRBD в файле /proc/drbd нумерация устройств своя собственная, но начинающаяся с нуля; так что, для нас не будет проблемой сопоставить устройство "/dev/drbd0" и вывод об этом устройстве с индексом "0".

Указываем службе DRBD на всех компьютерах перечитать конфигурационные файлы (после первичного конфигурирования DRBD можно и перезапустить с помощью команды "/etc/init.d/drbd restart"):

# /etc/init.d/drbd reload

Просмотрим информацию о статусе;

# /etc/init.d/drbd status

Хорошо, если будет примерно так:

drbd driver loaded OK; device status:
m:res    cs            st  ds  p  mounted  fstype
0:drbd0  Unconfigured

В выводе выше слово Unconfigured говорит о том, что блочной устройство создано и запущено но не инициализировано. Исправим это:

Для вновь установленных устройств проведем процедуру создания так называемых "мета-данных", содержащих в себе всю необходимую информацию о блочном устройстве, для любого объёма дисковых устройств задействованных в виртуальном блочном устройстве объем этих данных будет равен 128 Мегабайтам. Команды отдаём на обеих сторонах:

# drbdadm create-md drbd0

Writing meta data...
initialising activity log
NOT initialized bitmap
New drbd meta data block sucessfully created.

Далее, на обеих серверах отдаем команды инициирования блочного устройства:

# drbdadm attach drbd0

На стороне ведущего сервера отдаем команду применения к нему параметров синхронизации и подключения виртуального блочного устройства:

# drbdadm syncer drbd0
# drbdadm connect drbd0

На самом деле, достаточно на дисковых устройствах провести процедуру создания "мета-данных" и заставить DRBD перечитать свой конфигурационный файл. При этом DRBD сам попытается подключить описываемые в нем устройства (во всяком случае, в Debian Lenny так и происходило).

Теперь вывод статуса покажет следующее:

drbd driver loaded OK; device status:
m:res    cs         st                   ds                         p  mounted  fstype
0:drbd0  Connected  Secondary/Secondary  Inconsistent/Inconsistent  C

В выводе выше слова "Secondary/Secondary" и "Inconsistent/Inconsistent" говорят о том, что DRBD не знает о том, с какого на какое устройство необходимо производить синхронизацию. Исправим это (процедура изменения статуса может производится достаточно долго, это не ошибка и не "зависание"):

В обычном, рабочем режиме нужно стараться сделать так, чтобы сумма скоростей для всех блоков DRBD, использующих какой либо сетевой интерфейс, не превысила скоростные возможности самого интерфейса; если этот же интерфейс используется для отображения в блокам DRBD сторонних приложений, нужно учитывать и то, что этим сетевым приложениям тоже нужна доля пропускной способности. Перед первичной синхронизацией нужно принять меры к временному увеличению скорости до максимально возможной для используемого сетевого интерфейса с целью максимально утилизировать пропускную способность интерфейса и скорейшего окончания объёмной, в смысле передачи данных, операции первичной синхронизации (для FastEthernet 100 "rate" примет значение в 12M, для GigabitEthernet 1000 "rate" примет значение в 120):

# drbdsetup /dev/drbd0 syncer -r 120M
# drbdadm adjust drbd0

На первичном узле отдадим команду инициализации, указывающую DRBD на то, что этот узел будет первичным и запускающую полную синхронизацию данных, иначе говоря, копирование содержимого диска первичного узла на вторичный (на самом деле перезапись вторичного члена узла данными с первичного - условность, диски пустые; в нашем случае это скорее операция по битного сравнения дисков, на случай обнаружения сбойных участков):

# drbdadm -- --overwrite-data-of-peer primary drbd0

Второй вариант команды синхронизации, более стандартного синтаксиса, на мой взгляд; опция "-o" обозначает то же самое, что и "-- --overwrite-data-of-peer" в первом варианте команды синхронизации (не понимаю, что подвигло разработчиков применить синтаксис опции с двумя последовательностями из двух "тире" в первом варианте - путаницу, тем самым, они успешно внесли):

# drbdsetup /dev/drbd0 primary -o

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

Первичная синхронизация - процедура затяжная. Например, для дисков размеров в 1 Терабайт и сетевых интерфейсов FastEthernet синхронизация может затянуться почти на сутки; с сетевыми интерфейсом GigabitEthernet дело пойдет заметное веселее, в десять раз быстрее.

Состояние непосредственно процедуры первичной синхронизации выглядит примерно следующим образом:

# /etc/init.d/drbd status

drbd driver loaded OK; device status:
m:res    cs          st                 ds                     p  mounted  fstype
...      sync'ed:    70.1%              (284140/949966)M
0:drbd0  SyncSource  Primary/Secondary  UpToDate/Inconsistent  C

# watch cat /proc/drbd

0: cs:SyncSource st:Primary/Secondary ds:UpToDate/Inconsistent C r---
    ns:684001084 nr:0 dw:0 dr:684017408 al:0 bm:41747 lo:1 pe:3 ua:256 ap:0
        [=============>......] sync'ed: 70.3% (282345/949966)M
        finish: 10:02:20 speed: 7,744 (11,236) K/sec
        resync: used:1/61 hits:42686243 misses:41727 starving:0 dirty:0 changed:41727
        act_log: used:0/127 hits:0 misses:0 starving:0 dirty:0 changed:0

После завершения процедуры синхронизации статус блока DRBD будет выглядеть примерно следующим образом:

# watch cat /proc/drbd

0: cs:Connected st:Primary/Secondary ds:UpToDate/UpToDate C r---
    ns:973122824 nr:0 dw:0 dr:973130984 al:0 bm:59394 lo:0 pe:0 ua:0 ap:0
        resync: used:0/61 hits:60738455 misses:59373 starving:0 dirty:0 changed:59373
        act_log: used:0/127 hits:0 misses:0 starving:0 dirty:0 changed:0

После завершения синхронизации не помешает вернуть блоку его рабочую скорость:

# drbdsetup /dev/drbd0 syncer -r 20M
# drbdadm adjust drbd0

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

В случае недоступности одного из физических устройств (перезапуск сервера, замена диска и тому подобное), состояние виртуального устройства будет следующим:

# /etc/init.d/drbd status

drbd driver loaded OK; device status:
m:res    cs            st                 ds                 p  mounted  fstype
0:drbd0  WFConnection  Secondary/Unknown  UpToDate/DUnknown  C

Слово "WFConnection" говорит о том, что виртуальное устройство находится в состоянии поиска своего "партнёра по синхронизации"; "DUnknown" - о том, что состояние удалённого устройства неизвестно, по понятным причинам.

Учитывая то, что в конфигурации виртуальных блочных устройств DRBD мы никак не обозначили их приоритетность, после перезапуска сервиса виртуального устройства состояние отношения "primary" / "secondary" является неопределённым. Это может показаться не совсем логичным, но, меня это вполне устраивает; в любом случае, перед применением виртуального блочного устройства его состояние и целостность необходимо проверять и выбирать в качестве "primary" то, что более удобно по тем или иным соображениям. Установить отношения между членами блока - элементарно:

Для первичного члена группы:

# drbdsetup /dev/drbd0 primary

Для вторичного члена группы (это не обязательно, но для отъёма статуса primary - пригодится):

# drbdsetup /dev/drbd0 secondary

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

Состояние подключения виртуального блочного устройства:

# drbdadm cstate drbd0

Connected

Состояние отношений членов блока:

# drbdadm state drbd0

Primary/Secondary

Информация о текущем состоянии членов блока:

# drbdadm dstate drbd0

UpToDate/UpToDate

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

В процессе построения требуемой схемы приходилось обращаться к содержимому скрипта управления DRBD, размещаемого в директории /etc/inid.t/, он мне не особо понравился и я его слегка подрезал, для простоты:

# cat /etc/init.d/drbd

#!/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
DATE=`date +"%Y-%m-%d %H:%M:%S"`

case "$1" in
  start)
    # Запускаем скрипт создания символических ссылок на используемые блочные устройства
    /etc/custom/hdd/dev/check-dev.sh
    # Проверяем, не загружен ли уже модуль DRBD
    if [ -e /proc/drbd ]
    then
      echo "DRBD resources already started."
    else
      # Загружаем модуль DRBD
      echo "Starting DRBD driver."
      modprobe drbd
      # Выжидаем минуту
      echo "Waiting one minute for correct starting DRBD resources..."
      drbdadm up all
      sleep 30
      # Проверяем успешность загрузки модуля DRBD по наличию созданных им служебных конструкций
      if [ -e /proc/drbd ]
      then
        echo >&2 "DRBD driver started."
      else
        echo "..."
        echo >&2 "Wrong starting DRBD resources."
        exit 3
      fi
    fi
  ;;
  stop)
    echo -n "Stopping all DRBD resources"
    if [ -e /proc/drbd ]
    then
      drbdadm down all
      rmmod drbd
    fi
    echo "."
  ;;
  status)
    if [ -e /proc/drbd ]
    then
      echo "DRBD driver loaded OK; Status:"
      cat /proc/drbd
      exit 0
    else
      echo "DRBD driver not loaded."
      exit 3
    fi
  ;;
  reload)
    # Запускаем скрипт проверки символических ссылок на используемые блочные устройства
    /etc/custom/hdd/dev/check-dev.sh
    echo "Reloading DRBD configuration"
    drbdadm adjust all
    echo "."
  ;;
  restart|force-reload)
    echo >&2 "Restarting all DRBD resources."
    $0 stop
    sleep 60
    $0 start
  ;;
  *)
    echo "Usage: /etc/init.d/drbd {start|stop|status|reload|restart|force-reload}"
    exit 1
  ;;
esac

exit 0

В оригинальном скрипте управления DRBD была процедура, обрабатывающая статусный вывод до более "человеко-понятного" вида; из нежелания нагромождать скрипт эта процедура была мною убрана и вывод не намного "пострадал" в понятности (желающие могут скопировать код процедуры из оригинального скрипта и подставить его во вновь созданный).


Заметки и комментарии к публикации:


Оставьте свой комментарий ( выразите мнение относительно публикации, поделитесь дополнительными сведениями или укажите на ошибку )