凌晨一点,群里消息开始炸。一台云服务器升级内核后重启,直接卡死在 dracut shell 里——屏幕就那么两行白字:“dracut-initqueue timeout”跟“Unable to find root device”。SRE 同事第一反应是“再等等看”,但风扇声都停了,系统依然纹丝不动,敲什么键都没反应。

你绝对不是第一个在这上面栽跟头的人。dracut 报“Don't know how to handle 'root='”的时候,十有八九不是硬盘挂了,而是 initramfs 里缺了能认出那块盘的驱动。内核已经加载完了,却在挂载根分区前被拦在了门外。

常见的场景是:升级到 6.8 这样的新内核后,原来跑在 NVMe 上的 /dev/nvme0n1p2 突然变成了“未知设备”。或者你从普通云盘换到了 NVMe SSD,但 initramfs 还是老掉牙的 SCSI 驱动,根本不认新盘。还有一种情况,从旧镜像恢复到新环境,LVM 卷组死活激活不了。这些都会让 dracut 在初始化队列里干等,一直等到超时。我见过有人因为这个问题重装了三次系统,结果只是少打了两个字母。

那个 dracut 提示符其实有话说

好,现在你面前就摆着那个 dracut:/# 提示符了。别慌,也别直接去翻 grub.cfg——先搞清楚系统到底在找什么。lsblklspci 先来一轮。dracut shell 里能用的命令比你想的多。先跑 lsblk,看看系统能不能认出你的磁盘。如果什么都看不到,或者只看到一个 ramdisk 设备,那问题就清楚了:内核模块包里没有对应控制器的驱动。

然后 lspci -nn | grep -iE "nvme|scsi|sata",确认你的磁盘控制器是什么型号。比如 NVMe Controller [0108] 就是标准的 NVMe 设备。这是硬件层面的证据——内核得先有驱动能跟这个 PCI ID 握手,才能往下走。

接下来看 /proc/cmdline。这才是真正的关键。大部分人卡在这里是因为只盯着报错看,却没去确认内核启动时到底传了什么参数给 dracut。

cat /proc/cmdline
# 输出类似:
BOOT_IMAGE=/vmlinuz-6.8.0 root=/dev/nvme0n1p2 ro console=tty0 ...

如果你的 root= 指向的是 /dev/nvme0n1p2,那意味着 initramfs 必须包含 nvmenvme_core 这两个模块。如果指向 /dev/sda2(SCSI 设备),那就是 sd_modlibata 那一套。如果是 root=LABEL=xxxUUID=xxx,还需要确保 ext4xfs 文件系统驱动也在里面。还有一种特别坑的情况:root=/dev/mapper/VolGroup-lv_root。这是 LVM 设备,需要 dm_moddm_mirrordm_log 外加 lvm2 用户态工具全在 initramfs 里。少一个,LVM 卷组就激活不了,dracut 只能干等超时。

最后用 dmesg | grep -i "nvme\|scsi\|lvm\|dm" 确认驱动加载到哪里断掉了。常见的信息是 或者 ——都是模块依赖链没接上。

linux lsblk lspci command output

临时救急:手动加载模块,先把根分区挂起来

看到 dracut:/# 这行提示符,先别急着乱敲。此刻你和真正的根分区之间,往往就隔着一个还没加载的驱动,比如 nvme 或 dm-mod。

最直接的办法就是用 modprobe 把它拽进来。常见组合是:

如果 root= 指向 LVM,那还得把 lvm 相关也拉上:

加载完立刻验证设备是否现身:

ls /dev/nvme*
lvs

一旦看到熟悉的盘符或卷名,就可以动手挂载了:

顺手跑个 blkid 确认 UUID 也没坏处,省得下次重启又对着 root=UUID=... 发呆。若一切顺利,输入 kexec -e 就能跳出这个临时小 shell,回到真正的系统登录界面。要是嫌麻烦,exit 也能直接硬着陆——前提是你已经确定模块确实加载成功,否则只会再次掉回 initramfs 救援循环。

治本:重建 initramfs,让修复永久生效

手动加载模块修好了临时能进系统,但重启一次就又回到 dracut shell。治标不治本的事,谁都不想干两次。真正的解法是重建 initramfs,把缺失的驱动永久塞进去。

进系统第一件事,先确认当前内核版本:

uname -r

我遇到过好几次这种情况:系统里装了三个内核,你刚才用 modprobe 加载成功的那个驱动其实是旧内核模块树的,而 dracut 默认只会为当前运行的内核生成 initramfs。所以 uname -r 必须跟 grub 引导的内核一致,否则白干。确认好版本后,直接上 dracut 的强制注入命令:

dracut -f --force-drivers "nvme dm-mod"

-f 是强制覆盖已有的 initramfs 文件,不加它 dracut 会提示目标已存在然后跳过。--force-drivers 才是关键——它告诉 dracut:就算你觉得这模块不需要,也给我打包进去。这比 --add-drivers 更暴力,也更可靠。如果你的根分区在 LVM 或软件 RAID 上,还要额外加模块组:

dracut -f --force-drivers "nvme dm-mod" --add "lvm2 mdraid"

注意 --add 后面引号里的名字是 dracut 模块名,不是内核模块名。lvm2 对应的是 dracut 里那一整套 LVM 激活逻辑,包括 lvm_scandm-modlvm2 用户态工具链。只加 dm-mod 不添 lvm2,卷组还是激活不了。有时候 dracut 会自作聪明地裁剪依赖链,比如 nvme.ko 还依赖 nvme_corenvme_common,光指定 nvme 可能不够。保险做法是全部列出:--force-drivers "nvme nvme_core nvme_common"

重建完必须验证:

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

能看到 /usr/lib/modules/.../kernel/drivers/nvme/... 这样的路径,说明驱动已经打包进去了。要是输出空,那就是 --force-drivers 没生效——检查下内核模块本身是不是没装,比如 find /lib/modules/$(uname -r) -name nvme.ko* 先确认模块文件存在。

重建完重启之前,顺手检查下 grub 配置:

grubby --info=ALL | grep initrd

确保新生成的 initramfs 文件路径确实被 grub 引用了。有一次我在阿里云上折腾半天,所有步骤都对,结果 grub 还指着旧内核的 initramfs——因为系统里跑的是 grubby 而不是 update-grub。最后一步:重启。看着系统从 grub 菜单一路跑到 login 提示符,那种感觉比看日志里一行 [ OK ] 踏实得多。

linux modprobe load kernel module

内核升级后的那点事

这次修完,下次呢?dracut 这玩意儿平时没人管,但一碰上内核小版本升级——比如从 kernel-5.15 跳到 kernel-6.8——就容易翻车。新内核启用了 CONFIG_MD_RAID*,但 dracut 默认裁剪策略没跟上,initramfs 里直接缺了对应驱动。

内核更新完忘了重建 initramfs?别急着重装系统。掉进 dracut shell 之后,第一件事不是慌——先看看 /dev 和 /sys 挂没挂上。没挂的话手动 mount -t sysfs sysfs /sys;挂好之后 lsmod | grep 你缺的驱动名,如果没加载就 modprobe 硬塞进去。确认模块都在了,再跑 dracut -f /boot/initramfs-$(uname -r).img $(uname -r),重启动之前记得 reboot 前看一眼 grub.cfg 里内核参数对不对。这一套下来,比翻半年前写的排查笔记靠谱多了。

dracut -f --kver=all

这条会让 dracut 为当前系统里所有已安装的内核版本重新生成 initramfs。别嫌慢,它比等到下回启动时在 rescue shell 里敲命令省心多了。光靠脑子记不靠谱。把常用驱动写进持久化配置:

echo 'force_drivers+=" nvme dm-mod "' > /etc/dracut.conf.d/force-drivers.conf

这样以后不管 yum update 还是 apt upgrade,只要 dracut 自动跑一遍,NVMe 和 device-mapper 模块都会被打包进去。如果你在用 RAID 或 LUKS,把对应的 rd.mdrd.luks 也加上。

云服务器上动内核,第一件事永远是打快照。之前有哥们在 grub 里默认指向新内核,initramfs 重建的时候漏了个模块,重启直接失联——这种翻车现场我见过不止一次。旧内核条目别删,留着它当救生圈,老内核启动选项还在心里就踏实。内核升级这东西吧,一次跑通纯属运气好,养成把配置固化、顺手重建 initramfs 的习惯,比出事了再翻文档省心太多。机器不会跟你耍心眼,但你也不能给它机会让你掉坑里。