Linux运维8 min read次阅读

机器起不来了:GRUB 与启动故障的救援流程

服务器重启之后不起来了。屏幕上要么是 grub rescue>,要么是刷一堆 dracut 报错后掉进 emergency shell。这种时候最忌讳的就是乱试命令——搞清楚卡在启动的哪一步,问题就解决一半了。

启动的四个阶段

定位的第一步是判断停在哪:

  1. 固件/硬件 — 加电、POST、找启动设备。表现为黑屏、找不到启动盘、RAID 卡报错。
  2. GRUB(引导加载器) — 加载内核和 initramfs。表现为 grub>grub rescue> 提示符。
  3. 内核 + initramfs — 内核启动,initramfs 里加载驱动、挂载根文件系统。表现为卡在 dracut、找不到根设备、掉进 emergency / initramfs shell。
  4. 用户态(systemd) — 挂载 fstab、起服务。表现为能进 emergency 模式、某个服务卡住、文件系统只读。

每一阶段的现象和修法完全不同,先对号入座。

阶段二:GRUB 挂了

grub rescue>

长这样:

error: unknown filesystem
grub rescue>

通常是因为 GRUB 找不到它的配置文件或模块目录(/boot/grub2 丢了、分区号变了、或者磁盘顺序变了)。

先摸清有哪些盘和分区:

ls                          # 列出类似 (hd0) (hd0,msdos1) 的设备
ls (hd0,msdos1)/            # 逐个试,找哪个里面有 boot/ 或 grub2/

找到 /boot 所在分区后,手动引导:

set root=(hd0,msdos1)
set prefix=(hd0,msdos1)/grub2        # 注意路径,有的系统是 /boot/grub2
insmod normal
normal

如果能进系统了,进去之后必须重装 GRUB,否则下次重启还是一样:

# BIOS 引导
grub2-install /dev/sda
grub2-mkconfig -o /boot/grub2/grub.cfg

# UEFI 引导(EFI 分区挂在 /boot/efi)
grub2-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=centos
grub2-mkconfig -o /boot/efi/EFI/centos/grub.cfg

重装前确认 grub2-install 的目标是整块盘(/dev/sda)不是分区(/dev/sda1。写错位置可能把分区表搞坏。

用救援环境重装

如果手动引导也进不去,用安装盘/ISO 进救援模式(Troubleshooting → Rescue a CentOS system),它会自动把原系统挂到 /mnt/sysimage

chroot /mnt/sysimage
grub2-install /dev/sda
grub2-mkconfig -o /boot/grub2/grub.cfg
exit
reboot

chroot 是关键——不切进去的话,grub2-install 会用救援环境的内核和模块,装出来的东西对不上。

阶段三:内核与 initramfs

卡在 dracut,找不到根设备

典型报错:

dracut: Warning: /dev/mapper/centos-root does not exist

几种常见原因:

1. initramfs 里缺存储驱动

典型场景:换了 RAID 卡、或者在 A 机器上做的系统盘插到 B 机器上,initramfs 里没有新机器的磁盘驱动。

修法:进救援环境,chroot 之后重建 initramfs:

chroot /mnt/sysimage
# 先备份
cp /boot/initramfs-$(uname -r).img /root/initramfs.bak
dracut -f /boot/initramfs-$(uname -r).img $(uname -r)

如果是为了加特定驱动(比如某 RAID 卡):

dracut --add-drivers "megaraid_sas" -f /boot/initramfs-$(uname -r).img $(uname -r)

2. 根设备路径变了

GRUB 配置里写的是 root=/dev/sda2,但磁盘顺序变了,sda 变成了 sdb。这种情况改用 UUID 最稳:

blkid                       # 看根分区的 UUID

然后编辑 /etc/default/grub

GRUB_CMDLINE_LINUX="... root=UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx ..."

grub2-mkconfig -o /boot/grub2/grub.cfg用 UUID 而不是设备名,是避免这类问题的根本办法。

临时救急也可以在 GRUB 启动界面按 e 编辑内核参数,改完 Ctrl+X 启动,但那是临时的,进去后要改配置文件。

3. LVM 卷没激活

# 在 initramfs/emergency shell 里
lvm vgscan
lvm vgchange -ay          # 激活所有卷组
exit                      # 继续启动

阶段四:用户态

fstab 写错

能进系统但掉进 emergency mode,屏幕上提示输入 root 密码。最常见的原因是 /etc/fstab 里有一项挂载失败(UUID 写错、设备不存在、NFS 服务器连不上)。

# 输入 root 密码后
mount -o remount,rw /        # 根分区可能是只读的,先改成可写
vi /etc/fstab
# 把出错的那行注释掉或修正
mount -a                     # 测试能不能全部挂载成功

关键经验:给非关键的挂载项加 nofail,这样它挂不上也不会阻断启动:

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

NFS/CIFS 这类网络挂载尤其要加,否则网络不通就起不来。x-systemd.device-timeout 限制等待时间,避免卡 90 秒的默认超时。

文件系统损坏

# 在 emergency 或救援环境下,先卸载再修
umount /dev/sdb1
fsck -y /dev/sdb1

# XFS 用 xfs_repair(xfs 不能 fsck)
xfs_repair /dev/sdb1
# 损坏严重时先 -L 清空日志(会丢最后一点数据,不得已才用)
xfs_repair -L /dev/sdb1

fsck 一定要在卸载状态下跑,对已挂载的文件系统做检查会把数据搞得更糟。XFS 的 -L 会丢弃日志,可能丢数据,用之前先确认有没有别的办法(比如从备份恢复)。

某个服务卡住启动

systemctl list-jobs              # 看卡在哪个 job
systemctl status <服务名>
journalctl -xb                   # 看本次启动的日志
systemctl disable <问题服务>      # 先禁掉,让系统起来再说
systemctl reset-failed

也可以临时用内核参数跳过:systemd.unit=rescue.targetemergency,或者 systemd.mask=xxx.service

诊断信息的获取

进了 emergency 之后,最有用的几条:

journalctl -xb                   # 本次启动日志,-x 带解释,-b 本次
journalctl -k                    # 只看内核日志(等同 dmesg)
systemctl --failed               # 失败的单元
dmesg | grep -i error            # 硬件/驱动层错误

如果系统完全起不来、日志也看不到,就得靠安装盘的救援模式,进去之后日志在 /mnt/sysimage/var/log/ 下。

防患于未然

几件成本很低但关键的事:

  • fstab 用 UUID,别用 /dev/sdX,并给非关键项加 nofail
  • 改 fstab 之后必跑 mount -a 验证,确认没问题再重启
  • 动 GRUB 配置前备份 grub.cfg;改完用 grub2-mkconfig 而不是手改 grub.cfg(后者会被覆盖)
  • 重大变更(换 RAID 卡、迁移硬盘、改分区)之后,先在救援环境验证能启动,别等到真需要重启时才发现
  • 保留一份可启动的 ISO/救援盘,云主机要确认有没有 VNC/串行控制台可用——没有控制台的话,grub rescue 基本只能靠重装

最后这条是血的教训:云主机如果连不上控制台,很多启动故障你是没法救的,只能重装。所以备份比救援技术重要

分享:

评论区