Предисловие

Вот и пришла пора увеличить место на своём домашнем сервере, для этого было куплено 2 диска разных производителей, но одинакового объёма.
Захотелось сделать soft RAID1, но мне не очень подходит mdadm, так как я хочу иметь более гибкое зеркало, которое можно будет легко расширить в будущем, а не выделять сразу большой объём.
Решил попытать счастья с LVM RAID, но информации по нему, в сжатом виде, мало, в основном всё заканчивает на том, как его создать. Попробую исправить этот пробел.

Мои диски

Мои диски

LVM

Нам понадобится 2 диска, если вы собираетесь настраивать RAID, если нет, то просто пропускайте части с настройкой второго диска.

Для тестирования необязательно иметь настоящие диски, можно использовать обычные файлы: How to create virtual block device (loop device/filesystem) in Linux

Заметка

Имена дисков заменены на sdX, sdY, sdZ, во избежание потери данных на реальных носителях.

Инициализация

Казалось бы, сделал pvcreate над диском и готово, но не тут то было…
Согласно рекомендациям1 2, стоит всегда создавать разделы на диске, во избежание проблем с софтом, который не знает об LVM и будет считать диск пустым.

Для разбивки диска я буду использовать gdisk, обычно уже установлен, но если что, устанавливается командой:

1
apt install gdisk
Предупреждение

Убедитесь, что вы используете верный диск в командах, так как это уничтожит все данные на диске!

1
gdisk /dev/sdX
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# Смотрим разделы и проверяем, тот ли мы диск выбрали.
p

# Создаём пустую GUID partition table.
o # жмём `y`, чтобы удалить все разделы и создать GPT запись.

# Создаём первый и единственный раздел на весь диск.
n
1
<enter>
<enter>
8e00 # Указываем, что данный раздел относится к LVM.

# Теперь можно просмотреть то, что получилось.
p

# Если всё устраивает, то осталось записать изменения на диск.
w # жмём `y`, чтобы подтвердить запись.

проделываем то же самое со вторым диском.

Добавляем диски в LVM

1
2
3
pvcreate /dev/sdX1 /dev/sdY1
# или коротко
pvcreate /dev/sd[XY]1

Создаём группу vgdata из двух дисков

1
2
3
vgcreate vgdata /dev/sdX1 /dev/sdY1
# или коротко
vgcreate vgdata /dev/sd[XY]1

Простой логический диск

Создаём простой логический диск с именем lv0 на 3ГБ:

1
2
3
lvcreate --size 3G --name lv0 vgdata
# или коротко
lvcreate -L 3G -n lv0 vgdata

RAID 1 (mirror)

Создаём логический диск с RAID 1 под именем lvmirror на 3ГБ:

1
2
3
lvcreate --size 3G --name lvmirror --mirrors 1 vgdata
# или коротко
lvcreate -L 3G -n lvmirror -m1 vgdata

можно указать флаг --nosync, чтобы пропустить синхронизацию пустого зеркала.

Смотрим что получилось

1
lvs -a -o +devices

Колонка Cpy%Sync показывает, процент синхронизации, а Devices, устройства, на которых находятся данные логического диска.

FS

Осталось создать файловую систему:

1
mkfs.ext4 /dev/vgdata/lvmirror

И можно примонтировать и пользоваться

1
mount /dev/vgdata/lvmirror /mnt/

Тестируем LVM RAID 1 и учимся чинить

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

Для начала, посмотрим, как у нас всё выглядит до начала тестов:

1
lvs -a -o +devices,lv_health_status
1
2
3
4
5
6
LV                  VG     Attr       LSize Cpy%Sync Devices                                   Health
lvmirror            vgdata rwi-aor--- 3.00g 100.00   lvmirror_rimage_0(0),lvmirror_rimage_1(0)
[lvmirror_rimage_0] vgdata iwi-aor--- 3.00g          /dev/sdX1(1)
[lvmirror_rimage_1] vgdata iwi-aor--- 3.00g          /dev/sdY1(1)
[lvmirror_rmeta_0]  vgdata ewi-aor--- 4.00m          /dev/sdX1(0)
[lvmirror_rmeta_1]  vgdata ewi-aor--- 4.00m          /dev/sdY1(0)

Теперь мы можем приступать к тестированию, но сначала:

Предупреждение

Убедитесь, что вы используете верный диск в командах, так как это может привести к потере данные на диске!

Отвал диска (ресинхронизация) и его замена

В данном случае, мы будем симулировать отвал диска, через изменение его состояния 3

Отключаем диск sdX:

1
echo offline > /sys/block/sdX/device/state

Теперь, чтобы LVM понял, что с диском что-то не то, нужно записать на диск данные.

Если FS есть:

1
echo test > /mnt/test.txt 

Если FS нет(эта команда уничтожит FS, если она есть!):

1
dd if=/dev/zero of=/dev/vgdata/lvmirror count=10

Смотрим результат:

1
lvs -a -o +devices,lv_health_status
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
/dev/sdX: open failed: No such device or address
/dev/sdX: open failed: No such device or address
/dev/sdX: open failed: No such device or address
WARNING: Couldn't find device with uuid VkIceg-2ZVd-tTuZ-HgHz-EblT-5GwQ-oH3gKM.
WARNING: VG vgdata is missing PV VkIceg-2ZVd-tTuZ-HgHz-EblT-5GwQ-oH3gKM (last written to /dev/sdX1).
WARNING: Couldn't find all devices for LV vgdata/lvmirror_rmeta_0 while checking used and assumed devices.
WARNING: Couldn't find all devices for LV vgdata/lvmirror_rimage_0 while checking used and assumed devices.
LV                  VG     Attr       LSize Cpy%Sync Devices                                   Health
lvmirror            vgdata rwi-aor-p- 3.00g 100.00   lvmirror_rimage_0(0),lvmirror_rimage_1(0) partial
[lvmirror_rimage_0] vgdata iwi-aor-p- 3.00g          [unknown](1)                              partial
[lvmirror_rimage_1] vgdata iwi-aor--- 3.00g          /dev/sdY1(1)
[lvmirror_rmeta_0]  vgdata ewi-aor-p- 4.00m          [unknown](0)                              partial
[lvmirror_rmeta_1]  vgdata ewi-aor--- 4.00m          /dev/sdY1(0)

Обратите внимание, статус Health стал partial

Теперь запишем рандомные данные на диск, чтобы можно было увидеть рассинхронизацию:

FS есть, пишем в файл /mnt/random.file:

1
dd if=/dev/urandom of=/mnt/random.file bs=10M count=10 

FS нет(эта команда уничтожит FS, если она есть!):

1
dd if=/dev/urandom of=/dev/vgdata/lvmirror bs=10M count=10

Включаем диск sdX обратно:

1
echo running > /sys/block/sdX/device/state

Смотрим результат:

1
lvs -a -o +devices,lv_health_status
1
2
3
4
5
6
LV                  VG     Attr       LSize Cpy%Sync Devices                                   Health
lvmirror            vgdata rwi-aor-r- 3.00g 100.00   lvmirror_rimage_0(0),lvmirror_rimage_1(0) refresh needed
[lvmirror_rimage_0] vgdata Iwi-aor-r- 3.00g          /dev/sdX1(1)                              refresh needed
[lvmirror_rimage_1] vgdata iwi-aor--- 3.00g          /dev/sdY1(1)
[lvmirror_rmeta_0]  vgdata ewi-aor-r- 4.00m          /dev/sdX1(0)                              refresh needed
[lvmirror_rmeta_1]  vgdata ewi-aor--- 4.00m          /dev/sdY1(0)

Ошибки и предупреждения об отсутствии диска пропали, но статус Health изменился на refresh needed

Чиним

Запускаем синхронизацию командой:

1
lvchange --refresh /dev/vgdata/lvmirror

Но если диск выпал, я бы рекомендовал его заменить на новый, проинициализировав его как в начале статьи и добавив в PV и VG:

1
2
pvcreate /dev/sdZ1
vgextend vgdata /dev/sdZ1

И заменив диск у зеркала:

1
lvconvert --replace /dev/sdX1 /dev/vgdata/lvmirror /dev/sdZ1

Замена мёртвого диска

Теперь давайте просимулируем замену мёртвого диска, для этого нам придётся стереть заголовок нашего диска, чтобы он выглядел как новый:
Эта команда уничтожит данные на диске!

1
dd if=/dev/zero of=/dev/sdX bs=10M count=1

При вызове

1
lvs -a -o +devices,lv_health_status

Мы увидим тот же результат, что и при отвале диска, статус Health стал partial, но чинится это по другому.

Чиним

Удалим старый диск из VG

1
vgreduce --removemissing --force vgdata

Так как мы стёрли заголовки диска, нам придётся проинициализировать диск заново и добавить его в PV и VG:

1
2
pvcreate /dev/sdX1
vgextend vgdata /dev/sdX1

И нужно починить наше зеркало

1
lvconvert --repair /dev/vgdata/lvmirror

Автоматически запуститься ресинхронизация.

Конвертируем логический диск из RAID 1 в обычный

1
lvconvert -m0 /dev/vgdata/lvmirror

Конвертируем логический диск из обычного в RAID 1

1
lvconvert -m1 /dev/vgdata/lvmirror

Увеличиваем размер диска с зеркалом

1
2
3
4
5
umount /dev/vgdata/lvmirror
# Добавляем 5GB к диску
lvresize --resizefs --size +5G /dev/vgdata/lvmirror
# Смотрим результат
lvs

Уменьшаем размер диска с зеркалом

1
2
3
4
5
umount /dev/vgdata/lvmirror
# Делаем диск равным 4GB
lvresize --resizefs --size 4G /dev/vgdata/lvmirror
# Смотрим результат
lvs

Мониторинг

Для мониторинга состояния LVM я написал exporter для prometheus
lvm_exporter

Как я всё сломал

Пока писал статью, успел сломать тестовый LVM

Inconsistent metadata

Когда отключил диск, я решил удалить лишнее зеркало (у меня было 2 LV зеркальных), после подключения диска обратно, у меня появилась ошибка при любой команде LVM:

1
2
3
WARNING: ignoring metadata seqno 20 on /dev/sdX1 for seqno 21 on /dev/sdY1 for VG vgdata.
WARNING: Inconsistent metadata found for VG vgdata
WARNING: outdated PV /dev/sdX1 seqno 20 has been removed in current VG vgdata seqno 21.

После долгих поисков, нашёл нужную мне команду в багтрекере 4, которая мне помогла:

1
vgck --updatemetadata vgdata

  1. The Linux Documentation Project (LDP) - LVM HOWTO - Initializing disks or disk partitions ↩︎

  2. Red Hat - LVM 3.1.2. Multiple Partitions on a Disk ↩︎

  3. Simulating LVM Mirror Failure and recovery ↩︎

  4. Bug 1747585 - vgextend segfaults if attempting to extend using device that was previous removed and volumes were deleted ↩︎