← Статьи

Перенос Linux на другой NVMe без переустановки: что пошло не по плану

· ~10 мин чтения

Содержание

Ноутбук у меня рабочий, на нём 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. Вопрос без регистрации — напишите мне напрямую.