Миграция FreeBSD с одного диска на другой


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

Что ж, задача была сформулирована весьма четко: переместить рабочую систему FreeBSD-5.2.1 с одного ATA диска (20Gb) на другой ATA диск объемом 80Gb; полученный выигрыш в размере 60Gb распределить между файловыми системами согласно некоторым прогнозам роста объема данных.
Старый будет использоваться владельцами для других целей.

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

Кое-что до моего прихода уже пробовали делать, например, система была откопирована на новый диск сначала при помощи dd, а затем повторно небезызвестной и многими любимой Partition Magic (tm). Правда, в результате этих действий, желаемого увеличения размеров файловых систем, не произошло, что вполне предсказуемо, если учитывать, что ни Partition Magic (tm) ни тем более, dd, не различают файловые системы
внутри слайса (партиции) FreeBSD.

Разумеется, можно было бы оставить все как есть, и на оставшемся свободном пространстве "нового" диска создать второй FreeBSD слайс размером 60Gb, в нем сделать необходимое количество партиций и файловых систем, которые затем смонтировать в соответствующие
поддиректории корневой файловой системы, располагающейся на первом слайсе в 20Gb. И затем уже каким-либо способом: при помощи симлинков, архиваторов, обыкновенного копирования или команд dump/restore перенести все или часть файлов из старой файловой системы в новую.

Что ж, можно и так, но тогда было бы логичнее вообще не убирать из системы "старый" диск, а добавить к нему место с нового диска, получив
суммарный объем 100 Gb. Но раз "старому" диску владельцами была уготована другая судьба, есть повод постараться сделать все правильно.

Короче говоря, через некоторое время в системный блок на оба IDE контроллера в режиме master были водружены оба винчестера. И когда
система загрузилась со "старого" диска, то они вполне прогнозируемо обнаружились под именами ad0 (старый) и ad2 (новый). Практически
сразу, не дав мне полностью насладиться результатами команды dmesg, на консоли замелькали знакомые предостережения:

/var: write failed, filesystem is full

Залогинившись на второй виртуальный терминал, я убедился, при помощи команды df, что и /usr уже заполнена на 99%. Стало ясно, что пора
приниматься за работу.

План действий, который я себе наметил, оригинальностью не отличался.
Думаю, если порыться в Интернет, наверняка можно найти множество описаний этого процесса с разной степенью детализации и некоторыми
вариациями алгоритма. По крайней мере, у меня самого, при написании этих строк несколько раз возникало ощущение deja vu и потому, я не претендую на какие-либо права на нижеизложенный метод миграции FreeBSD на новый диск:

1. Определяем файловые системы, требующие миграции и рассчитываем их размер на "новом" диске;

2. Подключаем "новый" диск и загружаем систему со "старого" диска. В нашем случае, этот пункт уже выполнен, "новый" диск успешно определился как ad2 (конечно, его смело можно было подключать и на первый IDE контроллер в качестве slave, просто тогда бы диск назывался ad1). В принципе, никто не мешает проводить миграцию и на SATA или SCSI, но в этих случаях следует убедиться, что ядро скомпилировано с поддержкой всех необходимых драйверов.

3. Делаем "новый" диск загрузочным. На всем доступном пространстве диска создаем один слайс FreeBSD и в нем партиции (включая swap),
количество и размер которых, должны были быть рассчитаны в пункте 1.

4. На время миграции системы подготавливаем точки монтирования файловых систем "нового" диска на корневой системе "старого" диска. Эти точки монтирования договоримся называть: /new.<FS> Где FS это название мигрируемой файловой системы;

5. Загружаемся в однопользовательский режим (опционально), монтируем все /new.<FS> и переносим систему при помощи dump/restore

6. Выключаем систему, убираем "старый" диск и на его место ставим "новый". Загружаем систему и убеждаемся, что все работает.

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

Однако, чтобы не быть голословным и предоставить доказательства жизнеспособности этого способа миграции FreeBSD с диска на диск, я
смоделировал процесс в виртуальной машине VmWare (tm). Правда, для опытов я использовал диски гораздо меньшего объема: "старый" всего 4Gb, "новый" - 8Gb. Это облегчало мне жизнь, поскольку можно было не мудрить с расчетами, а смело увеличивать все мигрируемые файловые
системы ровно в два раза, ну хорошо примерно в два раза. Зато и эксперимент я уже мог смело проводить на свежей FreeBSD-6.0, а не FreeBSD-5.2.1, как в полевых условиях. Впрочем, роль версий, к тому же столь незначительная, в данных опытах особой роли не играет.

В нашем очень простом случае, мы будем мигрировать три файловые системы /, /usr и /var. Раздел /tmp никакой полезной информации не
содержал, /dev создается динамически, а больше файловых систем на экспериментальном диске не было:

testpet# df -k
Filesystem 1K-blocks Used Avail Capacity Mounted on
/dev/ad0s1a 507630 56188 410832 12% /
devfs 1 1 0 100% /dev
/dev/ad0s1e 507630 12 467008 0% /tmp
/dev/ad0s1f 1493014 1136910 236664 83% /usr
/dev/ad0s1d 1190350 1896 1093226 0% /var
testpet#
testpet# swapinfo
Device 1K-blocks Used Avail Capacity
/dev/ad0s1b 367432 0 367432 0%
testpet#
testpet# bsdlabel ad0s1
# /dev/ad0s1:
8 partitions:
# size offset fstype [fsize bsize bps/cpg]
a: 1048576 0 4.2BSD 2048 16384 8
b: 734864 1048576 swap
c: 8388513 0 unused 0 0 # "raw" part, don't edit
d: 2463744 1783440 4.2BSD 2048 16384 28552
e: 1048576 4247184 4.2BSD 2048 16384 8
f: 3092753 5295760 4.2BSD 2048 16384 28552
testpet#

Как видно из этого листинга, объемы свободного пространства на файловых системах (даже /usr) можно назвать достаточными. Впрочем, это
же тестовая машина testpet, которую я сконструировал внутри VmWare (tm) для единственной цели: перенести FreeBSD-6.0 с одного диска на другой и то, что фактической необходимости осуществлять миграцию не существует, меня не остановит.

Итак, будем считать, что первые два пункта плана уже выполнены и продолжим сразу с третьего. Следовательно, займемся подготовкой нового
диска. Cделаем этот диск загрузочным и создадим на нем одну дисковую партицию, занимающую весь диск и являющуюся слайсом FreeBSD. Все эти действия можно с легкостью выполнить, используя sysinstall, который, как выясняется попутно, во FreeBSD-6.0 благополучно переехал /usr/sbin. Впрочем, ребята мы самостоятельные и можем проделать все операции собственными руками без посторонеей помощи. Итак:

testpet# dd if=/dev/zero of=/dev/ad2 bs=512 count=32
testpet# fdisk -BI ad2
******* Working on device /dev/ad2 *******
fdisk: invalid fdisk partition table found
fdisk: Geom not found
testpet#

После этих действий в каталоге /dev должен появиться файл устройства нового слайса:

testpet# ls -la /dev/ad2*
crw-r----- 1 root operator 0, 79 Jan 8 18:59 /dev/ad2
crw-r----- 1 root operator 0, 95 Jan 8 18:26 /dev/ad2s1

Здесь /dev/ad2 - диск, /dev/ad2s1 - первый слайс на этом диске.

Теперь можно поставить BSD-метку на слайсе:

testpet# dd if=/dev/zero of=/dev/ad2s1 bs=512 count=32
testpet# bsdlabel -w -B ad2s1

Кстати сказать, здесь обе операции dd над /dev/ad2 и /dev/ad2s1 не обязательны, но man bsdlabel(8) рекомендует их выполнение, ссылаясь на
то, что некоторые BIOS'ы не смогут без них правильно распознать диск.
Лично я предпочитаю верить.

Идем дальше и проверим, что же именно bsdlabel записал на слайс.
Посмотрим на таблицу партиций:

testpet# bsdlabel ad2s1
# /dev/ad2s1:
8 partitions:
# size offset fstype [fsize bsize bps/cpg]
a: 16776506 16 unused 0 0
c: 16776522 0 unused 0 0 # "raw" part, don't edit
testpet#

А каталог /dev теперь содержит:

testpet# ls -la /dev/ad2*
crw-r----- 1 root operator 0, 79 Jan 9 11:15 /dev/ad2
crw-r----- 1 root operator 0, 82 Jan 9 11:31 /dev/ad2s1
crw-r----- 1 root operator 0, 100 Jan 9 11:15 /dev/ad2s1a
crw-r----- 1 root operator 0, 101 Jan 9 11:15 /dev/ad2s1c
testpet#

Другими словами, на диске ad2 внутри слайса ad2s1 появились две партиции ad2s1a и ad2s1c. Партиция ad2s1c называется "raw-partition" объединяющей все партиции внутри слайса, и по-существу является ссылкой на весь слайс. Соответственно, создавать файловую систему на ней мы не будем, а из ее характеристик нас, прежде всего, интересует размер. И в нашем случае, размер этой партиции, а значит и всего слайса составляет 16776522 секторов, т.е. 16776522 / 2 = 8388261 Kb или ~8191,7 Mb. Кроме того, у нас появилась партиция ad2s1a которая начинается со смещения 16 секторов и занимает 16776506 секторов.

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

Сразу отредактировать таблицу партиций в редакторе:

testpet# bsdlabel -e ad2s1

Или экспортировать ее в файл, отредактировать, а затем импортировать на диск:

testpet# bsdlabel ad2s1 > /tmp/disklabel
#
# редактируем /tmp/disklabel
#
testpet# bsdlabel -R ad2s1c /tmp/disklabel

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

# /dev/ad2s1:
8 partitions:
# size offset fstype [fsize bsize bps/cpg]
a: 2097152 0 4.2BSD
b: 1502496 * swap
c: 16776522 0 unused
d: 4943872 * 4.2BSD
e: 2097152 * 4.2BSD
f: * * 4.2BSD

После того, как она была обработана bsdlabel и записана на диск, вместо звездочек подставились автоматически рассчитанные значения:

testpet# bsdlabel ad2s1
# /dev/ad2s1:
8 partitions:
# size offset fstype [fsize bsize bps/cpg]
a: 2097152 0 4.2BSD 0 0 0
b: 1502496 2097152 swap
c: 16776522 0 unused 0 0 # "raw" part, don't edit
d: 4943872 3599648 4.2BSD 0 0 0
e: 2097152 8543520 4.2BSD 0 0 0
f: 6135850 10640672 4.2BSD 0 0 0

Что ж, партиции созданы, можно создавать на них файловые системы.
Запустим newfs с опциями по-умолчанию: -b 16384 -f 2048 или же вовсе без них:

testpet# newfs -b 16384 -f 2048 /dev/ad2s1a
/dev/ad2s1a: 1024.0MB (2097152 sectors) block size 16384, fragment size 2048
using 6 cylinder groups of 183.77MB, 11761 blks, 23552 inodes.
super-block backups (for fsck -b #) at:
160, 376512, 752864, 1129216, 1505568, 1881920

testpet# newfs -b 16384 -f 2048 /dev/ad2s1d
...
testpet# newfs /dev/ad2s1e
...
testpet# newfs /dev/ad2s1f
...

Файловые системы на ad2s1b и ad2s1c мы не создавали, поскольку первая
- swap, а вторая raw.

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

testpet# bsdlabel ad2s1
# /dev/ad2s1:
8 partitions:
# size offset fstype [fsize bsize bps/cpg]
a: 2097152 0 4.2BSD 2048 16384 28552
b: 1502496 2097152 swap
c: 16776522 0 unused 0 0 # "raw" part, don't edit
d: 4943872 3599648 4.2BSD 2048 16384 28552
e: 2097152 8543520 4.2BSD 2048 16384 28552
f: 6135850 10640672 4.2BSD 2048 16384 28552

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

testpet# mkdir /new.root /new.var /new.usr

Ура, с четвертым пунктом покончено и можно двигаться дальше, приступая к самой миграции. Для этого нужно перезагрузиться в однопользовательский режим. Зачем это надо? Дело в том, что мы собираемся получить слепки каждой файловой системы старого диска,
которые затем наложить на соответствующие файловые системы нового диска. И чтобы не нарушить консистентность файловых систем, их лучше всего размонтировать, или по крайней мере смонтировать как read-only.
Чтобы при этом всевозможные демоны не пытались что-либо писать на диск, их лучше всего остановить или выключить совсем. А проще всего
этого добиться переходом в однопользовательский режим.

Конечно, есть и второй вариант. Делать слепок мы собираемся при помощи утилиты dump, которая ради того, чтобы корректно откопировать смонтированную файловую систему, готова сделать ее snapshot. Таким образом, все операции могут проводиться уже над этим снимком, а не
файловой системой как таковой. Этот способ очень хорош, когда нет возможности перехода в однопользовательский режим.

Ради интереса все файловые системы мы скопируем и тем и другим способом. Начнем со способа номер два, со snapshot'ами. Итак
перезагрузка в однопользовательский режим нас больше не волнует, поэтому вперед! Включаем soft-updates и монтируем новые файловые
системы:

testpet# tunefs -n enable /dev/ad2s1d
testpet# tunefs -n enable /dev/ad2s1f
testpet# tunefs -n enable /dev/ad2s1e # для /tmp
testpet# mount /dev/ad2s1a /new.root
testpet# mount /dev/ad2s1d /new.var
testpet# mount /dev/ad2s1f /new.usr

И начинаем переносить файловые системы:

testpet# cd /new.root && dump -L -0u -f - / | restore -r -f -
DUMP: Date of this level 0 dump: Mon Jan 9 14:44:52 2006
DUMP: Date of last level 0 dump: the epoch
DUMP: Dumping snapshot of /dev/ad0s1a (/) to standard output
DUMP: mapping (Pass I) [regular files]
DUMP: mapping (Pass II) [directories]
DUMP: estimated 56343 tape blocks.
DUMP: dumping (Pass III) [directories]
DUMP: dumping (Pass IV) [regular files]
warning: ./.snap: File exists
expected next file 16454, got 152
DUMP: DUMP: 56939 tape blocks
DUMP: finished in 20 seconds, throughput 2846 KBytes/sec
DUMP: level 0 dump on Mon Jan 9 14:44:52 2006
DUMP: DUMP IS DONE
testpet#

20 секунд впечатлило. Продолжаем тоже самое и для остальных файловых систем:

testpet# cd /new.var && dump -L -0u -f - /var | restore -r -f -
testpet# cd /new.usr && dump -L -0u -f - /usr | restore -r -f -

Теперь можно выключить компьютер, отсоединить "старый" диск и поставить вместо него "новый".

Способ с загрузкой в однопользовательский режим отличается от вышеизложенного только тем, что сразу после включения soft-updates, т.е. после запуска серии команд tunefs, переходим в однопользовательский режим (нет нужды говорить, что при этом мы должны сидеть за консолью сервера, иначе мы потеряем управление системой):

testpet# init 1

Далее монтируем новые файловые системы:

testpet# mount /dev/ad2s1a /new.root
testpet# mount /dev/ad2s1d /new.var
testpet# mount /dev/ad2s1f /new.usr

А старые переводим в состояние read-only:

testpet# mount -o ro -u /
testpet# mount -o ro -u /var
testpet# mount -o ro -u /usr
testpet# mount /tmp

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

Итак, выполняем dump/restore для каждой файловой системы. В этом варианте запускаем команды dump без ключа -L, поскольку все файловые
системы на старом диске находятся в режиме read-only. Попутно, перемонтируем скопированные файловые системы в режиме read-write:

testpet# cd /new.root && dump -0u -f - / | restore -r -f -
testpet# mount -u /
testpet# cd /new.var && dump -0u -f - /var | restore -r -f -
testpet# mount -u /var
testpet# cd /new.usr && dump -0u -f - /usr | restore -r -f -
testpet# mount -u /usr


Что ж, даем очередной раз команду shutdown -p now и меняем диски местами. Уфф, на этом все, можно включить систему и убедиться, что все в порядке. Да, и маленькое предостережение на прощание:

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

Обновлено: 12.03.2015