在 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 管理界面
- 关闭虚拟机
- 选中虚拟机 → 硬件 → 选中硬盘
- 点击 磁盘操作 → 调整大小
- 输入要增加的大小(如
2表示增加 2 GiB) - 点击确认
方法 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 即可)。