LVM 迁移至 LVM-Thin
场景说明
双节点集群,节点名分别为 pve 和 x370:
| 节点 | 系统盘 | 外挂存储 | 初始状态 |
|---|---|---|---|
| pve | 1T NVMe | 2T HDD(挂载为 hdd) |
有 LVM,无 Thin Pool,运行 VM-104(384G,实际 ~150G)、VM-110(384G,实际 ~276G) |
| x370 | 500G NVMe | 2T HDD(挂载为 hdd) |
仅 LVM,无 Thin Pool,无 VM |
本文记录的操作:
- 在 x370 上移除 swap、缩容 root、创建 LVM-Thin Pool
- 将 pve 上的 VM-104 和 VM-110 跨节点迁移到 x370 的 LVM-Thin
1. 查看当前存储布局(x370 节点)
lvdisplay
vgdisplay
pvdisplay
输出:
--- Logical volume ---
LV Path /dev/pve/swap
LV Name swap
LV Size 8.00 GiB
--- Logical volume ---
LV Path /dev/pve/root
LV Name root
VG Name pve
LV Size <456.76 GiB
--- Volume group ---
VG Name pve
VG Size <464.76 GiB
Free PE / Size 0 / 0 # ← VG 已全部用完
--- Physical volume ---
PV Name /dev/nvme0n1p3
PV Size 464.76 GiB
pvesm status
cat /etc/pve/storage.cfg
当前只有 local(Directory 类型,路径 /var/lib/vz),无 local-lvm。
2. 确认 local 上的数据量
local 实际存储在系统根分区(/dev/pve/root)上:
df -h /
du -sh /var/lib/vz/*
x370 的系统盘非常干净——/var/lib/vz/ 下几乎无数据,root 仅占用 4.4 GiB。
结合 PVE 系统 + 更新 + 上传 ISO 临时空间,决定缩容 root 到 30 GiB:
缩容后布局:
root 30 GiB ← PVE 系统
thinpool ~434 GiB ← swap 8G + root 释放的 ~426G
上传 ISO 时文件会先写入
/var/tmp(root 分区),30G 留出了约 16G 余量。
3. 移除 swap(PVE 运行中操作)
3.1 停用 swap
swapoff -a
swapon --show
# 无输出才算彻底关闭
free -h
# Swap 行应为 0
3.2 从 /etc/fstab 移除 swap 条目
cat /etc/fstab
# 输出:
# /dev/pve/root / ext4 errors=remount-ro 0 1
# UUID=AB21-9339 /boot/efi vfat defaults 0 1
# /dev/pve/swap none swap sw 0 0 ← 删掉这一行
# proc /proc proc defaults 0 0
# PARTUUID=... /mnt/pve/mHdd exfat nofail,... 0 0
sed -i '/\/dev\/pve\/swap/d' /etc/fstab
3.3 删除 swap LV
lvremove /dev/pve/swap
3.4 验证空闲空间
vgdisplay
# Free PE / Size 应显示约 8 GiB
4. Live 环境中缩容 root 并创建 Thin Pool
root 是运行中的根分区(ext4),无法在线缩容。以下步骤在 PVE ISO Debug Mode 的 Live 环境中完成。
4.1 进入 PVE Debug Mode Live 环境
① 从 PVE ISO 启动,选择:
Advanced Options
├── Install Proxmox VE (Graphical, Debug Mode) ← 选这个
└── Install Proxmox VE (Terminal UI, Debug Mode) ← 图形黑屏则选这个
② 系统启动后会先进入 BusyBox 最小 shell(无 LVM 工具),直接退出:
exit
# 或 Ctrl + D
③ 退出后继续加载,会再次弹出 第二个 debug shell,此时 LVM 工具已就绪。
4.2 激活 LVM
vgscan
vgchange -ay
lvdisplay
# 应输出 /dev/pve/root
4.3 检查文件系统
e2fsck -f /dev/pve/root
4.4 缩小 ext4 到 30 GiB
resize2fs /dev/pve/root 30G
4.5 缩小 LV 到 30 GiB
lvreduce -L 30G /dev/pve/root
4.6 用空闲空间创建 Thin Pool
此时 VG 中的空闲空间 = swap 释放的 8 GiB + root 缩容释放的 ~426 GiB:
lvcreate -l 100%FREE -T /dev/pve/thinpool
验证:
lvdisplay
# 应看到:
# /dev/pve/root 30G
# /dev/pve/thinpool ~434G
4.7 退出 Live 环境
exit
# 进入安装界面,选择 Abort → 拔掉 U 盘 → reboot
5. 重启回 PVE 并注册存储
5.1 验证 LVM
lvdisplay
# 应有 /dev/pve/root(30G)和 /dev/pve/thinpool(~434G)
5.2 向 PVE 添加 LVM-Thin 存储
pvesm add lvmthin local-lvm --vgname pve --thinpool thinpool
参数:
| 参数 | 必填 | 说明 |
|---|---|---|
local-lvm |
✅ | PVE 中显示的名称 |
--vgname |
✅ | 卷组名,这里是 pve |
--thinpool |
✅ | 刚创建的 Thin Pool 名 |
--content |
❌ | 默认 images,rootdir |
验证:
pvesm status
# 应看到 local-lvm,类型 lvmthin
cat /etc/pve/storage.cfg
# 对应配置段:
# lvmthin: local-lvm
# thinpool thinpool
# vgname pve
# content images,rootdir
6. 跨节点迁移前置:QEMU 版本对齐
跨节点在线迁移要求两节点 QEMU 版本一致。pve 节点的 PVE 版本较新(QEMU 9.x),而 x370 的 QEMU 还是 8.1.5:
pveversion
qemu-system-x86_64 --version | head -1
在 x370 上配置清华源并升级:
# 配置清华源(以 bookworm 为例)
cat > /etc/apt/sources.list << 'EOF'
deb https://mirrors.tuna.tsinghua.edu.cn/debian bookworm main contrib non-free non-free-firmware
deb https://mirrors.tuna.tsinghua.edu.cn/debian bookworm-updates main contrib non-free non-free-firmware
deb https://mirrors.tuna.tsinghua.edu.cn/debian-security bookworm-security main contrib non-free non-free-firmware
EOF
# 配置 PVE 社区源
cat > /etc/apt/sources.list.d/pve.list << 'EOF'
deb https://mirrors.tuna.tsinghua.edu.cn/proxmox/debian/pve bookworm pve-no-subscription
EOF
# 禁用企业版仓库(非订阅用户)
sed -i 's/^deb/#deb/' /etc/apt/sources.list.d/pve-enterprise.list 2>/dev/null
# 更新并升级
apt update && apt dist-upgrade -y
升级后可能看到以下提示:
Processing triggers for initramfs-tools (0.142+deb12u3) ...
update-initramfs: Generating /boot/initrd.img-6.8.12-25-pve
...
Removable bootloader found at '/boot/efi/EFI/BOOT/BOOTX64.efi',
but GRUB packages not set up to update it!
Run the following command:
echo 'grub-efi-amd64 grub2/force_efi_extra_removable boolean true' | debconf-set-selections -v -u
Then reinstall GRUB with 'apt install --reinstall grub-efi-amd64'
这不是错误,是 UEFI 可移动启动路径的一次性提醒。按提示修复即可:
echo 'grub-efi-amd64 grub2/force_efi_extra_removable boolean true' | debconf-set-selections -v -u
apt install --reinstall grub-efi-amd64
注意:升级前先解除 r8169 黑名单(避免失联)
如果 x370 使用 Realtek RTL8125 网卡并安装了第三方 r8125 驱动,apt dist-upgrade 升级内核后需要重新编译 r8125 驱动。
正确做法:升级后不要立即重启。此时旧内核仍在运行,r8125 驱动正常工作,网络未断开。先解除 r8169 黑名单再重启,确保新内核的备用方案就绪:
# 重启前解除 r8169 黑名单,让新内核回落为 r8169
sed -i 's/^blacklist r8169/#blacklist r8169/' /etc/modprobe.d/blacklist-r8169.conf
update-initramfs -u
reboot
重启后新内核加载 r8169 驱动,网络正常连接。通过 SSH 远程完成后续操作:
# 安装新内核头文件(此时 uname -r 为新内核版本)
apt install pve-headers-$(uname -r) -y
cd /root/realtek-r8125-dkms
./dkms-install.sh
# 恢复黑名单,禁用 r8169,让 r8125 接管
sed -i 's/^#blacklist r8169/blacklist r8169/' /etc/modprobe.d/blacklist-r8169.conf
update-initramfs -u
reboot
第二次重启后 r8125 驱动正常工作。
若已重启导致失联,接显示器键盘物理恢复后执行上述步骤。详见 realtek-r8125.md
7. 迁移 VM-104(在线迁移)
VM-104:384 GiB 虚拟大小,实际占用 ~150 GiB(Windows,Trim 后)。
x370 的 thin pool 当前还有 ~434 GiB 剩余空间,远超 VM-104 的虚拟大小,可直接在线迁移。
7.1 PVE GUI 迁移
x370 Web UI → 选择 pve 节点 → VM-104 → 迁移:
- 目标节点:
x370 - 在线迁移(保持 VM 运行)
- 高级 → 目标存储:
local-lvm
在线迁移使用 NBD(Network Block Device)传输数据,不加密,性能相当于纯 TCP 吞吐。
7.2 验证迁移完成
qm config 104
# scsi0: local-lvm:vm-104-disk-0,...
# efidisk0: local-lvm:vm-104-disk-1,...
lvs -a | grep thinpool
# Data% 应显示约 35%(150G / 434G)
7.3 配置 Discard、SSD 仿真
迁移完成后磁盘默认使用 raw 格式,需要开启 discard 和 ssd 仿真以支持 Trim:
qm set 104 --scsi0 local-lvm:vm-104-disk-0,cache=writeback,discard=on,iothread=1,ssd=1
qm set 104 --efidisk0 local-lvm:vm-104-disk-1,efitype=4m,pre-enrolled-keys=1
ssd=1可以在磁盘创建后随时添加,不影响已有数据。Windows 重启后会自动识别为 SSD。
EFI 盘大小说明
EFI 盘虽然只有 528K,但 LVM-Thin 的最小分配单位是 4 MiB(PE Size):
# 日志输出:Rounding up size to full physical extent 4.00 MiB
# 下面两种写法效果一样
lvcreate -V 528K -T pve/thinpool -n vm-104-disk-1 # 自动 round up 到 4M
lvcreate -V 4M -T pve/thinpool -n vm-104-disk-1 # 直接给 4M
8. Windows VM Trim 配置(VM-104)
LVM-Thin 的磁盘是精简置备的,VM 内删除文件后,需要通知底层存储释放已空闲的块,否则物理空间不会被回收。
8.1 检查磁盘配置
qm config 104
要求配置:
| 配置项 | 要求 |
|---|---|
| 总线类型 | VirtIO SCSI |
| SCSI 控制器 | virtio-scsi-single |
| Discard | on |
| SSD 仿真 | on |
8.2 Windows 内确认驱动
打开 设备管理器 → 存储控制器,应看到 Red Hat VirtIO SCSI pass-through controller。
如果不是,挂载 virtio-win.iso 更新驱动。
8.3 手动 Trim
管理员 PowerShell:
# 普通 Trim(日常回收)
Optimize-Volume -DriveLetter C -Verbose
# 全盘强制 Retrim(删除大量文件后立即回收,需足够 VM 内存)
Optimize-Volume -DriveLetter C -ReTrim -Verbose
全盘 Retrim 需要为磁盘全部空间构建位图,384G 磁盘约需 8G+ VM 内存,不足时报错 40002。
8.4 验证 Trim 状态
# Trim 是否启用(0 = 启用)
fsutil behavior query DisableDeleteNotify
# 输出:NTFS DisableDeleteNotify = 0
# 磁盘类型(应为 SSD)
Get-PhysicalDisk | Select-Object FriendlyName, MediaType, BusType
# MediaType 应为 SSD
8.5 备选:设置每周自动 Trim
Windows 通常自带磁盘优化计划任务(每周自动运行),不需要额外配置。如需手动添加备选任务:
schtasks /Create /SC WEEKLY /D SUN /TN "Trim C Drive" /TR "powershell.exe -Command 'Optimize-Volume -DriveLetter C -Verbose'" /RL HIGHEST /RU SYSTEM
8.6 PVE 上验证回收
lvs -a | grep thinpool
关注 Data%——VM-104 虚拟 384G 但 Data% 仅 ~35%,说明精简特性在正常工作。
lvs的LV Size是虚拟大小(精简置备的上限),不代表物理占用。物理占用看Data%。
9. 迁移 VM-110
VM-110:384 GiB 虚拟大小,实际占用 ~276 GiB(Windows,Trim 前)。
此时 x370 的 thin pool 已有 VM-104 占用 ~150G,剩余约 284 GiB。剩余空间小于 VM-110 的虚拟大小(384G),但大于实际数据(~276G)。
9.1 第一次尝试:在线迁移(被拒绝)
qm migrate 110 x370 --online --with-local-disks --target-storage local-lvm
PVE 拒绝执行——qm migrate 在发起前会检查目标 thin pool 的剩余物理空间是否 ≥ 虚拟大小,即使实际数据少得多也会拒绝。
9.2 第二次尝试:qemu-img convert -W(失败,数据损坏)
绕过在线迁移的限制,将 qcow2 文件拷贝到 x370 的机械硬盘后,直接导入到 LVM-Thin:
# ❌ 错误方案
qemu-img convert -p -W /mnt/pve/hdd/images/110/vm-110-disk-0.qcow2 /dev/pve/vm-110-disk-0
结果:Thin Pool 写穿至 100%,VM 能启动但系统卡顿、应用损坏。进入 Windows 后执行 Trim 报错:
VERBOSE: Invoking slab consolidation on (C:)...
Optimize-Volume : Failed
执行 chkdsk C: /F 发现文件系统错误(dirty bit 被设置),但修复后系统仍然异常——这不是普通的 dirty bit 问题,是 thin pool 写穿导致的数据错乱。
原因:-W 的含义是 out-of-order writes(乱序写入):
官方文档:Allow out-of-order writes to the destination. This option improves performance, but is only recommended for preallocated devices like host devices or other raw block devices.
LVM-Thin volume 不是预分配设备,乱序写入导致 thin pool 的 chunk 分配碎片化,384G 虚拟空间被全部实际分配,物理空间耗尽后后续写入被丢弃。
| 场景 | 用 -W? |
|---|---|
| 预分配设备(厚置备 LVM、物理磁盘) | ✅ 乱序写入提升性能 |
| LVM-Thin volume | ❌ 不可用,碎片化写穿 |
管道传输(| ssh dd) |
❌ 不可用 |
| qcow2 → qcow2 | ❌ 不需要 |
结论:往 LVM-Thin 写数据永远不要加 -W。
9.3 恢复:先备份 VM-104,再清理损坏数据
VM-104 已迁移到 x370 且正在正常使用,在清理前先将其备份到 x370 的机械硬盘上:
# 在 x370 上备份 VM-104 到本地机械硬盘
vzdump 104 --storage hdd --compress zstd
# 备份文件保存到 /mnt/pve/hdd/dump/
备份完成后,删除 x370 thin pool 中损坏的 VM-110 数据(同时清理 VM-104 以腾出空间):
# 删除损坏的卷,释放 thin pool 空间
lvremove /dev/pve/vm-104-disk-0
lvremove /dev/pve/vm-104-disk-1
lvs -a | grep thinpool
# Data% 应降到正常水平
9.4 迁移策略调整
thin pool 总容量 ~434G,需迁入两个各 384G 的 VM。决定调整顺序:
- 先迁 VM-110(实际 ~276G,较大)→ 在线迁移
- 再迁 VM-104(实际 ~150G,较小)→ 备份恢复
这样 thin pool 在第一步后剩余 ~158G(434 - 276),第二步恢复备份时 VMA 会跳过空洞,~150G 实际数据写入后仍有盈余。
9.5 在线迁移 VM-110
VM-110 跨节点在线迁移到 x370:
qm migrate 110 x370 --online --with-local-disks --target-storage local-lvm
迁移完成后:
qm config 110
lvs -a | grep thinpool
# Data% 应约 63%(276G / 434G)
9.6 从备份恢复 VM-104
VM-104 的备份已在 9.3 中完成(在 x370 上备份到机械硬盘)。现在从备份恢复到 thin pool:
在 x370 节点 的 GUI 上操作:
Datacenter → x370 → 备份 → 选择备份文件 → 恢复到 local-lvm
VMA 恢复日志:
CFG: size: 658 name: qemu-server.conf
DEV: dev_id=1 size: 540672 devname: drive-efidisk0
DEV: dev_id=2 size: 412316860416 devname: drive-scsi0
Logical volume "vm-104-disk-0" created.
Logical volume "vm-104-disk-1" created.
map 'drive-efidisk0' to '/dev/pve/vm-104-disk-0' (write zeros = 0)
map 'drive-scsi0' to '/dev/pve/vm-104-disk-1' (write zeros = 0)
progress 1% (read 4123197440 bytes, duration 17 sec)
...
total bytes read 412317450240, sparse bytes 272608817152 (66.1%)
space reduction due to 4K zero blocks 1.45%
关键数据解读:
total bytes read 412317450240 → 虚拟 384 GiB
sparse bytes 272608817152 → 66.1% 被检测为空洞,跳过未写
zero block reduction → 额外 1.45% 零块跳过
实际写入 thin pool ≈ 133 GiB ← 远小于虚拟大小
9.7 最终结果
thin pool 物理: 434 GiB
VM-110 实际占用: ~276 GiB ← 在线迁移
VM-104 实际占用: ~133 GiB ← VMA 恢复(66.1% sparse 跳过)
━━━━━━━━━━━━━━━━━━━━━━━━━━━
实际总计: ~409 GiB ◀ 仍有 ~25 GiB 余量
两种方案对比:
qemu-img convert -W ❌ |
VMA 备份恢复 ✅ | |
|---|---|---|
| 稀疏检测 | 无(-W 乱序写入填满全部) | 内置,恢复时自动跳过 66%+ 空洞 |
| 写入顺序 | 乱序(碎片化) | 顺序(紧密排列) |
| Thin pool 空间利用 | 差,写穿 100% | 优,仅 ~35% |
| 数据一致性 | 数据损坏 | 高(VMA 含校验) |
超额配置警告
创建 VM 的 thin volume 时可能看到:
WARNING: Sum of all thin volume sizes (<770.01 GiB) exceeds the size of thin pool
WARNING: You have not turned on protection against thin pools running out of space.
这是正常现象——两个 384G 的虚拟卷总和 768G 远超 434G 物理空间,这正是精简配置的工作方式。只要实际数据量之和不超过物理上限即可。
如果担心撑爆,可以启用自动扩容(需 VG 有空闲空间):
# /etc/lvm/lvm.conf 设置:
# thin_pool_autoextend_threshold = 80
# thin_pool_autoextend_percent = 20
LVM-Thin 不支持缩容
若迁移后发现虚拟大小过大,不能像 qcow2 那样缩容:
| 存储类型 | 支持缩容 | 方式 |
|---|---|---|
| qcow2 文件 | ✅ | qemu-img resize --shrink |
| LVM 线性卷 | ✅ | lvreduce |
| LVM-Thin volume | ❌ | 不支持 |
只能新建小卷后复制数据:
lvcreate -V 300G -T pve/thinpool -n vm-110-disk-0-new
qemu-img convert -p /dev/pve/vm-110-disk-0 /dev/pve/vm-110-disk-0-new
qm set 110 -scsi0 local-lvm:vm-110-disk-0-new,discard=on,ssd=1
lvremove /dev/pve/vm-110-disk-0
10. 迁移后清理残留卷
迁移完成后,旧存储上的废弃卷可以删除。
# 确认无其他 VM 引用
grep -r "vm-104-disk-0\|vm-104-disk-1" /etc/pve/qemu-server/
# 删除残留卷
lvremove /dev/pve/vm-104-disk-0
lvremove /dev/pve/vm-104-disk-1
4MB 大小的卷通常是 EFI 盘,LVM-Thin 最小分配单位显示为 4M,属正常现象。