Перенос Linux на другой NVMe без переустановки: что пошло не по плану
Содержание
Ноутбук у меня рабочий, на нём Linux Mint, и системный диск на 512 ГБ уже начал поджимать. Решил сделать сразу два дела: добавить второй NVMe под данные, а системный заменить на терабайтник. Без переустановки системы, конечно, — за годы она обросла настройками, ключами, окружением для учёбы, и собирать всё заново желания нет никакого.
Первая часть прошла гладко. Со второй вышла история, ради которой я, собственно, и пишу эту заметку.
Все примеры ниже — из моего Mint, но схема подходит для любого дистрибутива. Отличия для Debian, Fedora, Arch и других я собрал в отдельном разделе в конце.
Длинные цепочки команд я разбиваю на строки обратным слешем \. Каждый блок можно скопировать в терминал целиком: он выполнится как одна команда и остановится на первой ошибке.
Сначала разобраться, где какой диск
Поставил в свободный слот Samsung 980 PRO на 2 ТБ, загрузился и первым делом посмотрел, как система видит диски:
lsblk -o NAME,SIZE,MODEL,FSTYPE,MOUNTPOINT
И тут первый подводный камень. Я по привычке ожидал, что новый диск будет nvme1n1, а старый системный — nvme0n1. Оказалось ровно наоборот: новый Samsung получил имя nvme0n1, а системный Micron стал nvme1n1. Нумерация NVMe зависит от порядка инициализации контроллеров, а не от того, какой диск стоял раньше.
Отсюда правило, которое я повторяю всем коллегам: никогда не копируйте команды разметки с чужих примеров, не сверившись с lsblk. Ориентироваться нужно по колонкам MODEL и SIZE, а не по имени устройства. Одна ошибка в букве — и mkfs с удовольствием отформатирует вашу систему.
Подключение второго диска под данные
Дальше всё стандартно. Перед разметкой стоит проверить здоровье диска, особенно если он куплен не в официальном магазине:
sudo apt install -y nvme-cli smartmontools && \
sudo smartctl -a /dev/nvme0n1
У нового диска Percentage Used должен быть 0%, Media and Data Integrity Errors — 0. Если там уже набежали терабайты записи и тысячи часов работы, то диск был в употреблении.
Разметка и форматирование:
sudo parted -s /dev/nvme0n1 mklabel gpt mkpart data ext4 0% 100% && \
sudo mkfs.ext4 -L data /dev/nvme0n1p1
И монтирование через fstab по UUID:
sudo mkdir -p /mnt/data && \
UUID=$(sudo blkid -s UUID -o value /dev/nvme0n1p1) && \
echo "UUID=$UUID /mnt/data ext4 defaults,noatime,nofail 0 2" | \
sudo tee -a /etc/fstab && \
sudo systemctl daemon-reload && sudo mount -a && \
sudo chown $USER:$USER /mnt/data
Пара слов про опции. nofail я ставлю на все несистемные диски: если такой диск когда-нибудь отвалится, система всё равно загрузится, а не повиснет в emergency mode. noatime убирает запись времени доступа при каждом чтении файла — для SSD это лишняя нагрузка, которая никому не нужна.
Чтобы диск был диском, а не папкой
Небольшая, но приятная мелочь. Смонтированный в /mnt раздел файловый менеджер Nemo показывает просто как каталог. Если хочется, чтобы он висел в боковой панели в разделе «Устройства», как флешка, достаточно добавить в fstab пару опций GVFS:
G=x-gvfs-show,x-gvfs-name=Data,x-gvfs-icon=drive-harddisk && \
sudo sed -i "/\/mnt\/data/s|nofail|nofail,$G|" /etc/fstab && \
sudo systemctl daemon-reload && \
sudo umount /mnt/data && sudo mount -a && nemo -q
x-gvfs-show включает показ в панели, x-gvfs-name задаёт имя, x-gvfs-icon — иконку. Путь /mnt/data при этом остаётся прежним, так что скрипты и ссылки не ломаются. Если в имени нужен пробел, он записывается как \040.
Про TRIM
Многие спрашивают, нужно ли в Linux вручную включать TRIM. В Mint и других системах на базе Ubuntu — нет, он уже работает: раз в неделю его запускает fstrim.timer. Проверить достаточно так:
systemctl list-timers fstrim.timer && lsblk --discard
Ненулевые значения в колонках DISC-GRAN и DISC-MAX означают, что диск TRIM поддерживает. После форматирования нового диска можно прогнать его вручную через sudo fstrim -av.
Зачем он вообще нужен? SSD не умеет перезаписывать ячейки на месте: перед записью блок нужно стереть, причём крупными кусками. Когда вы удаляете файл, файловая система помечает место свободным только у себя, а контроллер диска об этом не знает и продолжает таскать мёртвые данные при сборке мусора. TRIM сообщает ему, что эти блоки можно выбросить. В итоге скорость записи держится ровной, а износ ниже.
Опцию discard в fstab, то есть непрерывный TRIM, я не использую. Периодический fstrim на NVMe работает лучше и не тормозит при массовом удалении файлов.
План переноса системы
Теперь главное. Задумка была простая: клонировать системный диск на терабайтник целиком, растянуть последний раздел и поставить новый диск на место старого.
Почему именно dd, а не rsync или Clonezilla? Потому что при побайтовом клонировании копируются UUID файловых систем и PARTUUID разделов. А значит, fstab, GRUB и загрузочная запись в UEFI продолжают ссылаться на правильные разделы и не требуют никаких правок. Для этого, правда, fstab должен ссылаться именно на UUID, а не на /dev/nvme.... Проверяется так:
grep -v '^#' /etc/fstab
У меня всё было по UUID, как и положено в свежих установках Mint.
Свободного слота под третий диск в ноутбуке нет, поэтому терабайтник я подключил через USB Type-C адаптер. Тут стоит проверить, на какой скорости адаптер реально работает:
lsblk -o NAME,SIZE,MODEL,TRAN && lsusb -t | grep -iE "uas|storage"
Нужен драйвер uas и скорость 5000M или 10000M. Если видите 480M, адаптер работает как USB 2.0, и клонирование затянется на несколько часов.
Сам перенос выполняется только с Live-флешки. Копировать работающую систему нельзя: файлы меняются прямо во время копирования, и на выходе получится битая копия. Ещё один момент: Live-система сама монтирует найденные разделы, поэтому перед dd приёмник нужно отмонтировать:
sudo umount /dev/sdb1 2>/dev/null; \
lsblk -o NAME,SIZE,MODEL,TRAN,MOUNTPOINT
Клонирование:
sudo dd if=/dev/nvme1n1 of=/dev/sdb bs=64M \
status=progress conv=fsync oflag=direct
После этого на новом диске остаётся неразмеченное место, а резервная копия таблицы GPT лежит не в конце диска, где ей положено быть. Это исправляется одной цепочкой:
sudo sgdisk -e /dev/sdb && sudo partprobe /dev/sdb && \
sudo parted -s /dev/sdb resizepart 4 100% && \
sudo e2fsck -f /dev/sdb4 && sudo resize2fs /dev/sdb4
sgdisk -e переносит резервную GPT в конец, без этого parted расширять раздел откажется. Дальше раздел растягивается до конца диска, проверяется и расширяется файловая система.
Оставалось выключить ноутбук, вынуть старый диск и поставить новый на его место.
И тут выяснилось, что вынимать нечего
Разобрал ноутбук и увидел, что системный Micron на 512 ГБ — это не модуль M.2 в слоте, а чип, распаянный прямо на материнской плате. Производители всё чаще так делают в тонких ноутбуках: экономят место и стоимость, а пользователю остаётся один-единственный слот, который у меня уже занимал Samsung.
Вывод банальный, но я на нём споткнулся: прежде чем покупать диск на замену, посмотрите сервис-мануал или фото разобранной модели. Наличие NVMe в характеристиках ничего не говорит о том, съёмный он или нет.
Что делать, если старый диск не вынимается
Терабайтник в этой схеме оказался не нужен: в единственном слоте уже стоит Samsung на 2 ТБ, и он заметно быстрее Micron 2450. Логичнее перенести систему на него, а распаянный диск оставить под данные.
Схема та же: dd с Micron на Samsung и растягивание корневого раздела, только приёмник теперь nvme0n1. Всё, что лежало на Samsung, при клонировании будет стёрто, поэтому нужные данные стоит заранее скопировать. С Live-флешки:
sudo umount /dev/nvme0n1p1 2>/dev/null; \
sudo dd if=/dev/nvme1n1 of=/dev/nvme0n1 bs=64M \
status=progress conv=fsync oflag=direct
sudo sgdisk -e /dev/nvme0n1 && sudo partprobe /dev/nvme0n1 && \
sudo parted -s /dev/nvme0n1 resizepart 4 100% && \
sudo e2fsck -f /dev/nvme0n1p4 && sudo resize2fs /dev/nvme0n1p4
Но появляется проблема, которой нет при обычной замене. Старый диск физически остаётся в системе, и после клонирования у двух дисков полностью совпадают UUID и PARTUUID. Загрузчик и systemd запросто смонтируют не тот раздел, и разбираться, почему изменения «пропадают», можно долго.
Поэтому после клонирования, ещё с Live-флешки, нужно сменить все идентификаторы на старом диске:
sudo swapoff -a && \
sudo sgdisk -G /dev/nvme1n1 && \
sudo partprobe /dev/nvme1n1 && \
sudo e2fsck -f /dev/nvme1n1p4 && \
sudo tune2fs -U random /dev/nvme1n1p4 && \
sudo e2fsck -f /dev/nvme1n1p3 && \
sudo tune2fs -U random /dev/nvme1n1p3 && \
sudo mkswap /dev/nvme1n1p2 && \
sudo fatlabel -i -r /dev/nvme1n1p1 && \
sudo blkid
Здесь swapoff -a отключает swap, который Live-система могла подхватить со старого диска. sgdisk -G генерирует новые GUID для диска и разделов, tune2fs -U random меняет UUID у ext4, mkswap пересоздаёт swap с новым UUID, а fatlabel меняет идентификатор раздела EFI. В выводе blkid не должно остаться ни одного совпадения между дисками.
И ещё одна мелочь. В fstab на клоне осталась строка /mnt/data, которая ссылается на уже несуществующий раздел Samsung. Благодаря nofail система всё равно загрузится, но строку лучше убрать:
sudo mount /dev/nvme0n1p4 /mnt && \
sudo sed -i '\|/mnt/data|d' /mnt/etc/fstab && \
sudo umount /mnt
Приятный побочный эффект: запись загрузки в UEFI ссылается на PARTUUID раздела EFI, а он вместе с клоном переехал на новый диск. Так что система, как правило, сразу грузится с нового диска без всяких танцев с загрузчиком.
Если всё-таки не грузится
На этот случай держу под рукой переустановку GRUB через chroot. С Live-флешки, загруженной именно в режиме UEFI:
sudo mount /dev/nvme0n1p4 /mnt && \
sudo mount /dev/nvme0n1p1 /mnt/boot/efi && \
for d in dev proc sys run; do \
sudo mount --rbind /$d /mnt/$d; done && \
sudo chroot /mnt
Внутри chroot:
grub-install --target=x86_64-efi --efi-directory=/boot/efi \
--bootloader-id=ubuntu --recheck && \
grub-install --target=x86_64-efi --efi-directory=/boot/efi \
--removable && \
update-grub && efibootmgr -v
Mint использует каталог ubuntu в разделе EFI, поэтому --bootloader-id менять не нужно. Второй вызов с --removable кладёт загрузчик ещё и по стандартному пути \EFI\BOOT\BOOTX64.EFI, который любой UEFI находит сам, даже если запись в NVRAM потерялась. Я делаю это всегда: страховка ничего не стоит.
Если у вас не Mint
Сама схема — dd, sgdisk -e, расширение раздела, fstab по UUID — одинакова для любого Linux. Отличаются детали.
Установка пакетов. apt есть только в семействе Debian и Ubuntu. В Fedora и RHEL это dnf, в Arch — pacman, в openSUSE — zypper. Сами пакеты nvme-cli и smartmontools называются везде одинаково.
TRIM. В Ubuntu, Mint, Debian и Fedora fstrim.timer включён из коробки. В Arch и ряде других дистрибутивов его нужно включить самому:
sudo systemctl enable --now fstrim.timer && \
systemctl list-timers fstrim.timer
Восстановление GRUB. Здесь разница самая заметная:
--bootloader-id=ubuntuподходит только для Ubuntu и Mint. В Debian этоdebian, у других дистрибутивов своё имя. Посмотреть его проще всего в каталоге/boot/efi/EFI/.- Команда
update-grubесть только в Debian и Ubuntu. В Arch конфиг пересобирается черезgrub-mkconfig -o /boot/grub/grub.cfg, в Fedora — черезgrub2-mkconfig -o /boot/grub2/grub.cfg. - В Fedora и RHEL на UEFI
grub2-installне используют вообще. Вместо этого в chroot переустанавливают загрузчик пакетами:dnf reinstall shim-x64 grub2-efi-x64. - В Arch раздел EFI часто смонтирован не в
/boot/efi, а в/bootили/efi. Путь в--efi-directoryнужно подставить свой. - Pop!_OS и часть других дистрибутивов используют systemd-boot вместо GRUB. Там восстановление делается через
bootctl install.
Btrfs вместо ext4. В Fedora и openSUSE корень по умолчанию на Btrfs, и команды e2fsck, resize2fs, tune2fs к нему не подходят. Раздел после parted resizepart расширяется на смонтированной системе:
sudo btrfs filesystem resize max /
UUID на старом диске меняется через sudo btrfstune -u /dev/nvme1n1pX (раздел должен быть отмонтирован). С Btrfs дубликаты UUID особенно опасны: ядро считает такие разделы частями одной файловой системы и может начать писать не туда. Если старый диск остаётся в системе, смена UUID обязательна ещё до первой загрузки.
LVM и шифрование LUKS. Если при установке вы выбрали шифрование диска или LVM (в RHEL и его клонах это вариант по умолчанию), после растягивания раздела добавляются шаги:
sudo cryptsetup resize имя_luks && \
sudo pvresize /dev/mapper/имя_luks && \
sudo lvextend -r -l +100%FREE /dev/имя_vg/root
Без LUKS первая команда не нужна, а pvresize выполняется прямо на разделе. Если старый диск с LVM остаётся подключённым, у клона будут те же имя группы томов и UUID. Переименовать старую копию можно через vgimportclone.
Файловые менеджеры. Опции x-gvfs-show и x-gvfs-name понимают Nemo (Cinnamon), Nautilus (GNOME), Caja (MATE) и Thunar (Xfce). Dolphin в KDE их игнорирует: там диск проще закрепить в панели «Места» вручную.
Что я вынес из этой истории
- Имена NVMe-устройств не постоянны. Сверяйтесь с
lsblkпо модели и размеру перед каждой разрушающей командой. ddплюсsgdisk -eиresize2fs— самый простой способ переехать на диск большего размера без переустановки, при условии что fstab ссылается на UUID.- Два диска с одинаковыми UUID в одной системе — это проблема. Если старый диск остаётся, идентификаторы на нём нужно сменить.
- Старый диск после переноса я не форматирую минимум несколько дней. Это готовый бэкап на случай, если что-то всплывёт.
Обсуждение
Комментарии работают через GitHub: для ответа нужен аккаунт GitHub. Вопрос без регистрации — напишите мне напрямую.