内核升级完,重启,屏幕卡在 dracut-initqueue timeout。你敲ip a,或者ifconfig——回车声还在,网卡没了。再试touch,它告诉你文件系统只读。好家伙。

CentOS 9.2(内核5.14.0-284.el9)和Anolis OS 8.6(兼容RHEL 8.6)上都遇到过一模一样的破事:yum update选了新内核,重启就“断网+只读”。不是磁盘挂了,是initramfs没把云平台的网络驱动——常见的 vlan、bond、bridge、macvlan 这些模块——打包进去。你看到的就是ip aifconfig空手而归,network.service 报“Failed to bring up daemon”。显示 root=/dev/nvme0n1p3 之类,但早期网络就是不起来。mount | grep /一看,根分区多半是ro,连 /etc/resolv.conf 都改不了。dmesg | tail -50翻出来的关键词:dracut-initqueue timeout、unable to find root device、module not found。

别急着骂云厂商。更常见的路径是:旧内核还在跑的时候,你做了dracut -f重建initramfs,它按当前硬件探测结果生成清单,恰好没把网卡驱动列进去。随后重启到新内核,initramfs还是那套,网络驱动缺席,服务起不来。只读多半是安全策略和只读根分区叠加的结果,systemd把/usr、/var以readonly方式挂载,救援前什么都改不了。

先确认两件事:当前内核版本跟 initramfs 是不是一对儿,别糊里糊涂就往上怼。万一网络驱动挂掉、文件系统变只读,第一反应是进 dracut shell 临时顶住,再用 chroot 从根上修回来。说实话,这套手法比市面上那些“一键修复”靠谱太多了——至少不会修完更崩。

困在 dracut shell 里的时候

重启那一刻,屏幕卡在“dracut-initqueue timeout”,或者直接丢给你一个dracut:/#提示符。这个shell不像熟悉的bash,没man页,没补全,连systemctl都不存在。但它能做的,是让你手动把启动失败的那一步补上。

先稳住。在dracut:/#下,敲ls /dev看磁盘设备是否被内核识别。多数情况下,nvme0n1、sda这些还在。接着blkid确认分区UUID和文件系统类型——输出全空的话,问题就更底层了,可能是磁盘控制器驱动也没打进initramfs。好在云服务器通常是virtio或NVMe驱动,内核自带,很少翻车。

如果你用LVM(很多云主机默认分区就是LVM),这一步很关键:先激活卷组。不激活的话,lvs直接报“设备不存在”。激活后lvs能看到逻辑卷列表,确认root、swap、home都在。

挂载根分区到/sysroot:

mount /dev/mapper/rootvg-root /sysroot
mount /dev/nvme0n1p1 /sysroot/boot
mount --bind /dev /sysroot/dev
mount --bind /proc /sysroot/proc
mount --bind /sys /sysroot/sys

然后,才算真正回到自己的系统。dracut shell里没有network命令,没有yum,什么都干不了;进chroot后,那些工具才回来。注意,根分区挂载时报“只读”,可以用强制改读写。还报错的话,八成是文件系统有坏块或底层存储链路异常——建议立刻检查云控制台的监控面板。

头几次进dracut shell时我整个人是懵的。没有vim,没有history,连ctrl+r反向搜索都不支持。但只要记住三个命令:blkid看磁盘、激活LVM、mount挂根分区,就能从那个荒凉的提示符里爬回系统。一旦进了chroot,主动权就回到你手里。

dracut shell command line rescue

挂根文件系统,chroot 进去再说

到了这一步,才算从荒凉的dracut提示符爬回可用的系统。先把根分区挂到/mnt/sysimage,别急着激动,检查一下block id对不对:blkid | grep -i root。如果是LVM,确认已经把卷组激活,否则/dev/mapper/rootvg-root根本不会露面。

chroot之后,最直观的变化就是能敲yum了,也能看日志、改配置。真正的分水岭是namespace——chroot会把根目录切换到/mnt/sysimage,后续所有路径都按原系统来,而不是rescue环境的那套目录树。你终于可以像在服务器本机一样干活。

不少同事以为就完事,结果发现lsmod空空如也,ifconfig找不到设备。原因很简单:/dev、/proc、/sys这些虚拟文件系统没带过去。用三条mount --bind把它们一一系到目标根下,chroot才认得出网卡、模块和挂载点。

如果遇到只读报错,先试试;还不行就得怀疑文件系统本身或底层存储链路,那是另一个坑了。

一切就绪,执行,你会看到shell提示符里的路径瞬间变短。此刻起,每次敲击命令都作用在待修复的系统上,下一步就可以着手重建initramfs,让缺失的网络驱动回到正确的位置。

mount root filesystem chroot rescue

重建 initramfs,手塞网络驱动

chroot进去之后别急着嗨,先看一眼当前跑的内核版本。敲uname -r——如果跟问题机器启动时用的内核一致,那直接重建就行。不一致的话,老实说,你得先搞清楚目标内核是哪个,比如升级完没重启前那个旧内核还挂在/boot里,但系统已经拿新内核引导了,这种错位能把人绕晕。

重建命令其实就一行:

dracut -f /boot/initramfs-$(uname -r).img $(uname -r)

-f是强制覆盖,不加它遇到同名文件就报错退出。后半段指定内核版本号,保证了initramfs跟内核模块路径完全对上。你要是手贱改了/lib/modules下的软链接,那就等着module layout mismatch吧,我吃过这亏。

但事情往往没那么简单。内核升级后网络驱动丢了,根因是新的initramfs在生成时没扫到新内核的网卡模块——比如e1000virtio_netr8169这些。这时候要手动往里塞:

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

--add-drivers后面引号里列模块名,空格分隔。别问为什么不用--force-drivers,那参数意思是“强制加载”而不是“添加”,两者差一个字母,行为天差地别。我曾在Anolis OS 8上试过--force-drivers virtio_net,结果dracut直接把模块嵌进initramfs的early cpio里,反倒跟内核module路径冲突,开机直接panic。

重建完后用lsinitrd /boot/initramfs-$(uname -r).img | grep virtio_net确认模块确实在里边。然后更新引导配置——这一步常被忘,尤其用UEFI的机器,grub.cfg路径可能不同。最后reboot,心跳到了嗓子眼。能ping通就喝酒,不行就从dracut shell再来一轮。

机器终于从dracut shell里爬出来,屏幕上不再是“dracut-initqueue timeout”,而是熟悉的登录提示。别急着欢呼——网络和文件系统这两座大山还在等着。

dracut rebuild initramfs command

网络恢复和文件系统验证

很多时候升级完内核,网卡驱动是加载了,但接口没被激活。直接在控制台敲nmcli d。如果报unknown device,说明initramfs还是漏了驱动,得退回去再跑一遍dracut --add-drivers "e1000 virtio_net" -f。要是看到STATE=UP,赶紧拿临时IP续命。或者干脆ifup eth0,前提是你记得NetworkManager并不总靠谱——CentOS 7的ifup脚本会顺手把dnsmasq也拉起来,Anolis OS则更偏向nmcli。

显示active (exited)并不代表真的通,ping一下网关最实在。遇到cloud-init乱改配置导致死活起不来的情况,可以mount -o remount,rw / && systemctl mask cloud-init,先让它闭嘴。

/var/log/messages里藏着真相。grep -iE 'error|fail|timeout' /var/log/messages就能筛出那些丢包、丢驱动的痕迹。我见过r8169模块因为firmware版本不对直接kernel panic,也见过virtio_net因为pci id变化而被忽略。别忘了journalctl -b -k | grep -i driver,看看启动日志有没有抱怨module version mismatch。

最后mount | grep ' / ',确认根分区确实可写。能删能改,才算真正回到生产环境。

预防:升级之前,备份和检查

聊到这儿,最想说的其实不是怎么修,而是怎么别让自己半夜爬起来对着黑屏骂娘。线上环境搞一回内核升级,那真是刻骨铭心——你永远不会忘那种盯着dracut shell光标闪、网络驱动失联的浑身冷汗。

升级前,先把当前的initramfs扔到备份目录里。就一行:cp /boot/initramfs-$(uname -r).img /backup/。别嫌麻烦。等你在dracut shell里对着黑屏发呆的时候,就会觉得这步操作值回票价了。

然后跑一下lsinitrd /boot/initramfs-$(uname -r).img,看看当前initramfs里到底塞了哪些驱动。特别留意virtio_netnvmeata_piix这些存储和网络相关的模块。缺了就记下来,重建的时候补上。

我见过最坑的一次是同事直接在生产环境跑了yum update -y && reboot然后reboot,重启后网卡驱动没了,文件系统只读,连回滚都费劲。那之后团队定了个规矩:先在测试机跑一遍内核升级,lsinitrd | grep virtio确认模块齐全,再考虑上生产。

备份、检查、测试,这三步花不了十分钟,但能省掉一整晚的焦虑。下次升级前,记得先拿小本本记好。