И так, мы имеем:
Два компьютера с одинаковым набором дисковых устройств. В том случае, если дисковые устройства не одинаковы - 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
....
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
....
[ 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
....
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
....
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;
}
}
# отключаем функцию 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
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.
initialising activity log
NOT initialized bitmap
New drbd meta data block sucessfully created.
Далее, на обеих серверах отдаем команды инициирования блочного устройства:
# drbdadm attach drbd0
На стороне ведущего сервера отдаем команду применения к нему параметров синхронизации и подключения виртуального блочного устройства:
# drbdadm syncer drbd0
# drbdadm connect 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
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
# 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
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
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
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
# 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
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
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 была процедура, обрабатывающая статусный вывод до более "человеко-понятного" вида; из нежелания нагромождать скрипт эта процедура была мною убрана и вывод не намного "пострадал" в понятности (желающие могут скопировать код процедуры из оригинального скрипта и подставить его во вновь созданный).
Переход к настройке контроля за зеркалированием дисковых устройств файловой системы.
27 февраля 2012 в 16:59
27 февраля 2012 в 19:15
27 февраля 2012 в 19:19
27 февраля 2012 в 20:25
27 февраля 2012 в 20:42
27 февраля 2012 в 20:52