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

В статье рассмотрим способ переноса системного раздела ОС Linux на другое блочное устройство и необходимые изменения в UEFI загрузчике.

Предыстория

Ситуация казалась типичной. Пришел новый сервер, внутри два диска. Я сделал из них аппаратный RAID1 и отправил в Kickstart инсталляцию, чтобы накатить заказанный Enterprise Linux 8.

Коллеги из DBA Bercut через Ansible установили СУБД и смигрировали туда сервис, запустили в прод. А через несколько дней я случайно заметил, что на файловых системах сервера свободно 1,5ТБ пространства, хотя я помнил, что там было два SAS диска по 600ГБ. Нестыковочка.

Оказывается, в купленном заказчиком сервере была установлена еще пара контроллеров NVMe, и Anaconda во время Kickstart инсталляции решила занять именно один из них.

На эти NVMe контроллеры были другие планы — их собирались использовать более оптимальным образом.

Сервер уже в проде — ну что же, используем эту ошибку, как возможность поделиться опытом и написать интересную статью для Хабра.

Погнали чинить

Конечно, придется делать работы ночью: т.к. будет пара перезагрузок, мы совместим их с другими плановыми работами.

Требуется небольшая подготовка, нужен свежий SystemResqueCD. Возьмем его отсюда.

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

Осмотримся на сервере

lsblk покажет нам все доступные блочные устройства.

# lsblk
NAME              MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINT
sda                 8:0    0 558.4G  0 disk  
nvme1n1           259:0    0   1.8T  0 disk  
nvme0n1           259:1    0   1.8T  0 disk  
├─nvme0n1p1       259:2    0   256M  0 part  /boot/efi
├─nvme0n1p2       259:3    0     1G  0 part  /boot
└─nvme0n1p3       259:4    0   1.8T  0 part  
  ├─bercutvg-root 252:0    0  24.4G  0 lvm   /
  ├─bercutvg-swap 252:1    0  16.6G  0 lvm   [SWAP]
  ├─bercutvg-u01  252:2    0   1.7T  0 lvm   /u01
  ├─bercutvg-home 252:3    0  19.5G  0 lvm   /home
  ├─bercutvg-opt  252:4    0   9.8G  0 lvm   /opt
  └─bercutvg-var  252:5    0   9.8G  0 lvm   /var
 ...

Под устройством /dev/sda скрывается аппаратное зеркало из двух SAS дисков по 600ГБ.

# smartctl -i /dev/sda -d megaraid,0
=== START OF INFORMATION SECTION ===
Vendor:               SEAGATE
Product:              BL600MM0069
User Capacity:        600,127,266,816 bytes [600 GB]
Rotation Rate:        10000 rpm
Transport protocol:   SAS (SPL-3)
...
# smartctl -i /dev/sda -d megaraid,1
=== START OF INFORMATION SECTION ===
Vendor:               SEAGATE
Product:              BL600MM0069
User Capacity:        600,127,266,816 bytes [600 GB]
Rotation Rate:        10000 rpm
Transport protocol:   SAS (SPL-3)
...

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

Мы хотим перенести системный диск с nvme0n1 на sda.

Команда vgs показывает нам, что у нас есть LVM группа bercutvg, старую группу мы переименуем, а новую создадим.

Загрузка с SystemResqueCD

Планируем ночные работы. Понадобится доступ к системному контроллеру сервера, чтобы попасть в его консоль и запустить с ISO-образа SystemResqueCD. Во время старта сервера входим в меню загрузки и выбираем CD-ROM.

Загружаем сервер с SystemResqueCD с опциями по умолчанию.

Создаем временное рабочее окружение

Если вы любите работать с GUI-окружением, после загрузки можно запустить графическую оболочку командой startx, либо остаться работать в консольном интерфейсе.

Если вы планируете сложить дамп файловых систем в какую-то сетевую папку, запустите nmtui и настройте сетевые интерфейсы: IP, MASK, GW. В нашем случае это не обязательно, т.к. временный дамп будет располагаться на внутреннем устройстве nvme1n1.

По умолчанию на системе SystemResqueCD уже есть работающий sshd. Если вам удобней работать через ssh-клиент, надо прорезать порт 22/tcp в firewall (либо отключить сервис iptables), а также задать временный пароль для root.

Временный пароль root задаем в

passwd root

Чтобы настроить ssh в iptables, нужно добавить в середину файла /etc/iptables/iptables.rules правило:

-A INPUT -p tcp -s 192.168.1.100 --dport 22 -j ACCEPT

А затем перезапустить сервис:

systemctl restart iptables

В конфигурационной строке выше 192.168.1.100 — это адрес, с которого мы подключаемся во время работ.

Я использую ssh соединение. Это удобно — можно копировать команды в блокнот и обратно.

Создание дампа файловых систем

Дамп файловых систем мы будем делать с помощью утилиты fsarchiver.

Запустим эту утилиту в режиме осмотра:

fsarchiver probe detailed

В ответ мы увидим подробную карту блочных устройств, названия LVM томов и UUID файловых систем.

Нам необходимо перенести файловые системы:

/dev/nvme0n1p1

/boot

/dev/nvme0n1p2

/boot/efi

/dev/mapper/bercutvg-root

/

/dev/mapper/bercutvg-var

/var

/dev/mapper/bercutvg-home

/home

/dev/mapper/bercutvg-opt

/opt

/dev/mapper/bercutvg-u01

/u01

А также нужно не забыть пересоздать swap.

/dev/mapper/bercutvg-swap

swap

Через команду pvs или lsblk определяем, что текущая VG bercutvg размещена на /dev/nvme0n1p3, а устройство nvme1n1 свободно.

Создаем временную файловую систему для размещения дампа файловых систем на nvme1n1. Тут мы не будем тратить время на создание партиции, главное не затереть случайно nvme0n1.

mkfs.ext4 /dev/nvme1n1
mkdir /mnt/dump
mount /dev/nvme1n1 /mnt/dump

Для подключения сетевых папок (если они вам нужны), альтернативно можно воспользоваться, например, такими командами:

#CIFS шара (ее нужно заранее создать)
mount -t cifs //192.168.1.100/dump /mnt/dump -o username=user,password=pas
#через SSH (небыстрый способ)
sshfs login@192.168.1.100:/path/to/dir /mnt/dump									

Переходим в подключенную папку и создаем в ней дамп файловых систем.

cd /mnt/dump
#узнаем сколько потоков процессора есть в системе, 
#чтобы указать это число в -j (но не больше чем 32)
grep -c processor /proc/cpuinfo	
fsarchiver -j32 savefs /mnt/dump/image.fsa \
                /dev/nvme0n1p1 \
                /dev/nvme0n1p2 \
                /dev/mapper/bercutvg-root \
                /dev/mapper/bercutvg-var \
                /dev/mapper/bercutvg-home \
                /dev/mapper/bercutvg-opt \
                /dev/mapper/bercutvg-u01

Можно добавить опцию -v для режима "болтливости", однако я её не рекомендую - вы можете пропустить важное сообщение об ошибке, если вывод будет очень большим.

Команда создает дамп некоторое время, за растущим размером /mnt/dump/image.fsa можно следить в соседней консоли.

Заглянуть в созданный архив можно с помощью опции archinfo:

fsarchiver archinfo /mnt/dump/image.fsa

Это полезно, если вы забыли в каком порядке перечисляли файловые системы для дампа.

Подготовка устройств для распаковки данных

Мы хотим разместить наши файловые системы на устройстве sda. Сделаем его разметку.

Посмотрим разметку текущего системного диска /dev/nvme0n1;

gdisk -l /dev/nvme0n1

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

gdisk /dev/sda

#создание первого раздела, начинающегося на блоке 2048, размер 256МБ,
#тип ФС EF00 (EFI загрузка)
n				
1
2048
+256M
EF00

#создание второго раздела, начинающегося на первом свободном блоке, размер 1024МБ,
#тип ФС 8300 (EXT)
n   
2

+1024M
8300

#создание третьего раздела, начинающегося на первом свободном блоке, 
#размером на все оставшиеся блоки, тип ФС 8E00 (LVM)
n
3


8E00

#записываем изменения на диск и подтверждаем
w
y

Прочитаем новую разметку с только что размеченного диска:

partprobe /dev/sda
gdisk -l /dev/sda

Теперь необходимо создать LVM-группу bercutvg и тома на ней. Но имя bercutvg у нас уже занято. Придется старую группу переименовать.

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

vgrename bercutvg oldbercutvg
pvcreate /dev/sda3 
vgcreate bercutvg /dev/sda3

Создадим с нужными размерами тома:

/dev/mapper/bercutvg-root

/

25G

/dev/mapper/bercutvg-var

/var

10G

/dev/mapper/bercutvg-home

/home

20G

/dev/mapper/bercutvg-opt

/opt

10G

/dev/mapper/bercutvg-swap

swap

17G

/dev/mapper/bercutvg-u01

/u01

все остальные блоки - 100%FREE

Выполним следующие команды:

lvcreate -n root -L 25G bercutvg
lvcreate -n var  -L 10G bercutvg
lvcreate -n home -L 20G bercutvg
lvcreate -n opt  -L 10G bercutvg
lvcreate -n swap -L 17G bercutvg
lvcreate -n u01  -l 100%FREE bercutvg

Наполняем устройства данными

Самое простое — создать новый swap.

mkswap /dev/mapper/bercutvg-swap

Далее нам понадобится созданный ранее дамп. Необходимо вспомнить в каком порядке запаковывались файловые системы (или подсмотреть этот порядок через fsarchiver archinfo /mnt/dump/image.fsa).

Для EFI system partition (/boot/efi) и ФС загрузчика Grub (/boot) необходимо при восстановлении сделать новые UUID.

fsarchiver restfs -j32 /mnt/dump/image.fsa \
          id=0,dest=/dev/sda1,uuid=$(uuidgen) \
          id=1,dest=/dev/sda2,uuid=$(uuidgen) \
          id=2,dest=/dev/mapper/bercutvg-root \
          id=3,dest=/dev/mapper/bercutvg-var \
          id=4,dest=/dev/mapper/bercutvg-home \
          id=5,dest=/dev/mapper/bercutvg-opt  \
          id=6,dest=/dev/mapper/bercutvg-u01

Подключаем развернутые данные:

mkdir /mnt/root
 mount /dev/mapper/bercutvg-root /mnt/root
 mount /dev/mapper/bercutvg-var /mnt/root/var
 mount /dev/mapper/bercutvg-home /mnt/root/home 	
 mount /dev/mapper/bercutvg-opt /mnt/root/opt		
 mount /dev/mapper/bercutvg-u01 /mnt/root/u01 	
 mount /dev/sda2 /mnt/root/boot
 mount /dev/sda1 /mnt/root/boot/efi
 mount -o bind /proc /mnt/root/proc
 mount -o bind /sys /mnt/root/sys
 mount -o bind /sys/firmware/efi/efivars /mnt/root/sys/firmware/efi/efivars
 mount -o bind /dev /mnt/root/dev
 chroot /mnt/root /bin/bash

Монтирование efivars (строка 11 в командах выше) выполнено для того, чтобы у нас заработала команда efibootmgr , которую мы будем использовать далее.

Взять новые тэги от ФС /boot/efi и /boot, размещенной на /dev/sda1 и на /dev/sda2 и заменить их в /etc/fstab:

blkid -s UUID -o value /dev/sda1
blkid -s UUID -o value /dev/sda2
vim /etc/fstab

Сгенерировать новый конфигурационный файл для grub:

grub2-mkconfig -o  /boot/efi/EFI/redhat/grub.cfg 

Вот так можно посмотреть информацию о том, какое ядро запустит grub2 и что он найдет:

grubby --default-kernel
grubby --default-index
grubby --info=ALL

Далее необходимо проверить последовательность загрузки efi:

efibootmgr -v 

В моем случае вариант по умолчанию, это

Boot0003* Oracle Linux  HD(1,GPT,2be2b68a-ab31-48a4-bd15-526cf766174b,0x800,0x80000)/File(\EFI\redhat\shimx64.efi)

Что неправильно, т.к. 2be2b68a-ab31-48a4-bd15-526cf766174b — это старое NVMe устройство:

blkid | grep 2be2b68a-ab31-48a4-bd15-526cf766174b
/dev/nvme0n1p1: SEC_TYPE="msdos" UUID="F6D4-B2BB" BLOCK_SIZE="512" TYPE="vfat" PARTLABEL="EFI System Partition" PARTUUID="2be2b68a-ab31-48a4-bd15-526cf766174b"

Старый вариант загрузки можно было бы удалить командой

efibootmgr -Bb 0003;   # пока не запускаем её!

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

Добавим еще один вариант загрузки с устройства (-d) /dev/sda c первой партиции (-p):

efibootmgr -c -L 'Oracle Linux 8 sas raid' -d /dev/sda -p 1 -l '\EFI\redhat\shimx64.efi'

Вновь проверяем BootOrder в efibootmgr -v. Если он нас не устраивает, поменять можно так:

efibootmgr -o 0006,0001,0002,0000,0004,0005,0003

Выходим из chroot, отмонтируем все файловые системы в обратном порядке и перезагружаемся:

exit
for fs in /mnt/root/dev \
          /mnt/root/sys/firmware/efi/efivars \
          /mnt/root/sys \
          /mnt/root/proc \
          /mnt/root/boot/efi \
          /mnt/root/boot \
          /mnt/root/var \
          /mnt/root; do umount $fs; done
reboot

Проверка и очистка

Проверяем, что наша система загрузилась с правильного устройства:

mount | egrep 'boot|root'
pvs

Ищем в вариантах загрузки старую запись с NVMe 2be2b68a-ab31-48a4-bd15-526cf766174b и удаляем её:

# efibootmgr -v # blkid | grep 2be2b68a-ab31-48a4-bd15-526cf766174b
/dev/nvme0n1p1: SEC_TYPE="msdos" UUID="F6D4-B2BB" BLOCK_SIZE="512" TYPE="vfat" PARTLABEL="EFI System Partition" PARTUUID="2be2b68a-ab31-48a4-bd15-526cf766174b"
# efibootmgr -Bb 0003

Проверяем, что у нас сохранился правильный BootOrder.

Позже (через пару дней после основных работ) мы удалим ненужную LVM группу oldbercutvg примерно так:

lvchange -an oldbercutvg/home
lvchange -an oldbercutvg/opt
lvchange -an oldbercutvg/root
lvchange -an oldbercutvg/swap
lvchange -an oldbercutvg/u01
lvchange -an oldbercutvg/var
lvremove  oldbercutvg/home
lvremove  oldbercutvg/opt
lvremove  oldbercutvg/root
lvremove  oldbercutvg/swap
lvremove  oldbercutvg/u01
lvremove  oldbercutvg/var
vgremove oldbercutvg
pvremove /dev/nvme0n1p3
pvs

А затем почистим таблицы разделов, например, так:

# gdisk /dev/nvme0n1
p
d
3
d
2
d
w
Y
partprobe  /dev/nvme0n1

Послесловие

/dev/nvme0n1 и /dev/nvme1n1 теперь логически свободны, на /dev/nvme1n1 еще лежит дамп с бэкапом, пусть полежит еще немного.

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

P.S. Да, можно было переносить данные файловых систем напрямую и обойтись без создания промежуточного дампа с fsarchiver(как и без самой этой утилиты), но статья знакомит читателей с таким инструментом, и он может давать некоторые дополнительные возможности. Например, мы могли бы переносить данные на этот же системный диск с его переразметкой и у нас сохранился полный "холодный" бэкап системы на /dev/nvme1n1. Сам сервер резвый, дамп создавался и разворачивался за несколько минут. При планировании ваших работ, конечно, следует учитывать время создания дампа.

Комментарии (0)