О блоге

Все новые материалы размещаются на Блогосайте alv.me. Старые - в процессе переноса.

26.07.2008

Программный RAID во FreeBSD

2004-2005 гг

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

Ответов, как обычно, несколько. Во-первых, всегда полезно понимать, что стоит за front-end'ными утилитами, особенно - за такой универсальной и многогранной, как sysinstall. В-вторых же, применение sysinstall для послеустановочного конфигурирования иногда бывает нежелательным, в ряде случаев она любит внести кое-какую отсебятину в уже настроенные конфигурационные файлы. Не очень существенную, но, тем не менее, требующую внимания. Наконец, в-третьих - есть несколько видов установки и настройки, которые либо нельзя выполнить через sysinstall полностью, либо легче сделать без нее.

В этом разделе я остановлюсь на одном из таких частных случаев установки FreeBSD - на программный RAID-массив нулевого уровня на машине в двухдисковой конфигурации.

В данном случае речь идет о материнской плате Albatron на чипсете i845PE, с дополнительным контроллером IDE-RAID/SerialATA имени Promise FastTrak 376, однако все сказанное приложимо и к другим конфигурациям. В качестве дисков выступали 2 Seagate Barracuda IV, 80 Гбайт, один из которых работал мастером на 1-м встроенном IDE-канале, а второй - был единственным устройством на IDE-RAID/UATA133.

Этот самый RAID был сделан так, что массив на нем можно было устроить из трех (ей же ей, так в документации) дисков - одного на разъеме UATA133 и двух - на SerialATA (именно столько их и было). Причем каждое устройство должно было быть мастером на своем разъеме, к коим устройств типа CD ROM подключать не следовало (и действительно, не получалось). А аппаратно RAID'ы поддерживались уровней 0 и 1 (интересно, как это можно устроить mirroring для трех устройств - но, повторяю, так в документации было сказано).

Хотя вопросы аппаратного RAID'а меня не волновали - дисков SerialATA у меня не было, переходника Serial/Parallel ATA - также. Так что оставалось озаботиться только RAID'ом программным.

Я позволил себе задержаться на конфигурации потому, что перед этим она доставила мне немало веселья при общении с Linux'ами. Теоретически я всегда знал, что два современных винта на одном IDE-канале - вещь не самая быстрая. Но насколько она не быстра - понял, только когда (в Gentoo Linux, а затем - в самостройном) устроил на этом хозяйстве (две Барракуды на 1-м IDE) LVM-разделы. Оказалось, что даже перенести второй диск мастером на второй канал (в паре с CD) - и то быстрее было (хотя тогда началось экстремальное торможение при записи с CD). А целых три разъема дополнительного FastTrak'а бездействовали - ни одно из тогдашних ядер Linux видеть на нем винтов не желало в упор (собственно, конечно, только винтов на Parallel ATA, но, подозреваю, что и с сериальными была бы та же история). Так что когда Free пять-первой, еще беты, версии, этот винт увидела - был весьма рад...

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

Собственно говоря, для использования двух винчестеров под одной ОС RAID-массив вовсе не обязателен - достаточно было бы создать на втором BSD-слайс, разметить его как один логический раздел, создать на нем файловую систему и смонтировать ее в один из подкаталогов /home/user_имя_рек (мой домашний каталог - главный пожиратель дискового пространства). Просто, но не верх удобства.

Нужно все время помнить, что пользовательский каталог на самом деле не только образован двумя файловыми системами, но и лежит на двух винтах. Что, кроме всего прочего, накладывает ограничения на жесткие ссылки внутри него. Мы ведь помним, что жесткие ссылки могут существовать только в единой файловой системе. А для чего они нужны в домашнем каталоге - ответить не трудно: это один из простых и дешевых (с точки зрения расхода как дискового пространства, так и времени) способов подстраховаться от случайного удаления данных. Смахнешь, бывалыча, в азарте файл, показавшийся ненужным, хватишься - ан глянь, он в виде жесткой ссылки сидит где-нибудь в /home/user_имя_рек/dubles_data, и есть не просит...

Так что организовать два диска в виде единого пространства - безусловно, удобнее. А для этого в природе предусмотрено два механизма (кроме аппаратного RAID'а, но с ним, как уже говорилось - облом): программный RAID и механизм логических томов. Последний, насколько я понимаю, встроен в файловую систему JFS, относительно поддержки которой во Free информация промелькнула. Однако в текущих версиях (от 5.1 до 5.2.1) никаких упоминаний на сей счет я не обнаружил - в файле описания конфигурации ядра /usr/src/sys/conf/NOTES про JFS не говорилось ни слова. Да и от краткого с ней знакомства под Linux'ом впечатлений чего-то выдающегося она не оставила...

Так что оставался только программный RAID. Разумеется, level 0 - поскольку зеркалирование просто не соответствовало задаче, а level 5 показался сложным и неоправданным. Вообще, ИМХО, RAID-массивы с избыточностью на настольной машине - баловство: от ошибок пользователя (причины 99% потери данных в домашних условиях) они не страхуют (а иногда способны даже усугубить их последствия). От аппаратных же сбоев - только в том случае, если в столе лежит запасной винчестер на смену рухнувшему. Ситуация, вероятно, не редкая для админа локальной сети - но практически исключенная на дому; по крайней мере, если бы у меня завалялся незадействованный винчестер, я нашел бы десятки способов пристроить его к делу...
<>Изучение вопроса показало, что под FreeBSD, как это в ней обычно бывает, есть не один способ реализации программного RAID-0: исторический (вернее, в масштабах индустрии - почти доисторический) ccd и более современный vinum. Последний, представляя собой, как можно догадаться из его man-страницы, нечто вроде менеджера логических томов, считается более гибким. Однако я остановился на ccd - рациональное объяснение этому дать затрудняюсь.
Подготовка к слиянию

Итак, ccd (Concatenated Disk driver), что в переводе означает - драйвер слияния дисков. Средство, как я уже сказал, весьма древнее - man-страница датирована 1995 г., - а значит, проверенное временем. Позволяет создавать программные RAID-массивы типа нулевого (stripping) и первого (mirroring) уровней. Как уже говорилось, меня интересовал только нулевой.

Важное замечание: в отличие, опять же, от Linux, во FreeBSD разместить на программном RAID'е средствами ли ccd корневую файловую систему нельзя (при использовании vinum это, как описано во FreeBSD Handbook, возможно - хотя опять-таки не через sysinstall). Поэтому прежде чем заниматься его построением, необходимо систему установить хотя бы в минимальном объеме.

Я проделал это, установив систему на отдельный слайс с одним-единственным разделом в 512 Мбайт. Этого как раз хватает, если при заказной установке в пункте Distributions выбрасть Minimal, а затем через Custom добавить к этому минимуму man-страницы (дабы не остаться с ccd один на один). В дальнейшем предполагалось, что разделы /var, /usr, /home и так далее будут вынесены на конкатенированный массив, Кроме того, на каждом из дисков я создал по swap-разделу, равному объему памяти (что в сумме и дало рекомендуемый удвоенный ее объем).

По завершении установки и после рестарта системы требуется создать разделы для конкатенации. И тут обнаруживается, что построение RAID'а средствами ccd может быть выполнено двумя способами. Согласно первому, в соответствие с заветом великого Ленина, прежде чем объединиться, следовало решительно размежеваться, согласно второму - наоборот.

Суть второго способа - интуитивно понятна (и аналогична построению RAID'а в Linux с помощью mdadm): два дисковых раздела одинакового объема сливаются воедино, а потом это объединенное пространство делится на разделы под нужные файловые системы (типа /usr, /var, /home). Первая же схема выглядела в описании более сложной - сначала деление обоих дисков на симметричные "полуразделы" под будущие файловые системы, а потом попарное их слияние. Тем не менее, именно второй способ показался мне, уж не знаю, обоснованно или нет, идеологически более правильным. На нем я и остановился.

Итак, на каждом диске требуется создать по слайсу на всем оставшемся пространстве. При этом объем их должен быть примерно равным (в результате чего на втором диске остается 512 Мбайт, отведенных на первом диске под корневую файловую систему, но им применение найдется). А уж эти слайсы побить на попарные (и равновеликие) разделы, которые после конкатенации составят ветви /var, /usr, /home и все, которые еще планируется (например, /usr/ports и /usr/ports/distfiles - как раз хватает литер с d до h). А полугигабайтный остаток на втором диске можно сделать отднораздельным слайсом (и задействовать его, например, под /usr/src).

И дополнительное разбиение диска можно выполнить двумя способами - через sysinstall или вручную. С первым способом все понятно - отправляемся в меню Configure, выбираем в нем пункт Partition, выбираем для разметки оба диска и последовательно создаем на каждом по слайсу - создаем их тем же способом, что и при установке. И ни в коем случае не сохраняем изменения через пункт Write - это вызовет сообщение об ошибке на следующей стадии.

И еще важно: создание разделов через sysinstall в обязательном порядке предполагает их монтирование. Поэтому еще до запуска этой утилиты следует озаботиться созданием достаточного количества точек для этого - вроде, например, /tmp/var, /tmp/usr и так далее (в дальнейшем эти подкаталоги не потребутся, поэтому их целесообразно поместить именно здесь).

Далее, выходим из Fdisk'а через пункт Quit и, отказавшись от модификации загрузочного сектора, автоматически попадаем тем самым в меню Label. Где благополучно создаем все нужные нам партиции на каждом из слайсов. И вот здесь уже не забываем прибегнуть к пункту Write для сохранения всех сделанных изменений. Все, подготовка к слиянию в RAID-экстазе закончена.

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

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

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

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

$ fdisk -i /dev/ad0

и

$ fdisk -i /dev/ar0

Далее, вооружившись bsdlabel, запускаемой с опцией -e, калькулятором командной строки bc и, конечно же, man (8) bsdlabel, я на первом (/dev/ad0s1) диске, в дополнение к имевшимся

a: 524288 0 4.2BSD 2048 16384 32776
b: 2074624 524288 swap
c: 156301488 0 unused 0 0

дописал строки, определяющие полуразделы под будущие /var (256 Мбайт) и /usr (5 Гбайт), предназначив все оставшееся пространство (около 65 истинных Гбайт - не будем забывать, что объем моих дисков исчислялся в гигабайтах, принятых среди астрологов, хиромантов и производителей дисков, то есть реально составлял по 76 гигабайт, - под половинку от /home. В результате получилось:

d: 524288 2598912 4.2BSD 0 0 0
e: 10240000 3123200 4.2BSD 0 0 0
f: 142938288 13363200 4.2BSD 0 0 0

После этого я обратился ко второму диску (/dev/ar0s1), на котором создал парные к первому разделы под swap (1024 Мбайт), /var, /usr и /home (того же размера), что в итоге дало:

b: 2074624 0 swap
c: 156301312 0 unused 0 0
# "raw" part, don't edit
d: 524288 2074624 4.2BSD 0 0 0
e: 10240000 2598912 4.2BSD 0 0 0
f: 142938112 12838912 4.2BSD 0 0 0

Можно видеть, что, не смотря на все мои усилия по ручной коррекции геометрии, добиться полной идентичности мне не удалось, но на практике это ничему не помешало. Неиспользованный объем диска остался в самом его конце - как я уже сказал, при желании его можно задйствовать под какой-либо иной раздел.
Конкатенируем помаленьку

Все предыдущие действия можно было бы проделать и через sysinstall (с приведенными выше оговорками). Но теперь наступает время конкатенации разделов - и для этого в sysinstall средств как будто бы не предусмотрено. Но зато имеется специально для этого предназначенная утилита ccdconfig. Вооружаюсь соответствующей man-страницей - man (8) ccdconfig, - и выясняю, что и эту процедуру можно проделать двумя разными способами. Правда, оба требуют перехода в однопользовательский режим.

Первый способ - последовательный запуск ccdconfig для каждой пары сливаемых партиций примерно таким образом:

$ ccdconfig ccd0 128 none /dev/ad0s1d /dev/ar0s1d
$ ccdconfig ccd1 128 none /dev/ad0s1e /dev/ar0s1e
$ ccdconfig ccd1 128 none /dev/ad0s1f /dev/ar0s1f

Где ccd# - имя создаваемого RAID-устройства, 128 (для примера - это умолчальное значение) - размер (в Кбайт) блоков чередования записи данных на парные диски (понятно выразился? - как это сказать более по русски, не придумал), флаг none отменяет зеркалирование (то есть создает именно sripped-массив; насколько я понял чтобы сделать массив уровня 1, потребовалось бы указать флаг CCDF_MIRROR или его шестнадцатеричное значение - 004). Ну а аргументы понятны - это имена файлов партиций, которые подвергаются конкатенации.

В результате в каталоге /dev будут автоматически созданы новые устройства - /dev/ccd0, /dev/ccd1, /dev/ccd2 (напоминаю - речь идет о версии 5.X, использующей файловую систему устройств; в версиях 4-й ветки эти устройства потребовалось бы предварительно создать скриптом /dev/MAKEDEV или командой mknod), а в каталоге /etc возникнет конфигурационный файл ccd.config.

Второй способ - создать предварительно конфигурационный файл /etc/ccd.conf (которому на самом деле можно дать произвольное имя и поместить где угодно) в текстовом редакторе, и описать в нем объединяемые примерно таким образом:

# ccd ileave flags component devices
ccd0 128 none /dev/ad0s1d /dev/ar0s1d
ccd1 128 none /dev/ad0s1e /dev/ar0s1e
ccd2 128 none /dev/ad0s1f /dev/ar0s1f

После чего запустить ту же утилиту конфигурации следующим образом:

$ ccdconfig -C

в результате чего все необходимые сведения будут взяты из /etc/ccd.conf (если конфигу дали другое имя - следует указать его как аргумент с полным путем), устройства благополучно созданы.

Я проделал процедуру конкатенации обоими способами - одинаково просто и с идентичным (то есть неизменно превосходным) результатом. К слову - сбросить уже имеющиеся настройки ccd (если они почему-либо не устраивают) можно командой

$ ccdconfig -U

после чего они безболезненно переконфигурируются заново.
Завершающие штрихи

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

Итак, для начала размечаем созданные массивы:

$ bsdlabel -w /dev/ccd0 auto
$ bsdlabel -w /dev/ccd1 auto
$ bsdlabel -w /dev/ccd2 auto

Что даст нам на каждом из них по партиции c, к использованию непригодной. И потому повторяем процедуру в ручном режиме:

$ bsdlabel -e /dev/ccd#

что, как мы помним, вызовет текстовый редактор для прямой модификации дисковой разметки. Копируем строку, описывающую партицию c, изменяем маркирующую ее литеру (например, на d) и тип раздела (с unused на 4.2BSD). Необходимости указывать размеры блока, фрагмента и прочего - нет, эти значения будут определены при создании файловой системы. Что и проделываем:

$ newfs /dev/ccd0d
$ newfs /dev/ccd1d
$ newfs /dev/ccd2d

Теперь созданные файловые системы остается только смонтировать в предназначенные для них каталоги /var, /usr, /home. Однако если последний девственно чист (не зря же мы отказались от создания пользовательского аккаунта при постинсталляционной настройке), то в прочих - имеют место быть файлы, не очень многочисленные (установка ведь велась по принципу минимализма), но жизненно важные для работы системы. Так что предварительно их следует перенести на новые, конкатенированные, файловые системы.

Со старым каталогом /var это проходит без проблем: монтируем предназначенный для него ccd-раздел в какую-нибудь специально созданную точку (например, /tmp/var)

$ mount /dev/ccd0d /tmp/var

и просто перемещаем все содержимое:

$ mv /var/* /tmp/var

С каталогом /usr поступаем также:

$ mount /dev/ccd1d /tmp/usr
$ mv /usr* /tmp/usr

Если получается - все хорошо. Хотя тут возможны осложнения, если находящиеся в старом /usr файлы как-то используются в текущий момент системой. Конечно, при корректном переходе в однопользовательский режим такого случиться не должно, но чем черт не шутит. Так что не исключено, что для переноса файлов из /usr потребуется перезагрузка с rescue-CD.

Тем или иным образом перенеся содержимое старых каталогов в новые, конкатенированные, файловые системы, остается только прописать их в /etc/fstab примерно таким образом:

/dev/ccd0e /var ufs rw,noatime
/dev/ccd1e /usr ufs rw,noatime
/dev/ccd2e /home ufs rw,noatime

После чего - перезагрузка и радость от обретенных RAID'ов. А вот насколько она значительна с точки зрения быстродействия - мы и посмотрим в заключение.
Быстродействие программного RAID

Объектами тестирования были массив смешанных пользовательских данных, дерево портов FreBSD и avi-файл размером примерно с CD. Перед началом измерений файловая система /home была размонтирована и смонтирована как /mnt/hd.После этого последовательно выполнялись копирование массива данных и его удаление, развертывание, копирование и удаление дерева портов, копирование и удаление большого avi-файла. После каждого цикла файловая система размонтировалась, перемонтировалась заново и измерения повторялись - всего троекратно, в качестве результатов принимались средние значения.

Создавать на ccd специальный раздел специально для тестирования у меня возможности не было, поэтому все действия (копирование массива данных, развертывание и копирование дерева портов, копирование большого файла) выполнялись прямо в рабочем разделе. Что, конечно, оказывало некоторое (из общих соображение - негативное) влияние на результаты. Тем не менее, как можно видеть из таблиц 1 и 2, они оказались весьма показательными. Для большей наглядности я разделил данные для копирования/"растаривания" и для удаления. В качестве предмета сравнения выступили результаты для UFS2 и Ext2fs из одной моей прошлой заметки - обе в "умолчальных" режимах на 5-гигабайтном разделе одиночного диска.

Таблица 1. Копирование и "растаривание"
FS+Mode Data Untar Ports Big
UFS,default 211 189 255 104
UFS,ccd 185 107 175 83
Ext2,default 71 90 68 45

Сначала о копировании и "растаривании". Уже копирование массива данных дало более чем 10-процентный выигрыш в скорости против UFS2 на одиночном диске. При "растаривании" превосходство составило уже почти 50%, при копировании дерева портов - более 30%, и при копировании большого файла - более 20% (рис. 1).
Рис. 1. Копирование и "растаривание"

Результаты - не потрясающие воображения, но вполне весомые, а для "растаривания" уже сравнимые с ext2fs. Однако при удалении файлов все оказалось еще интересней (см. табл. 2).

Таблица 2. Удаление
FS+Mode Data Ports
UFS,default 7 92
UFS,ccd 5 34
UFS,ccd 5 34

Удаление массива данных с ccd-раздела происходило быстрее, чем с единичного UFS2, и даже чем с раздела ext2fs - хотя, при столь низких абсолютных значениях их трудно считать статистически значимыми. А вот на удалении дерева портов выигрыш во времени составил более 60% рис. 2). Удаление же единичного файла происходило практически мгновенно и потому время его здесь не приводится.
Рис. 2. Удаление

Таким образом, на типично пользовательских задачах использование программного RAID со стриппингом (по крайней мере, в исполнении ccd) дает вполне реальный (а иногда и просто значительный) прирост в быстродействии файловых операций. А поскольку нынче редкая мама не оснащается дополнительным IDE-контроллером, появляется возможность разнести винчестеры на отдельные линии (даже без задействования аппаратного ATA RAID), что должно еще более улучшить результаты.