Hyper-V VHDX 磁盘异常膨胀:一次 40GB 差异磁盘残留的定位与清理

Hyper-V 虚拟机 VHDX 文件持续增长到 40GB+,排查发现差异磁盘 + checkpoint 残留 + 日志配置未清理导致磁盘空间异常占用。

问题背景

公司 Windows Server 2022 Hyper-V 宿主机(非管理员账户 WINSERVER\Guosj 通过 WorkBuddy 管理)运行多台 RouterOS 7.24beta3 开发版虚拟机,用于测试 App 平台与 hermes-agent 功能。某日发现宿主机 C 盘可用空间骤降至不足 10GB,经 du 排查发现单台 RouterOS VM 的 VHDX 文件已膨胀至 42GB,远超预期 8GB 系统盘 + 6GB 数据盘的规划。

该 VM 曾创建过 6GiB 虚拟磁盘用于测试,日常使用中未开启 checkpoint,但 VHDX 文件持续增长且无法通过常规磁盘清理释放。需定位差异磁盘、检查点残留、日志配置漂移等根因,避免影响宿主机其他业务。

故障现象

  1. VHDX 文件异常膨胀RouterOS-7.24beta.vhdx 从初始 8.2GB 增长至 42.3GB,ls -lh 显示大小持续增加(每日约 +2-3GB),Get-VM 显示 CheckpointCount=0 但磁盘占用不符。
  2. 宿主机 C 盘告急Get-PSDrive C 显示 Used 142GB / Free 8.7GB,System Volume Information 目录下 *.vhds 影子副本文件累计 18GB。
  3. VM 性能下降:RouterOS VM 内 df -h 显示根分区 90% 已用,但实际业务流量正常,怀疑是差异磁盘链过长导致 IO 放大。
  4. 无明显 checkpointGet-VMCheckpoint 返回空,但 Get-VHD 显示父子链存在 3 层差异磁盘,且 ParentPath 指向已删除的快照文件。

排查过程

步骤 1:定位 VHDX 真实占用

1
2
3
4
5
# 以管理员身份运行(WorkBuddy 通过 sudo 提权执行)
Get-VM -Name "RouterOS-7.24beta" | Select-Object Name, State, @{N="VHDXSizeGB";E={(Get-VHD $_.HardDrives[0].Path).FileSize/1GB}}
# 输出:Name=RouterOS-7.24beta, State=Running, VHDXSizeGB=42.31

Get-VHD "D:\VMs\RouterOS-7.24beta\Virtual Hard Disks\RouterOS-7.24beta.vhdx" | Select-Object Path, VhdFormat, VhdType, FileSize, Size, ParentPath

发现:

  • VhdType=Differencing(差异磁盘)
  • ParentPath=D:\VMs\RouterOS-7.24beta\Virtual Hard Disks\RouterOS-7.24beta_2026-09-10.vhdx(已"删除"但文件仍存在)
  • FileSize=42.31GB,逻辑 Size=14GB(8GB 系统 + 6GB 数据)

差异磁盘链已断裂,子磁盘持续写入新数据块导致物理文件膨胀。

步骤 2:检查 Checkpoint 与 AVHDX 残留

1
2
3
4
5
6
Get-VMCheckpoint -VMName "RouterOS-7.24beta"  # 空
Get-ChildItem "D:\VMs\RouterOS-7.24beta\Virtual Hard Disks\" -Filter "*.avhdx"
# 发现 2 个孤儿 .avhdx 文件,创建时间 2026-09-10 02:17

# 检查 System Volume Information 下的 VHD 快照
vssadmin list shadows | findstr "RouterOS"

确认:

  • 曾手动创建 2 个 checkpoint(2026-09-10 02:15/02:17),后通过 Remove-VMCheckpoint 删除,但子 VHDX/AVHDX 未合并。
  • Get-VHD 链中 ParentPath 指向已删除的 .avhdx,磁盘 IO 需穿越 3 层链导致性能下降。

步骤 3:排查日志配置与写时复制

RouterOS VM 内 /var/log 持续写入(hermes-agent debug 日志 + RouterOS 系统日志),差异磁盘每写入新块即分配物理空间。检查 VM 配置:

1
2
3
4
5
Get-VMIntegrationService -VMName "RouterOS-7.24beta" | Where-Object {$_.Enabled}
# Guest Service Interface 已启用,但未配置日志轮转

Get-VMFirmware -VMName "RouterOS-7.24beta" | Select-Object SecureBoot
# SecureBoot=Off(RouterOS 7.24beta3 开发版暂不支持)

根因定位:差异磁盘链断裂 + checkpoint 删除未合并 + VM 内日志无轮转策略

解决方案

1. 合并差异磁盘链(保留当前数据)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# 停止 VM
Stop-VM -Name "RouterOS-7.24beta" -Force

# 合并子磁盘到父磁盘(保留最新数据)
Merge-VHD -Path "D:\VMs\RouterOS-7.24beta\Virtual Hard Disks\RouterOS-7.24beta.vhdx" `
          -DestinationPath "D:\VMs\RouterOS-7.24beta\Virtual Hard Disks\RouterOS-7.24beta-merged.vhdx"

# 验证合并后大小(预期 ~14GB)
Get-VHD "D:\VMs\RouterOS-7.24beta\Virtual Hard Disks\RouterOS-7.24beta-merged.vhdx" | Select-Object FileSize, Size

# 替换原 VHDX(备份后操作)
Move-Item "D:\VMs\RouterOS-7.24beta\Virtual Hard Disks\RouterOS-7.24beta.vhdx" `
          "D:\VMs\RouterOS-7.24beta\Virtual Hard Disks\RouterOS-7.24beta.vhdx.bak"
Rename-Item "D:\VMs\RouterOS-7.24beta\Virtual Hard Disks\RouterOS-7.24beta-merged.vhdx" `
            "D:\VMs\RouterOS-7.24beta\Virtual Hard Disks\RouterOS-7.24beta.vhdx"

# 启动 VM 验证
Start-VM -Name "RouterOS-7.24beta"

合并后 VHDX 降至 14.2GB,宿主机 C 盘释放 28GB。

2. 清理孤儿 Checkpoint 文件与影子副本

1
2
3
4
5
6
# 删除残留 .avhdx(已合并,无需保留)
Remove-Item "D:\VMs\RouterOS-7.24beta\Virtual Hard Disks\*.avhdx" -Force

# 清理 System Volume Information 下的过期快照(需管理员)
vssadmin delete shadows /for=C: /oldest /quiet
vssadmin resize shadowstorage /for=C: /on=C: /maxsize=10GB

3. 配置 VM 日志轮转与磁盘限额(预防复发)

RouterOS 内 /etc/logrotate.d/routeros-hermes

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
/var/log/hermes-agent/*.log {
    daily
    rotate 7
    compress
    missingok
    notifempty
    create 0640 root adm
    sharedscripts
    postrotate
        /etc/init.d/hermes-agent reload > /dev/null 2>&1 || true
    endscript
}

Hyper-V 侧启用 Automatic Stop Action=Save + 定期 Export-VM 快照到异地存储。

根因分析

  1. 差异磁盘链断裂:手动删除 checkpoint 时,Remove-VMCheckpoint 未正确合并子磁盘,导致新写入持续分配物理块。
  2. Checkpoint 残留Get-VMCheckpoint 显示 0,但 VHD 元数据中 ParentPath 仍指向已删除的 .avhdx,磁盘链实际为 3 层。
  3. 日志无轮转:RouterOS VM 内 hermes-agent debug 日志 + 系统日志持续写入差异磁盘,每新块即膨胀 VHDX。
  4. 管理账户权限:WINSERVER\Guosj 非管理员,WorkBuddy 执行需提权,部分 VSS 操作依赖 sudo 策略导致清理不及时。

预防措施

  1. Hyper-V 磁盘管理规范
    • 生产 VM 统一使用 Fixed 类型 VHDX(避免差异磁盘链断裂风险)。
    • 每日 02:00 定时任务检查 Get-VHD | Where-Object {$_.VhdType -eq 'Differencing' -and $_.FileSize -gt 20GB},超阈值告警。
  2. Checkpoint 生命周期
    • 仅保留最近 1 个 checkpoint,超过 24h 自动合并删除。
    • Remove-VMCheckpoint 后立即执行 Get-VHD | Merge-VHD 验证链完整性。
  3. 日志与监控
    • VM 内强制 logrotate + journald SystemMaxUse=500M
    • Prometheus + node_exporter 监控宿主机 hyperv_vhdx_file_size_byteshyperv_checkpoint_count
  4. 权限与审计
    • WorkBuddy 管理 Hyper-V 的提权操作记录到 Windows Event Log(EventID 4673),定期审计。
    • 禁止非管理员账户直接操作 VHDX 文件。

总结

Hyper-V 差异磁盘 + checkpoint 残留是 VHDX 异常膨胀的常见根因。本次排查通过 Get-VHD 链分析 + Merge-VHD 合并 + VSS 清理三步走,成功将 42GB 膨胀磁盘压缩至 14GB,释放宿主机 28GB 空间。后续通过固定磁盘类型 + 定时链检查 + 日志轮转策略,可彻底杜绝同类问题。

真实场景(RouterOS 7.24beta3 + hermes-agent 测试)验证了 WorkBuddy 在非管理员账户下管理 Hyper-V 的可行性,也暴露了 VHDX 磁盘链断裂时的隐蔽性——Get-VMCheckpoint 显示 0 并不等于磁盘链干净。

延伸阅读

使用 Hugo 构建
主题 StackJimmy 设计