Привет, Хабр!

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

Многие Linux‑серверы со временем сталкиваются с одной и той же проблемой: корневая файловая система постепенно «обрастает» данными приложений, снэпшоты виртуальных машин становятся слишком большими, резервное копирование занимает всё больше времени, а свободного места для классической миграции уже не остаётся. Особенно часто это встречается на старых системах, где изначальная разметка дисков создавалась без понимания того, какое прикладное программное обеспечение будет использоваться в будущем.

Так возникает необходимость вынести данные приложения на отдельную файловую систему, но традиционный подход требует полного копирования больших объёмов данных и длительного простоя сервисов. В этой статье мы покажем способ изменить разметку уже работающей Linux‑системы без переустановки ОС и без копирования сотен гигабайт данных.

В Linux считается хорошей практикой размещать файловые системы (ФС) для прикладного программного обеспечения (ППО) на отдельных от ФС операционной системы (ОС). Например, если ППО располагается в директории /opt/example/, то ФС размещаем на отдельный диск. У этого подхода есть следующие преимущества:

  1. Если из‑за сбоя ППО начнет занимать слишком много места, файловая система может переполниться. В такой ситуации, скорее всего, «зависнет» только само ППО, тогда как операционная система продолжит работать в штатном режиме. Это позволит системному администратору подключиться к системе обычным способом (например, через Secure Shell — SSH) и устранить проблему без загрузки в аварийном режиме для очистки диска.

  2. Если сервер работает в виде виртуальной машины (ВМ), удобно выносить файловую систему ППО на отдельный диск. В этом случае в снэпшот ВМ можно включать только диск с ОС, исключая диск с ППО. Это уменьшает размер этого снэпшота и ускоряет работу с ним. Такой подход особенно полезен при изменениях конфигурации ОС, когда данные ППО не требуют резервного копирования. Кроме того, это может быть важно для некоторых СРК.

Однако есть и нюансы. Человек, ответственный за разворачивание ОС, должен знать, какое именно ППО там будет стоять и в каких именно директориях оно будет хранить файлы. Помним: что‑то хранится в /var/www/, что‑то — в /home/something/ или в /u01/ и так далее.

Но зачастую у этого человека нет этой информации. Перед ним просто ставят задачу: «Сделай сервер Linux с диском в 200 гигабайт» или что‑то в этом роде. При таких вводных простейший выход — сделать один диск и всего одну ФС — корневую, /. Но такой подход имеет недостатки, которые являются зеркальным отражением его плюсов. Возникают ситуации, когда нужно переделывать разметку ФС на уже существующих серверах. Решение «в лоб» — присоединить еще один диск и вынести данные на ФС /opt/example/, например.

Итак, дано: весь диск 200 гигабайт, а размер данных в /opt/example/ — 180 гигабайт. Нам понадобится еще один 200-гигабайтный диск для того, чтобы скопировать данные, и после переноса на / останется много свободного места. Разумеется, копирование 180 гигабайт даже на быстрых дисках не происходит мгновенно. Такой процесс придется проводить с полной остановкой работы ППО, чтобы копии получились правильными и без искажений.

Мы написали этот текст, чтобы упростить вашу жизнь в похожей ситуации.

Термины:

  • BIOS, basic input‑output system, Базовая система ввода‑вывода

  • UEFI, Unified Extensible Firmware Interface, Единый расширяемый интерфейс программного обеспечения,

  • LVM Менеджер логических томов, logical volume manager

  • GRUB Великий объединенный загрузчик, GRand Unified Bootloader

Теперь приступим

Итак, у нас есть стандартный Debian 13 со стандартной разметкой из одного диска, установленный в BIOS‑режиме и с тестовым пользователем в /opt/example/. В нашем случае эта директория почти ничего не весит, но мы будем проводить работы так, как будто она весит много гигабайт:

Скрытый текст
root@debian:~# lsblk
NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
sda      8:0    0   32G  0 disk
├─sda1   8:1    0 30,3G  0 part /
├─sda2   8:2    0    1K  0 part
└─sda5   8:5    0  1,7G  0 part [SWAP]
sr0     11:0    1 1024M  0 rom
root@debian:~# fdisk -l /dev/sda
Disk /dev/sda: 32 GiB, 34359738368 bytes, 67108864 sectors
Disk model: VBOX HARDDISK
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xa08b7ba1

Device     Boot    Start      End  Sectors  Size Id Type
/dev/sda1  *        2048 63578111 63576064 30,3G 83 Linux
/dev/sda2       63580158 67106815  3526658  1,7G  f W95 Ext'd (LBA)
/dev/sda5       63580160 67106815  3526656  1,7G 82 Linux swap / Solaris
root@debian:~# df -hT /
Файловая система Тип  Размер Использовано  Дост Использовано% Cмонтировано в
/dev/sda1        ext4    30G         1,2G   27G            5% /
root@debian:~# ls -laR /opt/
/opt/:
итого 12
drwxr-xr-x  3 root    root    4096 апр  6 18:26 .
drwxr-xr-x 18 root    root    4096 апр  6 16:25 ..
drwx------  2 example example 4096 апр  6 18:27 example

/opt/example:
итого 24
drwx------ 2 example example 4096 апр  6 18:27 .
drwxr-xr-x 3 root    root    4096 апр  6 18:26 ..
-rw------- 1 example example   19 апр  6 18:27 .bash_history
-rw-r--r-- 1 example example  220 мар  8 18:21 .bash_logout
-rw-r--r-- 1 example example 3526 мар  8 18:21 .bashrc
-rw-rw-r-- 1 example example    0 апр  6 18:27 file1
-rw-r--r-- 1 example example  807 мар  8 18:21 .profile
root@debian:~#

Для примера добавим диск в 20 гигабайт и проведем его разметку так, как нам необходимо. При этом поставим пакет lvm2, чтобы задействовать LVM на этом диске:

Скрытый текст
root@debian:~# lsblk
NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
sda      8:0    0   32G  0 disk
├─sda1   8:1    0 30,3G  0 part /
├─sda2   8:2    0    1K  0 part
└─sda5   8:5    0  1,7G  0 part [SWAP]
sdb      8:16   0   20G  0 disk
sr0     11:0    1 1024M  0 rom
root@debian:~# fdisk /dev/sdb

Welcome to fdisk (util-linux 2.41).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.

Device does not contain a recognized partition table.
Created a new DOS (MBR) disklabel with disk identifier 0x0131a0fb.

Command (m for help): n
Partition type
   p   primary (0 primary, 0 extended, 4 free)
   e   extended (container for logical partitions)
Select (default p):

Using default response p.
Partition number (1-4, default 1):
First sector (2048-41943039, default 2048):
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-41943039, default 41943039):

Created a new partition 1 of type 'Linux' and of size 20 GiB.

Command (m for help): t
Selected partition 1
Hex code or alias (type L to list all): lvm
Changed type of partition 'Linux' to 'Linux LVM'.

Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.

root@debian:~# fdisk -l /dev/sdb
Disk /dev/sdb: 20 GiB, 21474836480 bytes, 41943040 sectors
Disk model: VBOX HARDDISK
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x0131a0fb

Device     Boot Start      End  Sectors Size Id Type
/dev/sdb1        2048 41943039 41940992  20G 8e Linux LVM
root@debian:~# apt install lvm2
Установка:
  lvm2

Установка зависимостей:
  dmeventd  libaio1t64  libdevmapper-event1.02.1  liblvm2cmd2.03  thin-provisioning-tools

Сводка:
  Обновление: 0, Установка: 6, Удаление: 0, Пропуск обновления: 0
  Объём загрузки: 3 206 kB
  Требуемое пространство: 11,1 MB / 29,0 GB доступно

Продолжить? [Д/н] y
Пол:1 http://ftp.ru.debian.org/debian trixie/main amd64 libdevmapper-event1.02.1 amd64 2:1.02.205-2 [13,6 kB]
Пол:2 http://ftp.ru.debian.org/debian trixie/main amd64 libaio1t64 amd64 0.3.113-8+b1 [14,9 kB]
Пол:3 http://ftp.ru.debian.org/debian trixie/main amd64 liblvm2cmd2.03 amd64 2.03.31-2 [785 kB]
Пол:4 http://ftp.ru.debian.org/debian trixie/main amd64 dmeventd amd64 2:1.02.205-2 [62,4 kB]
Пол:5 http://ftp.ru.debian.org/debian trixie/main amd64 lvm2 amd64 2.03.31-2 [1 287 kB]
Пол:6 http://ftp.ru.debian.org/debian trixie/main amd64 thin-provisioning-tools amd64 1.1.0-4+b1 [1 042 kB]
Получено 3 206 kB за 5с (585 kB/s)
Выбор ранее не выбранного пакета libdevmapper-event1.02.1:amd64.
(Чтение базы данных … на данный момент установлено 36605 файлов и каталогов.)
Подготовка к распаковке …/0-libdevmapper-event1.02.1_2%3a1.02.205-2_amd64.deb …
Распаковывается libdevmapper-event1.02.1:amd64 (2:1.02.205-2) …
Выбор ранее не выбранного пакета libaio1t64:amd64.
Подготовка к распаковке …/1-libaio1t64_0.3.113-8+b1_amd64.deb …
Распаковывается libaio1t64:amd64 (0.3.113-8+b1) …
Выбор ранее не выбранного пакета liblvm2cmd2.03:amd64.
Подготовка к распаковке …/2-liblvm2cmd2.03_2.03.31-2_amd64.deb …
Распаковывается liblvm2cmd2.03:amd64 (2.03.31-2) …
Выбор ранее не выбранного пакета dmeventd.
Подготовка к распаковке …/3-dmeventd_2%3a1.02.205-2_amd64.deb …
Распаковывается dmeventd (2:1.02.205-2) …
Выбор ранее не выбранного пакета lvm2.
Подготовка к распаковке …/4-lvm2_2.03.31-2_amd64.deb …
Распаковывается lvm2 (2.03.31-2) …
Выбор ранее не выбранного пакета thin-provisioning-tools.
Подготовка к распаковке …/5-thin-provisioning-tools_1.1.0-4+b1_amd64.deb …
Распаковывается thin-provisioning-tools (1.1.0-4+b1) …
Настраивается пакет libdevmapper-event1.02.1:amd64 (2:1.02.205-2) …
Настраивается пакет thin-provisioning-tools (1.1.0-4+b1) …
Настраивается пакет libaio1t64:amd64 (0.3.113-8+b1) …
Настраивается пакет liblvm2cmd2.03:amd64 (2.03.31-2) …
Настраивается пакет dmeventd (2:1.02.205-2) …
Created symlink '/etc/systemd/system/sockets.target.wants/dm-event.socket' → '/usr/lib/systemd/system/dm-event.socket'.
dm-event.service is a disabled or a static unit, not starting it.
Настраивается пакет lvm2 (2.03.31-2) …
Created symlink '/etc/systemd/system/sysinit.target.wants/blk-availability.service' → '/usr/lib/systemd/system/blk-availability.service'.
Created symlink '/etc/systemd/system/sysinit.target.wants/lvm2-monitor.service' → '/usr/lib/systemd/system/lvm2-monitor.service'.
Created symlink '/etc/systemd/system/sysinit.target.wants/lvm2-lvmpolld.socket' → '/usr/lib/systemd/system/lvm2-lvmpolld.socket'.
Обрабатываются триггеры для initramfs-tools (0.148.3) …
update-initramfs: Generating /boot/initrd.img-6.12.74+deb13+1-amd64
Обрабатываются триггеры для libc-bin (2.41-12+deb13u2) …
Обрабатываются триггеры для man-db (2.13.1-1) …
root@debian:~# pvcreate /dev/sdb1
  Physical volume "/dev/sdb1" successfully created.
root@debian:~# vgcreate vg_root /dev/sdb1
  Volume group "vg_root" successfully created
root@debian:~# lvcreate -L4g vg_root -n lv_swap
  Logical volume "lv_swap" created.
root@debian:~# lvcreate -l100%FREE vg_root -n lv_root
  Logical volume "lv_root" created.
root@debian:~# mkswap /dev/mapper/vg_root-lv_swap
Setting up swapspace version 1, size = 4 GiB (4294963200 bytes)
no label, UUID=1c40ccc2-e5e2-4d05-9b07-172f94cb2c7f
root@debian:~# mkfs.ext4 /dev/mapper/vg_root-lv_root
mke2fs 1.47.2 (1-Jan-2025)
Creating filesystem with 4193280 4k blocks and 1048576 inodes
Filesystem UUID: e086d44c-cfb9-4b0d-beac-1621256af630
Superblock backups stored on blocks:
        32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
        4096000

Allocating group tables: done
Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done


root@debian:~# lsblk
NAME                MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
sda                   8:0    0   32G  0 disk
├─sda1                8:1    0 30,3G  0 part /
├─sda2                8:2    0    1K  0 part
└─sda5                8:5    0  1,7G  0 part [SWAP]
sdb                   8:16   0   20G  0 disk
└─sdb1                8:17   0   20G  0 part
  ├─vg_root-lv_swap 254:0    0    4G  0 lvm
  └─vg_root-lv_root 254:1    0   16G  0 lvm
sr0                  11:0    1 1024M  0 rom
root@debian:~#

Если бы мы хотели использовать вместо стандартной ФС ext4 другую, XFS, то пришлось бы ставить пакет xfsprogs. Обратите внимание: в примере выше мы не создали отдельного /boot/ — потому что GRUB 2 в Debian умеет загружаться напрямую с LVM, но в CentOS, например, потребовался бы отдельный раздел под /boot/, так как там GRUB 2 так не может. Поэтому для простоты демонстрации создаем два логических тома: один под корень (/), другой под раздел подкачки (swap).

Теперь необходимо загрузиться с образа «живой» системы. В нашем случае это тоже Debian 13, но подойдет любой дистрибутив, который умеет работать с LVM и в котором есть программа удаленной синхронизации rsync (мы используем ее локально). После переносим системные файлы. Кстати, перед загрузкой с образа «живой» системы мы изменили нумерацию дисков: /dev/sdb стал /dev/sda и наоборот. Теперь прежний /dev/sdb станет системным диском и будет логично, что он первый:

Скрытый текст
root@debian:~# lsblk
NAME                MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
loop0                 7:0    0  2.8G  1 loop /run/live/rootfs/filesystem.squashfs
sda                   8:0    0   20G  0 disk
└─sda1                8:1    0   20G  0 part
  ├─vg_root-lv_swap 254:0    0    4G  0 lvm
  └─vg_root-lv_root 254:1    0   16G  0 lvm
sdb                   8:16   0   32G  0 disk
├─sdb1                8:17   0 30.3G  0 part
├─sdb2                8:18   0    1K  0 part
└─sdb5                8:21   0  1.7G  0 part
sr0                  11:0    1  3.5G  0 rom  /run/live/medium
root@debian:~# parted /dev/sdb
GNU Parted 3.6
Using /dev/sdb
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) rm 5
(parted) rm 2
(parted) resizepart 1 100%
(parted) quit
Information: You may need to update /etc/fstab.

root@debian:~# e2fsck -f /dev/sdb1
e2fsck 1.47.2 (1-Jan-2025)
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
/dev/sdb1: 41248/1986768 files (0.1% non-contiguous), 477393/7947008 blocks
root@debian:~# resize2fs /dev/sdb1
resize2fs 1.47.2 (1-Jan-2025)
Resizing the filesystem on /dev/sdb1 to 8388352 (4k) blocks.
The filesystem on /dev/sdb1 is now 8388352 (4k) blocks long.

root@debian:~# ls /mnt
root@debian:~# mkdir /mnt/1 /mnt/2
root@debian:~# mount /dev/sdb1 /mnt/1
root@debian:~# mount /dev/mapper/vg_root-lv_root /mnt/2
root@debian:~# swapon /dev/mapper/vg_root-lv_swap
root@debian:~# lsblk
NAME                MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
loop0                 7:0    0  2.8G  1 loop /run/live/rootfs/filesystem.squashfs
sda                   8:0    0   20G  0 disk
└─sda1                8:1    0   20G  0 part
  ├─vg_root-lv_swap 254:0    0    4G  0 lvm  [SWAP]
  └─vg_root-lv_root 254:1    0   16G  0 lvm  /mnt/2
sdb                   8:16   0   32G  0 disk
└─sdb1                8:17   0   32G  0 part /mnt/1
sr0                  11:0    1  3.5G  0 rom  /run/live/medium
root@debian:~# ls /mnt/1
bin   dev  home        initrd.img.old  lib64       media  opt   root  sbin  sys  usr  vmlinuz
boot  etc  initrd.img  lib             lost+found  mnt    proc  run   srv   tmp  var  vmlinuz.old
root@debian:~# ls /mnt/2
lost+found
root@debian:~# rsync -aAHXS --exclude 'opt/example/*' /mnt/1/* /mnt/2/
root@debian:~# ls /mnt/2/
bin   dev  home        initrd.img.old  lib64       media  opt   root  sbin  sys  usr  vmlinuz
boot  etc  initrd.img  lib             lost+found  mnt    proc  run   srv   tmp  var  vmlinuz.old
root@debian:~# ls /mnt/2/opt/
example
root@debian:~# ls -a /mnt/2/opt/example/
.  ..
root@debian:~#

В примере выше мы провели несколько операций:

  1. На диске /dev/sdb (бывший /dev/sda) удалили старый раздел подкачки, а бывший корень расширили на все свободное место.

  2. Создали директории /mnt/1/ и /mnt/2/

  3. Бывший корень смонтировали в /mnt/1/

  4. Новый корень, пока пустой, смонтировали в /mnt/2/

  5. Все файлы старого корня, кроме содержимого директории /opt/example/, скопировали в новый корень.

Теперь продолжим:

root@debian:~# cd /mnt/1
root@debian:/mnt/1# ls
bin   dev  home        initrd.img.old  lib64       media  opt   root  sbin  sys  usr  vmlinuz
boot  etc  initrd.img  lib             lost+found  mnt    proc  run   srv   tmp  var  vmlinuz.old
root@debian:/mnt/1# ls | grep -e opt -e lost
lost+found
opt
root@debian:/mnt/1# for i in $(ls | grep -v -e opt -e lost); do rm -rf $i; done
root@debian:/mnt/1# ls
lost+found  opt
root@debian:/mnt/1# mv -f opt/example/{.,}* .
root@debian:/mnt/1# ls -a
.  ..  .bash_history  .bash_logout  .bashrc  file1  lost+found  opt  .profile
root@debian:/mnt/1# rm -rf opt/
root@debian:/mnt/1# ls -la
total 36
drwxr-xr-x 3 root root  4096 Apr  6 17:02 .
drwxr-xr-x 1 root root    80 Apr  6 16:59 ..
-rw------- 1 1001 1001    19 Apr  6 15:27 .bash_history
-rw-r--r-- 1 1001 1001   220 Mar  8 15:21 .bash_logout
-rw-r--r-- 1 1001 1001  3526 Mar  8 15:21 .bashrc
-rw-rw-r-- 1 1001 1001     0 Apr  6 15:27 file1
drwx------ 2 root root 16384 Apr  6 13:19 lost+found
-rw-r--r-- 1 1001 1001   807 Mar  8 15:21 .profile
root@debian:/mnt/1#

Мы сделали из бывшего корня директорию /opt/example/. При этом файлы директории никуда не уходили с ФС, а только меняли свое расположение на ней.

Осталось поправить таблицу разделов и переставить GRUB 2 для BIOS, чтобы ОС могла загрузиться с новой разметкой:

Скрытый текст
root@debian:/mnt/1# cd
root@debian:~# umount /mnt/1
root@debian:~# lsblk
NAME                MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
loop0                 7:0    0  2.8G  1 loop /run/live/rootfs/filesystem.squashfs
sda                   8:0    0   20G  0 disk
└─sda1                8:1    0   20G  0 part
  ├─vg_root-lv_swap 254:0    0    4G  0 lvm  [SWAP]
  └─vg_root-lv_root 254:1    0   16G  0 lvm  /mnt/2
sdb                   8:16   0   32G  0 disk
└─sdb1                8:17   0   32G  0 part
sr0                  11:0    1  3.5G  0 rom  /run/live/medium
root@debian:~# dd if=/dev/sdb of=/mnt/2/root/mbrbackup bs=446 count=1
1+0 records in
1+0 records out
446 bytes copied, 0.00131297 s, 340 kB/s
root@debian:~# dd if=/dev/zero of=/dev/sdb bs=446 count=1
1+0 records in
1+0 records out
446 bytes copied, 0.00817176 s, 54.6 kB/s
root@debian:~# mount /dev/sdb1 /mnt/2/opt/example/
root@debian:~# lsblk
NAME                MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
loop0                 7:0    0  2.8G  1 loop /run/live/rootfs/filesystem.squashfs
sda                   8:0    0   20G  0 disk
└─sda1                8:1    0   20G  0 part
  ├─vg_root-lv_swap 254:0    0    4G  0 lvm  [SWAP]
  └─vg_root-lv_root 254:1    0   16G  0 lvm  /mnt/2
sdb                   8:16   0   32G  0 disk
└─sdb1                8:17   0   32G  0 part /mnt/2/opt/example
sr0                  11:0    1  3.5G  0 rom  /run/live/medium
root@debian:~# ls -ld /mnt/2/opt/example/
drwxr-xr-x 3 root root 4096 Apr  6 17:02 /mnt/2/opt/example/
root@debian:~# chown 1001:1001 /mnt/2/opt/example/
root@debian:~# chmod 700 /mnt/2/opt/example/
root@debian:~# ls -ld /mnt/2/opt/example/
drwx------ 3 1001 1001 4096 Apr  6 17:02 /mnt/2/opt/example/
root@debian:~# cp /mnt/2/etc/fstab /mnt/2/etc/fstab_backup
root@debian:~# grep -v -e "^#" -e "^$" /mnt/2/etc/fstab
UUID=2764cb53-516c-46f7-a498-cbdb80a31dea /               ext4    errors=remount-ro 0       1
UUID=d889b89e-035a-4556-89de-9092084ac37b none            swap    sw              0       0
/dev/sr0        /media/cdrom0   udf,iso9660 user,noauto     0       0
root@debian:~# nano /mnt/2/etc/fstab
root@debian:~# grep -v -e "^#" -e "^$" /mnt/2/etc/fstab
/dev/mapper/vg_root-lv_root /               ext4    errors=remount-ro 0       1
UUID=2764cb53-516c-46f7-a498-cbdb80a31dea /opt/example               ext4    defaults 0       2
/dev/mapper/vg_root-lv_swap none            swap    sw              0       0
/dev/sr0        /media/cdrom0   udf,iso9660 user,noauto     0       0
root@debian:~# cat /mnt/2/etc/initramfs-tools/conf.d/resume
RESUME=UUID=d889b89e-035a-4556-89de-9092084ac37b
root@debian:~# nano /mnt/2/etc/initramfs-tools/conf.d/resume
root@debian:~# cat /mnt/2/etc/initramfs-tools/conf.d/resume
RESUME=/dev/mapper/vg_root-lv_swap
root@debian:~# mount --bind /dev /mnt/2/dev
root@debian:~# mount --bind /proc /mnt/2/proc
root@debian:~# mount --bind /sys  /mnt/2/sys
root@debian:~# chroot /mnt/2
root@debian:/# grub-install /dev/sda
Installing for i386-pc platform.
Installation finished. No error reported.
root@debian:/# update-initramfs -u -k all
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
        LANGUAGE = (unset),
        LC_ALL = (unset),
        LC_CTYPE = (unset),
        LC_NUMERIC = (unset),
        LC_COLLATE = (unset),
        LC_TIME = (unset),
        LC_MESSAGES = (unset),
        LC_MONETARY = (unset),
        LC_ADDRESS = (unset),
        LC_IDENTIFICATION = (unset),
        LC_MEASUREMENT = (unset),
        LC_PAPER = (unset),
        LC_TELEPHONE = (unset),
        LC_NAME = (unset),
        LANG = "en_US.UTF-8"
    are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
        LANGUAGE = (unset),
        LC_ALL = (unset),
        LC_CTYPE = (unset),
        LC_NUMERIC = (unset),
        LC_COLLATE = (unset),
        LC_TIME = (unset),
        LC_MESSAGES = (unset),
        LC_MONETARY = (unset),
        LC_ADDRESS = (unset),
        LC_IDENTIFICATION = (unset),
        LC_MEASUREMENT = (unset),
        LC_PAPER = (unset),
        LC_TELEPHONE = (unset),
        LC_NAME = (unset),
        LANG = "en_US.UTF-8"
    are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").
update-initramfs: Generating /boot/initrd.img-6.12.74+deb13+1-amd64
update-initramfs: Generating /boot/initrd.img-6.12.73+deb13-amd64
root@debian:/# update-grub
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-6.12.74+deb13+1-amd64
Found initrd image: /boot/initrd.img-6.12.74+deb13+1-amd64
Found linux image: /boot/vmlinuz-6.12.73+deb13-amd64
Found initrd image: /boot/initrd.img-6.12.73+deb13-amd64
Warning: os-prober will not be executed to detect other bootable partitions.
Systems on them will not be added to the GRUB boot configuration.
Check GRUB_DISABLE_OS_PROBER documentation entry.
Adding boot menu entry for UEFI Firmware Settings ...
done
root@debian:/#

С помощью этой команды мы:

  1. Затерли нулями старую главную загрузочную запись (master boot record, MBR) на прежнем системном диске /dev/sdb, первые 446 байт. Благодаря этому ВМ не сможет случайно загружаться с уже нерабочим старым GRUB.

  2. Смонтировали ФС /opt/example/ на новое место в будущем chroot. В принципе, неважно, но на всякий случай.

  3. Скорректировали файл /etc/fstab в соответствии с тем, как сейчас будут располагаться ФС.

  4. Поправили файл /etc/initramfs‑tools/conf.d/resume, чтобы ОС искала раздел подкачки при загрузке в новом месте.

  5. Собрали chroot и вход в него.

  6. Изнутри chroot установили GRUB 2 на /dev/sda, обновили временную файловую систему, используемую ядром Linux при начальной загрузке (initramfs), чтобы ОС искала раздел подкачки при загрузке в новом месте, обновили конфигурацию GRUB 2.

Теперь выходим из всего этого и загружаемся уже в новую ОС:

root@debian:/# exit
exit
root@debian:~# poweroff

Как видно после загрузки, миграция / на иной диск прошла успешно:

root@debian:~# lsblk
NAME                MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
sda                   8:0    0   20G  0 disk
└─sda1                8:1    0   20G  0 part
  ├─vg_root-lv_swap 254:0    0    4G  0 lvm  [SWAP]
  └─vg_root-lv_root 254:1    0   16G  0 lvm  /
sdb                   8:16   0   32G  0 disk
└─sdb1                8:17   0   32G  0 part /opt/example
sr0                  11:0    1 1024M  0 rom
root@debian:~# ls -ld /opt/example/
drwx------ 3 example example 4096 апр  6 20:02 /opt/example/
root@debian:~# ls -la /opt/example/
итого 40
drwx------ 3 example example  4096 апр  6 20:02 .
drwxr-xr-x 3 root    root     4096 апр  6 18:26 ..
-rw------- 1 example example    19 апр  6 18:27 .bash_history
-rw-r--r-- 1 example example   220 мар  8 18:21 .bash_logout
-rw-r--r-- 1 example example  3526 мар  8 18:21 .bashrc
-rw-rw-r-- 1 example example     0 апр  6 18:27 file1
drwx------ 2 root    root    16384 апр  6 16:19 lost+found
-rw-r--r-- 1 example example   807 мар  8 18:21 .profile
root@debian:~# ls /
bin   dev  home        initrd.img.old  lib64       media  opt   root  sbin  sys  usr  vmlinuz
boot  etc  initrd.img  lib             lost+found  mnt    proc  run   srv   tmp  var  vmlinuz.old
root@debian:~#

Что мы можем еще сказать?

Миграции, описанные выше, позволяют исправить неудачную первоначальную разметку Linux‑систем без переустановки ОС и без полного копирования больших объёмов данных. В рассмотренном сценарии удалось перенести операционную систему на новый диск, сохранив данные приложения на прежней файловой системе и избежав длительного копирования сотен гигабайт.

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

При этом основная сложность подобных работ — не сами команды Linux, а аккуратная работа с загрузчиком, initramfs, UUID, LVM и порядком монтирования файловых систем.

Любые ошибки на этих этапах могут привести к неработоспособности системы после перезагрузки, поэтому подобные операции требуют:

  • резервных копий;

  • предварительного тестирования;

  • и чёткого понимания механики загрузки Linux.

Надеюсь, наш текст позволит вам не изобретать космолет заново. Удачи!