PVE 虚拟机 ImmortalWrt 扩容教程(免重装)

在 Proxmox VE 中为 ImmortalWrt(OpenWrt)虚拟机扩大磁盘空间,解决插件装不下的问题。全程无需重装系统,保留所有配置和已安装插件。

📋 前置信息

项目 说明
宿主机 Proxmox VE(Debian 系)
虚拟机 ImmortalWrt 24.10.6(squashfs-combined 镜像)
磁盘格式 raw / qcow2 均适用
文件系统 squashfs(只读根)+ f2fs overlay(可写层)
扩容目标 overlay 分区,即插件和配置的存储位置

🔍 了解 ImmortalWrt 的分区结构

ImmortalWrt(及 OpenWrt)默认使用 squashfs + overlay 的双层架构:

/dev/sda
├── sda128  BIOS boot   (239K)
├── sda1    Boot 分区    (32M)
└── sda2    Rootfs 分区  (300M)
             ├── squashfs  (只读根文件系统, ~45M)
             └── loop0 → f2fs overlay (可写层, ~253M, 插件装在这里)

空间不足的根本原因:overlay 只有约 253M,大型插件装几个就满了。

扩容的目标就是:扩大 sda2 分区 → 扩大 overlay 文件系统。


第一步:确认当前磁盘状态

SSH 进入 ImmortalWrt,执行以下命令收集信息:

df -h
fdisk -l /dev/sda
mount | grep overlay

需要记录的关键信息:

信息 示例值 用途
overlay 大小 252.9M 确认当前可用空间
sda2 起始扇区 66048 扩分区时必须保持不变
overlay 文件系统类型 f2fs 决定用哪个扩展工具
PARTUUID fdisk -l 输出中的 Disk identifier 扩分区时需要保留

[!IMPORTANT]
记下 fdisk -l 输出中 sda2 的起始扇区(Start),后面会用到。

还需要获取 overlay 的偏移量信息(后面扩展 f2fs 时需要):

cat /sys/block/loop0/loop/offset

记下这个数字(例如 47251456)。


第二步:PVE 扩大虚拟磁盘

方法 A:通过 Web 管理界面

  1. 关闭虚拟机
  2. 选中虚拟机 → 硬件 → 选中硬盘
  3. 点击 磁盘操作 → 调整大小
  4. 输入要增加的大小(如 2 表示增加 2 GiB)
  5. 点击确认

方法 B:通过命令行

在 PVE 宿主机 Shell 中执行:

# 关闭虚拟机(替换 VMID 为你的虚拟机 ID)
qm stop <VMID>

# 扩大磁盘 +2G(scsi0 对应第一块 SCSI 磁盘,根据实际情况调整)
qm resize <VMID> scsi0 +2G

第三步:查看虚拟磁盘路径

在 PVE 宿主机 Shell 中查看虚拟机配置:

cat /etc/pve/qemu-server/<VMID>.conf

找到包含 scsi0 或 virtio0 的行,例如:

scsi0: local:101/vm-101-disk-0.raw,size=340256K

根据存储类型确定磁盘路径:

配置中的值 实际磁盘路径
local:<VMID>/vm-<VMID>-disk-0.raw /var/lib/vz/images/<VMID>/vm-<VMID>-disk-0.raw
local:<VMID>/vm-<VMID>-disk-0.qcow2 /var/lib/vz/images/<VMID>/vm-<VMID>-disk-0.qcow2
local-lvm:vm-<VMID>-disk-0 /dev/pve/vm-<VMID>-disk-0

[!NOTE]
以下示例中使用 /var/lib/vz/images/101/vm-101-disk-0.raw 作为磁盘路径,请替换为你的实际路径。


第四步:使用 sgdisk 扩展分区

[!CAUTION]
操作前务必在 PVE 中为虚拟机创建快照! 万一操作失误可以一键回滚。

确保虚拟机已关闭,在 PVE 宿主机 Shell 中逐条执行:

# 定义磁盘路径变量(替换为你的实际路径)
DISK=/var/lib/vz/images/101/vm-101-disk-0.raw

4.1 修复 GPT 备份头

扩大磁盘后,GPT 备份头位置不对,需要先修复:

sgdisk -e $DISK

4.2 扩展 sda2 分区

# 删除旧的分区 2
sgdisk -d 2 $DISK

# 重建分区 2(起始扇区保持不变,结束位置用完全部空间)
# ⚠️ 66048 替换为你第一步记录的 sda2 起始扇区
sgdisk -n 2:66048:0 $DISK

# 恢复原始 PARTUUID(替换为你的实际值)
# ⚠️ 这一步至关重要!PARTUUID 错误会导致系统无法启动
sgdisk -u 2:<你的PARTUUID> $DISK

[!WARNING]
如何获取正确的 PARTUUID?

在扩容前,先在 ImmortalWrt 中运行 fdisk -l /dev/sda 查看 Disk identifier,
然后将其最后两位改为 02(对应分区号 2)。

例如:Disk identifier 为 70A75237-C499-577D-1C86-271D2B1D1C00,
则 sda2 的 PARTUUID 为 70A75237-C499-577D-1C86-271D2B1D1C02。

4.3 验证结果

sgdisk -p $DISK

确认输出中:

  • 分区 2 的 Start sector 仍然是原来的值(如 66048)
  • 分区 2 的 Size 已经变大(如 2.3 GiB)
  • Total free space 为 0

示例输出:

Number  Start (sector)    End (sector)  Size       Code  Name
   1             512          66047   32.0 MiB    8300
   2           66048        4874782   2.3 GiB     8300
 128              34             511   239.0 KiB   EF02

第五步:扩展 f2fs 文件系统

分区扩大后,其中的 f2fs 文件系统还是原来的大小,需要单独扩展。

5.1 安装必要工具

PVE 宿主机上需要 f2fs 支持:

apt-get update && apt-get install -y f2fs-tools

5.2 计算 f2fs 的偏移量

f2fs overlay 不是从 sda2 开头开始的,而是在 squashfs 之后。偏移量的计算方式:

f2fs 偏移量 = sda2 起始字节 + overlay 在分区内的偏移

sda2 起始字节 = sda2 起始扇区 × 512

例如:

  • sda2 起始扇区:66048 → 起始字节:66048 × 512 = 33,816,576
  • overlay 偏移量(第一步记录的值):47,251,456
  • f2fs 在磁盘中的绝对偏移 = 33,816,576 + 47,251,456 = 81,068,032

5.3 挂载 f2fs 清理日志

f2fs 的 resize 工具要求文件系统处于"干净"状态。需要在 PVE 宿主机上挂载一次来清理日志:

# 创建 loop 设备(偏移量替换为你计算的值)
losetup -f --show -o 81068032 $DISK
# 记住输出的设备名,如 /dev/loop0

# 加载 f2fs 内核模块
modprobe f2fs

# 挂载并卸载以清理日志
mkdir -p /tmp/f2fs_fix
mount -t f2fs /dev/loop0 /tmp/f2fs_fix
umount /tmp/f2fs_fix

5.4 执行扩展

resize.f2fs /dev/loop0

成功输出应包含:

Info: Done to rebuild checkpoint blocks
Info: Done to update superblock
Done: 0.000000 secs

5.5 清理并启动

losetup -d /dev/loop0
qm start <VMID>

第六步:验证扩容结果

SSH 进入 ImmortalWrt:

df -h

扩容前:

/dev/loop0    252.9M    49.6M   203.3M  20% /overlay

扩容后:

/dev/loop0      2.2G   228.1M     2.0G  10% /overlay

🎉 大功告成! 现在可以随意安装插件了。


⚠️ 常见问题

Q1:重启后系统无法启动,卡在 "Waiting for root device PARTUUID=..."

原因:扩展分区时 PARTUUID 被改变了。

解决方法:在 PVE 宿主机上用 sgdisk 恢复正确的 PARTUUID:

qm stop <VMID>
sgdisk -u 2:<正确的PARTUUID> <磁盘路径>
qm start <VMID>

Q2:resize.f2fs 报错 "Mount unclean image to replay log first"

原因:f2fs 日志没有被正确清理(通常是因为强制关机)。

解决方法:在 PVE 宿主机上挂载 f2fs 一次来清理日志(见第五步 5.3)。

Q3:resize.f2fs 报错 "Failed to open the device!"

原因:在 ImmortalWrt 内部运行 resize.f2fs 时,overlay 处于挂载状态,无法操作。

解决方法:不要在 ImmortalWrt 内部 resize,需要关闭虚拟机后在 PVE 宿主机上操作(见第五步)。

Q4:sgdisk -p 显示分区扩大了但 "last usable sector" 还是很小

原因:GPT 备份头没有移到磁盘末尾。

解决方法:先执行 sgdisk -e <磁盘路径> 修复 GPT 备份头位置,然后重新扩展分区。

Q5:overlay 是 ext4 而不是 f2fs 怎么办?

如果你的 ImmortalWrt overlay 使用的是 ext4(通过 mount | grep overlay 确认),则第五步的 resize 命令改为:

# 替代 resize.f2fs
e2fsck -f /dev/loop0
resize2fs /dev/loop0

📝 完整命令速查

以下是整个过程的精简命令列表,适合熟悉流程后快速参考:

# === PVE 宿主机操作 ===

# 变量设置(替换为你的实际值)
VMID=101
DISK=/var/lib/vz/images/$VMID/vm-$VMID-disk-0.raw
START_SECTOR=66048        # sda2 起始扇区
PARTUUID=70A75237-C499-577D-1C86-271D2B1D1C02   # sda2 的 PARTUUID
F2FS_OFFSET=81068032      # f2fs 在磁盘中的绝对偏移

# 1. 关机并扩大虚拟磁盘
qm stop $VMID
qm resize $VMID scsi0 +2G

# 2. 修复 GPT 并扩展分区
sgdisk -e $DISK
sgdisk -d 2 $DISK
sgdisk -n 2:$START_SECTOR:0 $DISK
sgdisk -u 2:$PARTUUID $DISK
sgdisk -p $DISK   # 验证

# 3. 挂载清理 f2fs 日志并扩展
apt-get install -y f2fs-tools
losetup -f --show -o $F2FS_OFFSET $DISK   # 记住输出的 /dev/loopX
modprobe f2fs
mkdir -p /tmp/f2fs_fix
mount -t f2fs /dev/loop0 /tmp/f2fs_fix
umount /tmp/f2fs_fix
resize.f2fs /dev/loop0

# 4. 清理并启动
losetup -d /dev/loop0
qm start $VMID

# === ImmortalWrt 验证 ===
df -h

本教程基于 ImmortalWrt 24.10.6 + Proxmox VE 实测验证。如果你的 OpenWrt/ImmortalWrt 使用 ext4 镜像而非 squashfs,扩容过程会更简单(直接 cfdisk 扩分区 + resize2fs 即可)。

上一篇 win11下安装BT2019 R4