Featured image of post LVM 迁移至 LVM-Thin

LVM 迁移至 LVM-Thin

PVE 双节点集群把 LVM 迁移到 LVM-Thin 的实操记录:缩容 root 建薄池、虚拟机跨节点在线迁移、Windows Trim 配置,以及一次迁移失败的恢复过程

LVM 迁移至 LVM-Thin

场景说明

双节点集群,节点名分别为 pvex370

节点 系统盘 外挂存储 初始状态
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

本文记录的操作:

  1. x370 上移除 swap、缩容 root、创建 LVM-Thin Pool
  2. 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%,说明精简特性在正常工作。

lvsLV 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。决定调整顺序:

  1. 先迁 VM-110(实际 ~276G,较大)→ 在线迁移
  2. 再迁 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,属正常现象。


参考