前言

周末例行升级 PVE 服务器,apt upgrade 跑完一切正常——直到重启后,屏幕上赫然出现了:

1
2
3
GNU GRUB version 2.12-9+pmx2
Minimal BASH-like line editing is supported...
grub> _

所有虚拟机和 LXC 容器全部离线,整个 PVE 宿主机无法引导。这篇文章记录了从发现问题到完全修复的全过程,希望能帮到遇到同样问题的朋友。

故障现象

升级完成后重启,直接卡在 GRUB 命令行界面,无法进入正常的引导菜单。PVE 宿主机(192.168.31.99)ping 不通,上面的所有 LXC 容器和虚拟机全部失联。

升级日志中有一个容易被忽略的线索:

1
2
3
Installing for x86_64-efi platform.
File descriptor 3 (pipe:[17948819]) leaked on vgs invocation.
Parent PID 3005002: grub-install.real

grub-install 过程中有 file descriptor leaked 警告,说明 GRUB 安装过程中出现了异常。

紧急救援:手动引导进系统

既然卡在 grub> 命令行,说明 GRUB 本身是能加载的,只是找不到正确的引导配置。这意味着我们可以手动指定内核来启动系统。

grub> 提示符下执行:

1
2
3
linux /boot/vmlinuz-6.17.13-7-pve root=/dev/mapper/pve-root ro quiet
initrd /boot/initrd.img-6.17.13-7-pve
boot

系统成功启动!用的是旧版本内核,但至少能进去了。

根因分析

进入系统后排查,发现了问题的根源:

1. proxmox-boot-tool 从未正确初始化

1
2
# proxmox-boot-tool status
E: /etc/kernel/proxmox-boot-uuids does not exist.

这个文件从 PVE 安装那天起就不存在!这意味着每次内核更新后,系统都不会把新内核同步到 EFI 分区。

2. 之前能正常启动全靠运气

没有 proxmox-boot-uuids,EFI 分区上的旧 GRUB 二进制文件和旧 grub.cfg 碰巧还能找到旧内核,所以之前每次重启都没问题。

3. 这次升级把隐患暴露了

升级更新了 shim-helpers-amd64-signedgrub-efi-amd64,新的 GRUB 被写入 EFI 分区,但新的 grub.cfg 中引用了 7.x 内核的 LVM 路径,而 GRUB 的 LVM 模块因配置不对无法正确解析磁盘元数据,导致直接掉进 grub> 命令行。

4. 7.x 内核的 initrd 缺失

1
2
ls /boot/initrd.img-7.0.2-6-pve
# 文件不存在!

vmlinuz 存在但 initrd 不存在,即使 GRUB 能找到内核也无法正常引导。

修复过程

Step 1: 初始化 proxmox-boot-tool

这是修复的核心步骤——让 PVE 正确识别 EFI 分区并建立同步机制:

1
2
3
4
5
6
7
8
# 先卸载 EFI 分区(已挂载时 init 会报错)
umount /boot/efi

# 初始化,将 EFI 分区注册到 proxmox-boot-tool
proxmox-boot-tool init /dev/nvme1n1p2

# 重新挂载
mount /boot/efi

输出中会看到:

1
2
3
4
5
Installing for x86_64-efi platform.
Installation finished. No error reported.
Copying kernel 6.17.13-7-pve
Copying kernel 7.0.2-2-pve
Copying kernel 7.0.2-6-pve

验证修复:

1
2
cat /etc/kernel/proxmox-boot-uuids
# 输出:D5FB-2F74 ✅ 文件已生成

Step 2: 生成缺失的 initrd

1
update-initramfs -u -k 7.0.2-6-pve

Step 3: 持久化内核启动参数

当前启动参数(IOMMU、ACS override 等)只存在于 /proc/cmdline,没有持久化配置文件,内核更新后可能丢失:

1
2
3
4
echo "root=/dev/mapper/pve-root ro nomodeset quiet intel_iommu=on iommu=pt pcie_acs_override=downstream,multifunction initcall_blacklist=sysfb_init" > /etc/kernel/cmdline

# 同步到 EFI 分区
proxmox-boot-tool refresh

Step 4: 清理旧内核

修复后发现 /boot 堆积了 18 个内核,占用 2.2G 空间:

1
2
ls /boot/vmlinuz-* | wc -l
# 18

只保留当前运行内核 + 一个备用 + 一个 6.x 回滚内核:

1
2
3
4
apt purge -y proxmox-kernel-6.14 proxmox-kernel-6.14.11-{1,2,3,4,5,6,8,9}-pve-signed \
proxmox-kernel-6.14.8-2-pve-signed \
proxmox-kernel-6.17.{4,9}-pve-signed \
proxmox-kernel-6.17.13-{1,2,3,7}-pve-signed

清理后 /boot 从 2.2G 降到 689M,保留 3 个内核:

内核 用途
7.0.2-6-pve 当前运行(默认)
7.0.2-2-pve 7.x 备用
6.17.13-11-pve 6.x 回滚

Step 5: 重启验证

1
reboot

重启后 GRUB 引导菜单正常显示,7.0.2-6-pve 内核成功启动,所有 LXC 容器自启动正常。

顺便修复:Locale 问题

修复过程中发现 PVE 的 locale 也有问题,perl 一直报 warning:

1
2
3
4
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
LANG = "en_US.UTF-8"
are supported and installed on your system.
1
2
3
4
5
6
7
8
9
10
# 安装 locale 和中文字体
apt install -y locales fonts-noto-cjk

# 生成 locale
sed -i "s/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/" /etc/locale.gen
sed -i "s/# zh_CN.UTF-8 UTF-8/zh_CN.UTF-8 UTF-8/" /etc/locale.gen
locale-gen

# 设置默认 locale
update-locale LANG=en_US.UTF-8

为什么之前升级没事?

翻看 dpkg 日志,这台 PVE 是从 9.0 全新安装的,之前的升级都是小版本更新(9.0→9.1→9.2),只更新应用包,不动引导层。这次升级恰好更新了 shim-helpers-amd64-signedgrub-efi-amd64,触发了 GRUB 重装,才把安装时遗留的隐患暴露出来。

简单说:之前能跑是运气好,这次升级把之前的债还了。

经验总结

  1. PVE 安装后检查 proxmox-boot-tool status,确保 EFI 分区已正确注册,不要等到出问题才发现
  2. 升级前看一眼升级内容,涉及 GRUB、内核、shim 的包要特别注意
  3. 升级后先别急着重启,检查 proxmox-boot-tool status/boot/efi/EFI/proxmox/ 的时间戳,确认文件已更新
  4. 掌握 GRUB 手动引导命令,关键时刻能救命:
    1
    2
    3
    linux /boot/vmlinuz-xxx root=/dev/mapper/pve-root ro quiet
    initrd /boot/initrd.img-xxx
    boot
  5. 定期清理旧内核,避免 /boot 空间不足和 grub 更新变慢
  6. 持久化 /etc/kernel/cmdline,确保 IOMMU 等关键启动参数不会丢失

参考