Библиотека — Восстановление незагружающейся системы [X]
Шифр
system-recovery
Разделы
freebsd linux tooling
Сверено
2 сентября — в этот день утверждения статьи сверялись с первоисточниками, а не правился текст
Объём
21 049 знаков · 23 раздела · чтения около 20 минут

Восстановление незагружающейся системы


Как пользоваться этой заметкой

Её читают в неудачный момент, поэтому она устроена как навигация, а не как повествование: сначала определяется этап, на котором встала загрузка, дальше — раздел с готовыми командами для этого этапа.

Три правила, которые экономят больше времени, чем любая команда ниже.

Сначала прочитать сообщение целиком. «Не грузится» — это не диагноз; на экране почти всегда есть имя устройства, точки монтирования или юнита, и оно указывает на причину точнее любых догадок.

Ничего не форматировать и не «чинить» до того, как понятен этап. Большая часть безвозвратных потерь при восстановлении наносится самим восстанавливающим — mkfs не на том разделе, xfs_repair -L на живом журнале, переустановка загрузчика на не тот диск.

Начинать с наименее разрушительного средства. Порядок такой: изменить параметры ядра на одну загрузку → загрузиться в аварийный режим → chroot с live-носителя → чинить файловую систему.

Механика загрузчика и снепшотов подробно разобрана в обновлении и управлении загрузкой; здесь — только то, что делают, когда система уже не грузится.

Шаг первый: на каком этапе встали

Что на экране Этап Куда дальше
Логотип прошивки, потом ничего; «No bootable device» Firmware не нашёл загрузчик Загрузчик и EFI
Меню GRUB не появилось, чёрный экран с курсором Загрузчик повреждён Загрузчик и EFI
grub rescue> или grub> Загрузчик жив, не находит конфиг или ядро Загрузчик и EFI
«Kernel panic — not syncing: VFS: Unable to mount root fs» Ядро загрузилось, корень не смонтирован Корень не монтируется
Приглашение (initramfs) или dracut:/# initramfs жив, дальше не идёт Корень не монтируется
«Failed to start …», система висит или уходит в emergency init работает, падает юнит или монтирование Система стартует, но не доходит до логина
Приглашение входа есть, войти не удаётся Загрузка прошла Забыт пароль root
mountroot> (FreeBSD) Ядро не нашло корень FreeBSD

Если ничего не видно вовсе, потому что машина удалённая, — это тот случай, ради которого хостеры дают VNC-консоль или IPMI. Без доступа к консоли восстановление сводится к переустановке, поэтому проверить, что такой доступ есть и вы знаете, как его открыть, стоит заранее, а не сейчас.

Linux

Правка параметров ядра на одну загрузку

Базовый приём, с которого начинается почти всё остальное. В меню GRUB — e на нужной записи, курсор в конец строки linux …, дописать нужное, Ctrl+X для запуска. Изменения не сохраняются: следующая загрузка снова обычная.

Параметр Что делает
rescue (или 1, s, single) однопользовательский режим: корень смонтирован, сеть и большинство сервисов не запущены
emergency минимум: оболочка на консоли, больше ничего не стартует и не монтируется; корень остаётся в том режиме, в каком его оставили ядро и initramfs
systemd.unit=multi-user.target загрузка без графики, когда виноват дисплей-менеджер
init=/bin/bash вместо init запускается оболочка; ни один сервис не стартует
rd.break остановиться в initramfs, до передачи управления системе
systemd.debug_shell оболочка на tty9 параллельно обычной загрузке
nomodeset без загрузки драйвера видеокарты — против чёрного экрана на графике

Разница между rescue и emergency практична: первый монтирует файловые системы из fstab, второй — нет. Если проблема как раз в fstab, нужен emergency.

init=/bin/bash — самый радикальный вариант: не запускается вообще ничего, включая systemd. Корень при этом смонтирован только для чтения, и перед правкой его нужно перемонтировать:

mount -o remount,rw /
# ...правки...
mount -o remount,ro /
exec /sbin/init          # либо reboot -f

Обычным reboot из такого окружения выходить нельзя — некому его обработать; отсюда reboot -f или exec /sbin/init.

Корень не монтируется

Симптом — паника ядра с Unable to mount root fs или приглашение (initramfs). Ядро загрузилось, initramfs работает, а корневая файловая система не найдена или не читается.

В аварийной оболочке initramfs доступен минимальный набор утилит — обычно busybox. Первым делом выяснить, видит ли ядро диски вообще:

cat /proc/partitions
blkid                       # какие UUID видны на самом деле

Дальше три типичные причины.

UUID в параметрах ядра не совпадает с реальным. Случается после клонирования диска, пересоздания файловой системы или замены накопителя. Сверить root=UUID=… из /proc/cmdline с выводом blkid. Лечение — загрузиться с правильным UUID (правкой строки в GRUB), затем поправить /etc/fstab и пересобрать конфигурацию загрузчика.

Нет драйвера контроллера или файловой системы в initramfs. Типично после смены железа, миграции виртуалки на другой гипервизор или неудачной пересборки. Проверяется просмотром содержимого образа:

lsinitrd /boot/initramfs-$(uname -r).img | grep -i virtio

Лечение — пересобрать initramfs из chroot (см. ниже): dracut --force --regenerate-all либо update-initramfs -u -k all.

Файловая система повреждена. Переходить к проверке файловой системы.

Смонтировать корень вручную и посмотреть, что там, можно прямо из initramfs:

mkdir /mnt/root && mount /dev/sda2 /mnt/root && ls /mnt/root

Система стартует, но не доходит до логина

Init работает, значит корень смонтирован и большая часть системы жива. Виноват конкретный юнит или строка fstab.

systemctl --failed                       # что упало
journalctl -b -p err                     # ошибки текущей загрузки
journalctl -b -1 -p err                  # ошибки предыдущей, если сейчас загрузились
systemd-analyze blame                    # что заняло время, если загрузка зависла надолго

Самая частая причина зависания без сообщений — несуществующее устройство в fstab: systemd честно ждёт его появления полторы минуты на каждую запись, а потом уходит в emergency. Лечение и профилактика — в разделе про испорченный fstab.

chroot с live-носителя

Когда починить изнутри не получается, система собирается из live-образа. Порядок монтирования важен: без псевдофайловых систем внутри chroot не будут работать ни grub-install, ни пересборка initramfs.

mount /dev/sda2 /mnt                     # корень
mount /dev/sda1 /mnt/boot                # отдельный /boot, если есть
mount /dev/sda1 /mnt/boot/efi            # ESP на UEFI-машине

for d in dev proc sys run; do mount --rbind /$d /mnt/$d; done
chroot /mnt /bin/bash

--rbind, а не --bind: внутри /dev и /sys смонтированы вложенные файловые системы (devpts, efivarfs), и без рекурсии они не попадут внутрь. Отсутствие efivarfs — самая частая причина, по которой efibootmgr в chroot отвечает «EFI variables are not supported on this system».

В Arch и производных всё это делает arch-chroot /mnt одной командой, включая копирование resolv.conf для работающего DNS.

Если корень на LVM или зашифрован, до монтирования нужно его активировать:

cryptsetup open /dev/sda2 root           # LUKS
vgchange -ay                             # LVM

Выход — exit, затем umount -R /mnt. Рекурсивный размонтаж обязателен, иначе часть файловых систем останется занятой и изменения могут не попасть на диск.

Загрузчик: GRUB и запись EFI

Внутри chroot. На BIOS-машине загрузчик ставится в MBR диска, на UEFI — в раздел ESP:

# BIOS
grub-install /dev/sda

# UEFI
grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=GRUB

Аргумент grub-install на BIOS — диск целиком (/dev/sda), а не раздел. Указание раздела — классическая ошибка, ведущая к неработающей загрузке.

Пересборка конфигурации:

grub-mkconfig -o /boot/grub/grub.cfg     # Arch
grub2-mkconfig -o /boot/grub2/grub.cfg   # Fedora, RHEL, openSUSE
update-grub                              # Debian, Ubuntu (обёртка над тем же)

В Fedora, RHEL и openSUSE утилиты GRUB носят префикс grub2- (grub2-install, grub2-mkconfig), а конфигурация лежит в /boot/grub2/grub.cfg; в chroot об этом легко забыть и пересобрать не тот файл.

Отдельный класс проблем — потерянная запись в NVRAM прошивки. Файлы загрузчика на месте, но firmware о них не знает: обновление прошивки, сброс настроек, замена материнской платы, иногда обновление у хостера.

efibootmgr -v                            # что прошивка знает сейчас
efibootmgr -c -d /dev/sda -p 1 -L "GRUB" -l '\EFI\GRUB\grubx64.efi'
efibootmgr -o 0003,0001                  # порядок загрузки

Запасной путь на случай, когда NVRAM не сохраняет записи (встречается на дешёвых материнских платах и у части хостеров): положить загрузчик по «дефолтному» пути, который прошивка ищет без всяких записей — /EFI/BOOT/BOOTX64.EFI на ESP.

Откат снепшота

Если перед обновлением был снепшот, это самый быстрый и наименее травматичный путь. Инструменты и их настройка разобраны в обновлении и управлении загрузкой; здесь — суть операции.

Btrfs: подтом с рабочей системой заменяется на снепшот. Из live-образа монтируется корень тома целиком (subvolid=5), рабочий подтом отодвигается, снепшот встаёт на его место:

mount -o subvolid=5 /dev/sda2 /mnt
mv /mnt/@ /mnt/@broken
btrfs subvolume snapshot /mnt/@snapshots/42/snapshot /mnt/@

Если стоит grub-btrfs, всё это делается проще: снепшоты видны прямо в меню GRUB, и можно сначала загрузиться в снепшот и убедиться, что он рабочий.

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

lvconvert --merge /dev/vg0/root-snap
reboot

Важное свойство, о котором забывают: слияние уничтожает снепшот. Второй попытки отката не будет.

Повреждённая файловая система

Общее правило: проверять только размонтированную файловую систему. Проверка смонтированной ничего не чинит и может испортить.

ext4. fsck в интерактивном режиме, -y — соглашаться со всем (осмысленно, когда вопросов сотни):

fsck -f /dev/sda2

Если повреждён сам суперблок, fsck не сможет начать. У ext4 есть резервные копии суперблока; их расположение зависит от размера блока и параметров, а узнать его можно, не трогая диск, — mke2fs в режиме предпросмотра:

mke2fs -n /dev/sda2                      # -n НИЧЕГО не пишет, только печатает раскладку
e2fsck -b 32768 /dev/sda2                # использовать резервный суперблок

Флаг -n здесь критичен: без него та же команда создаст новую файловую систему поверх старой.

XFS. fsck для XFS ничего не делает — по замыслу; используется xfs_repair. Ключевая тонкость касается журнала. Если в журнале остались незаписанные изменения, xfs_repair откажется работать и сообщит, что файловую систему нужно смонтировать, чтобы журнал проигрался, затем размонтировать и повторить попытку. Это и есть правильная последовательность:

mount /dev/sda2 /mnt && umount /mnt      # проиграть журнал
xfs_repair /dev/sda2

Флаг -L обнуляет журнал и позволяет починку без проигрывания. Он выбрасывает всё, что в журнале было, то есть теряет данные, и применяется только когда смонтировать не удаётся никак. Совет «просто добавьте -L», который часто встречается на форумах, стоит воспринимать как предложение потерять последние изменения.

Btrfs. btrfs check --repair в документации помечен как опасный и не является первым средством. Прежде него: btrfs scrub для проверки контрольных сумм и mount -o ro,rescue=all — режим для монтирования повреждённого тома в режиме чтения, чтобы вытащить данные. Правильный порядок для Btrfs почти всегда — сначала спасти данные, потом чинить.

FreeBSD

Однопользовательский режим

Пункт 2 в загрузочном меню либо boot -s в приглашении загрузчика. Корень монтируется только для чтения; для правок:

mount -u -o rw /
mount -a -t ufs
zfs mount -a

Первая команда перемонтирует корень в режиме записи, две следующие подключают остальное. Без них правки в /etc не сохранятся.

Приглашение загрузчика

Esc в меню даёт приглашение OK, где можно вмешаться в загрузку до старта ядра:

unload
boot /boot/kernel.old/kernel             # загрузить прежнее ядро
set kern.geom.label.disk_ident.enable=0  # изменить tunable на одну загрузку
lsdev                                     # какие устройства видит загрузчик

Загрузка прежнего ядра — штатный способ пережить неудачную сборку или обновление: installkernel сохраняет предыдущее в /boot/kernel.old.

Отдельный случай — модуль, который ломает загрузку. Отключается в том же приглашении: set module_blacklist=имя перед boot.

Откат boot environment

Самый сильный аргумент FreeBSD с ZFS в этом сценарии. Boot environment — отдельный загрузочный датасет; откат означает загрузиться в предыдущий, а не чинить текущий.

Прямо из загрузочного меню доступен пункт выбора boot environment — то есть откат возможен вообще без работающей системы. Если система запускается хотя бы в однопользовательском режиме:

bectl list
bectl activate -t before-upgrade         # -t: только на следующую загрузку
reboot

Флаг -t делает выбор одноразовым: если и в этом BE что-то не так, следующая загрузка вернётся к прежнему по умолчанию. Механика BE подробнее — в ZFS и обновлении системы.

Импорт пула с live-образа

Когда не грузится ничего, загружаемся с установочного образа и выбираем Live CD. Пул от незагружающейся системы импортируется в отдельный каталог:

zpool import                             # какие пулы видны
zpool import -f -R /mnt zroot            # -R: смонтировать под /mnt, не трогая /
zfs mount zroot/ROOT/default

-R здесь обязателен: без него пул попытается смонтироваться в свои штатные точки и займёт корень live-системы. -f нужен, потому что пул числится импортированным на другой системе — той, которая не загружается.

Откат к снепшоту, если система сломалась после обновления:

zfs rollback -r zroot/ROOT/default@before-upgrade

-r уничтожает более поздние снепшоты — как и слияние LVM, операция без второй попытки.

Частые случаи

Забыт пароль root

Linux, через параметры ядра:

# в строке linux: init=/bin/bash
mount -o remount,rw /
passwd root
touch /.autorelabel                      # если включён SELinux
exec /sbin/init

Строка с .autorelabel не косметическая: без неё система с SELinux после смены пароля может не загрузиться, потому что у нового /etc/shadow окажется неверный контекст.

FreeBSD — однопользовательский режим:

mount -u -o rw /
passwd root

Отсюда следует то, что часто формулируют как принцип, но редко принимают всерьёз: физический доступ к консоли равносилен root. Если это неприемлемо — нужны пароль на GRUB (grub-mkpasswd-pbkdf2 и superusers в конфигурации) и шифрование диска; без второго первое обходится загрузкой с флешки.

Испорчен fstab

Симптом — система уходит в emergency или висит по полторы минуты на запись. Загрузиться нужно в emergency (именно он, а не rescue: rescue попытается смонтировать всё из fstab), затем:

mount -o remount,rw /
vi /etc/fstab
systemctl daemon-reload

Профилактика стоит одного слова в опциях монтирования. nofail означает «не считать ошибкой, если устройства нет», а x-systemd.device-timeout= сокращает ожидание:

UUID=...  /mnt/data  ext4  defaults,nofail,x-systemd.device-timeout=10s  0 2

Для всего, что не является корнем — внешних дисков, сетевых шар, дополнительных томов — это правильное умолчание. Отсутствующая флешка не должна мешать серверу загружаться.

Переполнен /boot

Отдельный раздел /boot на несколько сотен мегабайт заполняется старыми ядрами, после чего обновление падает на середине, а иногда система остаётся без рабочего initramfs.

uname -r                                 # текущее ядро — его не трогать
dpkg -l 'linux-image-*' | grep ^ii       # Debian/Ubuntu
apt purge linux-image-5.15.0-56-generic
dnf remove --oldinstallonly              # Fedora/RHEL
pacman -Qe | grep linux                  # Arch

Чистить нужно пакетным менеджером, а не rm в /boot: удалённые вручную файлы оставят пакет в системе, и следующая пересборка initramfs снова попытается их создать.

Профилактика — ограничить число хранимых ядер: installonly_limit в dnf.conf, unattended-upgrade с автоочисткой либо соответствующий хук в Arch.

Недоступен ключ LUKS

Симптом: запрос пароля не появляется вовсе, либо система сообщает, что не может открыть устройство.

Если ключевой файл лежал в initramfs, а initramfs пересобран без него, — открывать придётся паролем, а crypttab и образ пересобрать. Проверить, какие ключевые слоты вообще заняты:

cryptsetup luksDump /dev/sda2

Если пароль забыт, а слоты пусты — данные потеряны, и это не преодолевается. Единственный работающий ответ на этот случай даётся заранее: заголовок LUKS сохраняется отдельно, до аварии.

cryptsetup luksHeaderBackup /dev/sda2 --header-backup-file luks-header.img

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

Профилактика

Всё, что реально сокращает время простоя, делается заранее.

Live-носитель и доступ к консоли. Флешка с образом системы — для физической машины; проверенный доступ к VNC или IPMI — для удалённой. «Проверенный» означает, что вы им хоть раз пользовались: узнавать, что консоль хостера требует Java-апплета или отдельного пароля, лучше не в момент аварии.

Снепшот или boot environment перед каждым обновлением. На FreeBSD с ZFS freebsd-update делает это сам; на Linux с Btrfs или LVM — либо snapper, либо привычка. Откат из снепшота занимает минуты против часов на восстановление.

Ограничение числа ядер и nofail в fstab. Две настройки, закрывающие два самых частых случая из предыдущего раздела.

Резервная копия заголовка LUKS и пароля от репозитория бэкапов. Оба — вне восстанавливаемой машины.

Проверенные бэкапы. Восстановление незагружающейся системы имеет смысл, пока оно дешевле развёртывания заново из бэкапа. Если бэкап есть и проверен, у всей этой заметки появляется граница: в какой-то момент правильный ответ — не чинить, а развернуть. Как убедиться, что бэкап рабочий, — в резервном копировании.

Записывать, что сделали. При восстановлении делается десяток шагов, и через полгода при повторе не вспомнится ни один. Заметка на пять строк после успешного восстановления окупается.

Источники

  • kernel-command-line(7) — systemd.unit=, rescue, emergency, systemd.debug_shell
  • dracut.cmdline(7) — rd.break и его точки останова, rd.shell, rd.debug
  • e2fsck(8) — -b и поиск резервных суперблоков через mke2fs -n
  • xfs_repair(8) — требование проиграть журнал монтированием и цена флага -L
  • efibootmgr — управление записями в NVRAM
  • bectl(8) — activate -t и одноразовый выбор boot environment
  • zpool-import(8) — -f и -R при импорте чужого пула
  • FreeBSD Handbook: The Boot Process — приглашение загрузчика и однопользовательский режим

Связанные заметки

Из библиотеки института уносят только знания. Читателей здесь не считают: посещение не регистрируется, отметок в издании не остаётся, счётчиков нет ни у одного издания фонда.

Издание пополняется и пересверяется. Текст доступен на условиях CC BY-SA 4.0 — унесённое можно передавать дальше на тех же условиях.