VNC 连不上云服务器,十次里七八次跟网络没半毛钱关系。你对着黑屏的 VNC 窗口狂点刷新、重启实例、换浏览器,折腾一圈下来,还是卡在登录界面之前——这时候就该换个思路了:内核启动阶段就已经翻车了,VNC 只是个报警器,真正的问题是内核崩溃,或者 initramfs 炸了。
在 Anolis OS 8 上就踩过一次,yum update 之后重启,VNC 直接黑了。串口日志最后一行是 Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)。云厂商控制台里的串口日志,那东西平时没人看,真出事它就是救命稻草。拿它定位到是 initramfs 里缺了磁盘驱动,然后通过 dracut 手动进急救 shell 把驱动塞进去,才把机器拉回来。
VNC黑屏背后:内核启动失败才是真凶
VNC 无响应,最常见的情况是 grub 选完内核之后就没下文了。内核在加载 initramfs 时如果找不到根文件系统需要的块设备驱动——比如 NVMe 或者 virtio 的驱动没被打进 initramfs 里——就直接 panic。这时候 VNC 画面冻住,键盘没反应,只能靠串口日志看到最后几行报错。
而 dracut 这个工具,说是生成 initramfs 的低级工具,其实它更像个急救包。它在 RHEL/CentOS/Fedora/Anolis OS 这些发行版里是标配。启动时如果内核崩溃,dracut 会尝试 fallback 到一个 shell 环境——就是那个 dracut:/# 提示符。进去之后你能手动加载内核模块、检查磁盘分区、甚至重建 initramfs。前提是你得知道怎么触发这个 shell:要么在 grub 启动项里加 rd.shell 参数,要么让内核干脆起不来,它自动掉进去。
手头那台云服务器还正常的时候,建议先把串口日志输出配好——大部分云厂商的控制台都有“远程连接 > 串口终端”这类入口。等哪天 VNC 突然黑屏,你至少能看见内核到底死在哪一行,而不是对着黑框干瞪眼。
很多人第一次面对 VNC 黑屏时,会下意识去ping、重启、重装浏览器插件——折腾半天才发现,问题根本不在网络层。内核在启动中途挂了,画面当然静止不动。这时候唯一能告诉你“死在哪里”的,只有串口日志。它不像 VNC 那么花哨,却像心电图一样忠实记录每一次跳动和骤停。
先把日志拿到手:别靠猜,看原文
阿里云叫“远程连接”,腾讯云叫“登录实例控制台”,入口名字不同,本质都是同一个 Web Terminal。进去之后默认看到的是 serial console output buffer——也就是串口输出缓冲区。别被术语吓到,它只是一屏纯文本。你要找的是最后几行,因为内核 panic 之前那一刻的信息最值钱。如果屏幕里出现 dracut: FATAL: No or empty root= argument 这种句子,恭喜你,问题已经缩小到一个参数缺失上。
三行定生死:kernel panic、dracut warning、failed to mount
Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0) 这串字符我背得出来。它意味着内核自己承认:“我找不到根分区。”常见原因有两个:要么 initramfs 里没编译对应存储驱动,要么 bootloader 传给内核的 root=UUID=... 那一行写错了。前者得重建镜像,后者只需改 grub.cfg。再看 dracut Warning: '/dev/disk/cloud/xxx' does not exist,这是提醒你设备节点根本没生成。多半是 udev 没跑完就崩了,或者 cloud-init 的 disk-setup 脚本半途而废。至于 Failed to mount /sysroot: No such device,基本可以断定 initramfs 阶段认不到磁盘。
一个真实案例:root= 被谁吃了?
dracut: FATAL: No or empty root= argument
dracut: ^ syntax error in command line
dracut: Entering emergency mode. Exit the shell to continue.
sh-5.1# cat /proc/cmdline
BOOT_IMAGE=/vmlinuz-5.14.0 root= ro quiet crashkernel=auto
这段输出来自一台 Anolis OS 8.5 的测试机。运维同事为了“安静启动”加了 quiet,顺手把 root= 后面的 UUID 删了个空格,结果整个系统停在 dracut shell。我在串口日志里一眼锁定那行 FATAL 报错,进 shell 后先确认 /proc/cmdline,发现确实缺了完整路径。补上正确的 root=UUID=xxxxx 后重启,VNC 顺利亮起登录界面。整个过程不到十分钟,前提是你能读懂那几行红字。
dracut shell实战:从崩溃到修复
好,现在你手里已经有了串口日志里的那行致命错误,也知道该往哪个方向查。接下来就是真刀真枪跟 dracut shell 打交道的时候了。说实话,我第一次见到那个 sh-5.1# 提示符的时候,心里是发怵的——黑底白字,文件系统没挂,连 ls 都只能列出一堆 tmpfs 的空目录。但你一旦熟悉了它,就会发现这其实是系统留给你的最后一道门。
怎么进去?最常见的两种方式。第一是在 GRUB 菜单出现时按 e 进入编辑模式,找到以 linux 开头的那一行,在末尾加上 rd.shell。如果加的是 rd.break,那系统会在 initramfs 刚加载完、还没切换到根文件系统之前停住,直接给你一个 shell。我习惯用 rd.shell,因为它是在启动失败后才弹出提示,能保留更完整的上下文——比如你还能看到是挂载哪个设备时挂掉的。
进去之后先别慌。第一件事永远是 lsblk,看磁盘设备在不在。如果连 /dev/vda 或 /dev/sda 都看不到,那问题不在 dracut 层面,是内核没识别到磁盘控制器——你得检查 virtio 驱动或者 AWS ENA 驱动有没有编进去。如果能看到磁盘,但 lsblk -f 不显示文件系统类型,那大概率是 root= 参数给的 UUID 不对,或者分区表被搞乱了。
确认设备存在之后,手动挂载试试:
mount /dev/vda1 /sysroot
mount | grep sysroot # 确认挂载成功
如果这一步报错,说明文件系统驱动没在 initramfs 里——比如你用的 XFS 但镜像只编了 ext4。这时候就得 chroot 进去,运行 dracut --force --kver $(uname -r) 重新生成 initramfs,把缺失的模块补上。注意,不 chroot 的话 dracut 命令会找不到内核模块路径。
挂载成功之后,chroot 进去,然后:
vim /etc/default/grub # 检查 GRUB_CMDLINE_LINUX 里的 root= 参数
grub2-mkconfig -o /boot/grub2/grub.cfg # RHEL/CentOS 系
exit
reboot
这一套流程我跑过不下二十次。最坑的一次是挂载成功、chroot 进去、重新生成了 grub.cfg,结果重启还是停在 dracut shell。后来发现 /boot 是独立分区,我在 chroot 之后直接运行 grub2-mkconfig,它把生成的文件写到了 /boot/grub2/grub.cfg,但实际引导加载器读的是 /boot 分区下的那个 /grub2/grub.cfg——路径对不上,根本没用。所以 chroot 之后第一件事,先把引导分区也挂上,再生成配置。
最后一句提醒:如果你在云控制台重启之前改了 GRUB 参数,记得把 rd.shell 删掉,否则下次正常启动它也会弹 shell 等你,然后因为超时自动进紧急模式——自己挖的坑自己填。
GRUB引导修复:让系统重新站起来
有时候内核能加载,但根文件系统挂不上,VNC就停在那行提示。别急,先在 dracut shell 里把 root= 这件事说清楚。当内核认不出根设备时,先把 root= 指对路。lsblk -f 看一眼 UUID,确认 /dev/vda1 或 /dev/sda1 的文件系统类型。然后手动挂载试试:mount /dev/vda1 /sysroot,如果报错多半是驱动缺失或 UUID 不对。此时直接在 GRUB 命令行补一刀:linux16 /vmlinuz-$(uname -r) root=UUID=$(blkid -s UUID -o value /dev/vda1) ro quiet crashkernel=auto,rd.driver.pre=virtio_net virtio_blk virtio_pci 也加上,省得 initramfs 再去猜。
万一你之前改过磁盘或分区,/etc/default/grub 里的 GRUB_CMDLINE_LINUX 也得同步,否则每次自动生成都会把你精心调过的参数冲掉。
重建 GRUB 配置的正确姿势
chroot /sysroot 之后,先确保 /boot 分区已经挂好,再用 grub2-mkconfig -o /boot/grub2/grub.cfg 生成主配置。遇到独立 /boot 的情况,最容易犯的错就是生成到错误的路径,导致重启依旧进 dracut。检查办法很简单:cat /proc/cmdline 看当前生效参数,grep root= /boot/grub2/grub.cfg 核对是否一致。
如果你用的是 Anolis OS,grub2-mkconfig 的行为和 RHEL 系差不多,但记得确认 /usr/lib/os-release 里的 ID 别被误判成别的发行版。
内核升级后的救命稻草:dracut -f
有一次我升级完 kernel-core,没跑 dracut -f,结果 initramfs 还是旧的,启动直接报 “Unknown command line”。解决办法很粗暴:dracut --force --kver $(uname -r),强制重做一遍 initramfs,再把缺失的 XFS/Btrfs 驱动塞进去。做完这步别忘了 exit、reboot,顺手把云控制台串口日志打开,下次再翻车就能看到更早的打印。
这类问题最怕的就是看似修好了,其实只是运气好。
预防胜于治疗:给云服务器上道保险
折腾完这一轮,你大概也看出来了——dracut shell 里那行命令敲下去的时候,心跳通常是加速的。与其每次重启都赌一把,不如花十分钟把保险上好。
但凡要动内核、fstab 或者磁盘分区,先在云控制台打个快照。得清楚,这步操作的回滚代价就是点一下恢复,而另一边是重装系统加重新配置,折腾一整天。
备份到底备什么?/boot 分区、/etc/default/grub、/etc/fstab,这三个文件能让你在 GRUB 崩了的时候三分钟回到地面。我用 cron 写了个简单脚本,每周一把 /boot 和 /etc 下那几个关键文件 tar 到另一个磁盘分区,快照归快照,本地副本归本地副本,两层总比一层稳。
还有一个容易被忽略的:保留一个可用的备用内核。比如你当前跑着 5.10.xxx,升级到 5.14 之前,先确认旧内核的 vmlinuz 和 initramfs 还在 /boot 里躺着。ls /boot 看一眼列表,grubby --default-kernel 看看当前默认是谁。万一新内核翻车,GRUB 菜单里还能选旧的,不用进 dracut 对着 serial 日志发呆。
修启动故障最值钱的经验不是记住了多少命令,而是下次你会有意识地在改动前多敲那几行备份命令。快照两块钱,省你半夜三点从床上爬起来开串口。
评论