Как перенести UEFI системный диск Enterprise Linux на другое устройство?

от автора

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

В статье рассмотрим способ переноса системного раздела ОС 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. Сам сервер резвый, дамп создавался и разворачивался за несколько минут. При планировании ваших работ, конечно, следует учитывать время создания дампа.


ссылка на оригинал статьи https://habr.com/ru/articles/838742/