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

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

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


  1. 4chemist
    08.11.2024 23:12

    Создать на HDD три раздела, на третьем сделать pvcreate, добавить его к существующей группе vgextend, затем заняться уменьшением томов lvresize и все такое, переместить тома на HDD pvmove, удалить из группы vgreduce NVME.

    Виртуозы делают эту процедуру без выведения сервера из работы. LVM написан админами для админов.


    1. n27051538 Автор
      08.11.2024 23:12

      Можно, но потом прикольно будет про это забыть и вспомнить, когда сервер через несколько лет сам не сможет загрузиться. Можно забыть про драйвар FC HBA, пропустить -r в lvreduce или просто рухнет питание ЦОДа во время работ.

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


      1. omgiafs
        08.11.2024 23:12

        Пара миллионов абонентов на одном физическом сервере? Без даже дублирования? Ваш архитектор не тот, за кого себя выдаёт.


    1. n27051538 Автор
      08.11.2024 23:12

      Уменьшение размера файловой системы недоступно для XFS, а для EXT4 возможно только с отмонтированием. Так что если мы с вами будем спорить на "домашнем компе", я думаю, что у вас не получится без перезагрузок налету.


  1. n27051538 Автор
    08.11.2024 23:12

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


    1. 4chemist
      08.11.2024 23:12

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

      Не выдумывайте странные форсмажоры типа "через сотни лет не сможет загрузиться", "рухнет питание цод Tier3", "Ты туда не ходи, ты сюда ходи. Кирпич башка попадёт, что тогда будет?" это просто смешно.


      1. useribs
        08.11.2024 23:12

        Что-то в этом есть, "регламенты - предтеча IaC", а вообще на сарказм похоже. Не соглашусь с критикой по изобретению форсмажоров, люди в подобном планировании дошли до того, что даже в облаках разные зоны доступности изобрели. На мой вкус избавиться бы от еще одной точки отказа - зеркало в mdraid/btrfs сделать вместо аппаратного raid.

        В целом неплохой инструмент автор описал fsarchiver, есть юзкейсы


      1. n27051538 Автор
        08.11.2024 23:12

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

        PS. В моих личных воспоминаниях есть четыре дизастера: сгоревший ЦОД в Екатеринбурге, сбой аварийного переключения ЦОДа в Ростове, сбой системы автозапуска генераторов в Петербурге, ложное срабатывпние системы пожаротушения в Петербурге. Если вы об этом не слышали, это не значит, что это миф. Более мелких случаев десятки. Не ездите в шлёпках на мотике, иногда ноги отрывает.


  1. ZVEZDO4ETik
    08.11.2024 23:12

    Интересно, явное написание "Enterprise Linux", а не Red Hat Enterprise Linux или Oracle Enterprise Linux было написано в виду сложившейся тяжелой внешнеполитической обстановочки?