SSD Nodes Learn Hosting plans →
指南 Matt Connor作者: Matt Connor

如何从 LVM 卷组中安全移除磁盘

使用 pvmove 将数据迁出物理卷,再依次执行 vgreduce 和 pvremove,保持文件系统挂载和服务在线,避免先卸盘导致数据丢失。

从 LVM 卷组中移除磁盘意味着什么

要从 LVM 卷组中移除磁盘,必须先将其数据迁移到其他设备,再将已清空的设备移出卷组,擦除其上的 LVM 标签,最后才能将它从服务器上卸下。LVM(逻辑卷管理器)会将卷组中每块磁盘提供的存储划分为小块并分配出去,因此您认为是“一个卷”的文件系统,可能有部分数据位于即将拔出的磁盘上。默认情况下,LVM 不会为这些数据保留第二份副本。先卸下磁盘会导致原本一分钟前还正常工作的文件系统丢失。

如果文件系统保持不变,只移除磁盘,整个操作可以在线完成。pvmove 会在文件系统已挂载并提供网络流量服务时复制数据。无需重启,也无需进入救援模式。真正有风险的不是技术本身,而是操作顺序,以及这里的每条命令都需要设备路径,并且不会向您进行二次确认。

物理卷、卷组和逻辑卷的实际含义

这三者是同一堆栈中的三个层次。磁盘承载的是哪一层,决定了是否可以“直接卸载”。

  • 物理卷(PV)。 带有 LVM 标签的磁盘或分区。pvcreate 会写入该标签。标签后面的空间会划分为物理区段,即固定大小的块,默认大小为 4 MiB。
  • 卷组(VG)。 由一个或多个 PV 构成的存储池。VG 统一管理其中所有 PV 的区段,不关心某个区段来自哪块磁盘。
  • 逻辑卷(LV)。 从存储池中分配出的存储区域。这是文件系统所在的块设备,通常显示为 /dev/<vg>/<lv> 或 /dev/mapper/<vg>-<lv>。
  • 各层之间的映射才是整个问题的关键。 一个 LV 的区段可以位于组内的任意 PV 上。LVM 也会将单个 LV 分布到多块磁盘上,因为存储池就是用于集中管理空间的。

因此,第一步是确认待移除的磁盘承载了什么内容。共有 4 种情况,所需的操作各不相同。

  1. 磁盘带有 LVM 标签,但从未加入卷组。没有任何对象依赖它。运行 pvremove,然后卸载磁盘。
  2. 磁盘属于某个卷组,但没有承载已分配的区段。运行 vgreduce,然后运行 pvremove,再卸载磁盘。
  3. 磁盘承载了仍需保留的逻辑卷中的区段。必须先将这些区段迁移到其他 PV,这正是 pvmove 的用途。
  4. 逻辑卷将随磁盘一同移除。此时应先卸载它,并在运行 lvremove 前删除其 /etc/fstab 行,否则下次启动会停在紧急提示符处。

普通 LVM 不会自动创建镜像,除非你明确创建了镜像逻辑卷或 RAID 逻辑卷。因此,如果属于第 3 种情况的磁盘已经被卸载,这就不是移除流程,而是从备份恢复。如果你仍在规划变更,建议先重新阅读附加块存储卷并将其加入卷组。磁盘最初是如何加入的,决定了将其移除时需要执行多少操作。

在输入命令前确认每个设备的对应关系

先列出计算机上的设备,并让 LVM 报告它将哪些设备视为物理卷。

lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS
sudo pvs

然后将这些路径一次性设置为 shell 变量。继续执行其他操作前,将引号中的文本替换为上述列表中的实际路径。

VG="the volume group name from pvs"
OLD="the path of the disk that is leaving"
NEW="the path of the replacement disk"

在每个破坏性步骤前,打印这些变量,并对照 lsblk 列表逐项读出。来自同一提供商且大小相同的两个卷,最容易导致误操作。如果无法确定某个路径指向哪个物理设备,请将设备的型号和序列号与目标设备进行匹配,不要依赖内核发现设备的顺序。

大多数 LVM 命令都接受全局 dry run 标志 --test。该标志会计算变更并报告结果,但不会写入元数据。在执行实际命令前,先为每个破坏性步骤使用该标志进行演练。请在您自己的系统上通过 man pvmove、man vgreduce 和 man pvremove 确认该标志,因为不同 lvm2 版本的选项列表可能不同。

同时备份卷组描述。sudo vgcfgbackup 会将当前布局写入 /etc/lvm/backup/,vgcfgrestore 可以恢复损坏的描述。它只能恢复元数据,无法恢复磁盘故障后丢失的数据。

第 1 步:确认此 PV 是否仍包含 extents

这是决定后续所有操作的问题。因此,不要依赖几个月前的记忆,而应直接检查磁盘。

sudo pvs -o pv_name,vg_name,pv_size,pv_free,pv_used
sudo pvdisplay "$OLD"

从输出中读取两项信息。首先,检查 $OLD 中的路径是否显示了卷组名称。卷组列为空,表示该设备是一个从未被任何卷组使用的 PV,这属于情况 1。其次,检查已使用的空间。可用空间等于总大小,表示此设备没有分配任何内容,因此可以直接跳到第 4 步。只要存在已分配空间,就说明逻辑卷的 extents 位于此设备上,必须将它们迁移。pvdisplay 以 PE(physical extents)为单位显示相同信息,包括已分配数量和可用数量。

接下来,确认涉及哪些逻辑卷,以及卷组的其余部分是否能够容纳这些数据。

sudo lvs -a -o lv_name,vg_name,lv_size,seg_pe_ranges
sudo vgs -o vg_name,pv_count,vg_size,vg_free

在 lvs 输出中,segment range 会标出每个 LV 的各个片段所在的 PV。因此,请查找 $OLD 中的路径,并记录它旁边的每个 LV 名称。这些卷在保持挂载状态的同时,会在底层被重新写入。在 vgs 输出中,将卷组的可用空间与 $OLD 中的已分配空间进行比较。如果卷组中的其余磁盘已有足够空间容纳这些数据,则不需要替换设备,可以进入第 3 步。如果空间不足,则需要在第 2 步解决。

在进行上述操作前,建议先完成一项检查:如果更改原因是文件系统已满,请确认这些空间确实正在使用。df 和 du 对已满文件系统的结果不一致 通常表示已删除的文件仍被运行中的进程保持打开状态,迁移存储无法解决此问题。

第 2 步:将卷组扩展到替换设备

在服务提供商处挂载替换卷,确认它对应的设备路径,然后为其写入标签并将其加入卷组。

sudo apt install lvm2   # Ubuntu and Debian ship the tools in this package
sudo pvcreate "$NEW"
sudo vgextend "$VG" "$NEW"
sudo vgs -o vg_name,pv_count,vg_size,vg_free

pvcreate 只写入 LVM 标签,不执行其他操作。vgextend 将该设备的物理区段加入存储池。这两个命令都不会修改现有逻辑卷中的数据,因为只有在明确请求分配空间时,分配情况才会改变。

LVM 接受完整的原始设备,因此 pvcreate /dev/sdb 可以正常工作。在整个磁盘上创建一个分区只需额外执行一个步骤,并会留下其他工具能够识别的签名。这样,下一个运行 lsblk 的人就能看到设备的用途,而不是看到一个未标记的磁盘。对于未来可能扩展到 2 TiB 以上的设备,请使用 GPT 分区表,而不要使用 MBR。替换设备也应预留足够空间,因为 标称大小和工具报告的大小并不是同一个数值,而且 LVM 元数据还会占用少量空间。纸面容量与原设备完全相同的替换设备,其物理区段数量最终可能略少。

该代码块中的最后一个命令会检查第 3 步的前提:卷组当前是否拥有比 $OLD 已分配空间更多的空闲物理区段?在满足此前,pvmove 没有空间写入数据,因此会拒绝执行该操作。

步骤 3:在线且可恢复地使用 pvmove 移动 extents

sudo pvmove --test "$OLD"
sudo pvmove -i 30 "$OLD"

下面说明其工作机制,这也解释了为什么可以安全地操作已挂载的文件系统。对于每个需要移动的 segment,pvmove 会在逻辑卷内创建临时镜像,将 extents 复制到新位置,等待两端同步,然后删除旧端并重写卷组元数据。所有操作都发生在文件系统下方的 device mapper 层,因此文件系统不会看到空缺,应用程序可以持续读写。-i 30 标志每 30 秒打印一次进度。若要自行指定目标,而不是让 LVM 自动选择,请指定目标设备:sudo pvmove "$OLD" "$NEW"。

  • 该操作速度较慢,因为它会以较慢设备的速度复制每个已分配的 extent。对于网络连接的卷,速度受网络限制。请在 tmux 或 screen 中运行它,避免 SSH 会话断开导致进程终止。
  • 该操作会与工作负载争用磁盘 I/O。请在服务器负载较低时启动,或者使用 sudo pvmove -n <lvname> "$OLD" 一次移动一个卷,将负载分散到多个维护时段。
  • 该操作支持断点续作。如果计算机重启或进程在中途退出,请运行不带参数的 sudo pvmove,继续未完成的移动。sudo pvmove --abort 则会取消操作,并使每个逻辑卷继续指向一份一致的数据副本。
  • 某些布局无法移动。当前版本的 lvm2 会拒绝移动用作 writecache 的设备、启用了 integrity 的 RAID 逻辑卷(包括位于 cache 下方的 RAID 逻辑卷),以及正在进行转换或合并的卷。Cache 和 thin pool 布局不在此限制之列,lvm2 自己的测试套件也会移动它们。如果 pvmove 拒绝执行,请阅读它输出的错误,因为 man pvmove 不会列出这些情况。添加 --force 不会使不受支持的布局变为受支持。

完成后,再次执行步骤 1 中关于 $OLD 的检查。该设备上是否仍有已分配的空间?遗留的已分配 extents 表示移动尚未完成,而不是操作已静默失败;不带参数运行 sudo pvmove 即可继续移动。

步骤 4:使用 vgreduce 将设备移出组

sudo vgreduce --test "$VG" "$OLD"
sudo vgreduce "$VG" "$OLD"

vgreduce 会从卷组成员列表中移除该设备,并将卷组元数据重写到剩余的 PV 上。如果该设备仍有已分配的扩展区,该命令会拒绝执行,因为继续操作会截断活动逻辑卷的一部分。请将此拒绝视为安全检查正常生效,然后返回步骤 3。

此时不要使用 vgreduce --removemissing。该选项用于磁盘已经消失的卷组;与 --force 结合使用时,它会删除缺失设备上包含扩展区的所有逻辑卷。这是用于修复已经发生的事故的工具。

步骤 5:使用 pvremove 清除标签,然后分离磁盘

sudo pvremove "$OLD"
sudo wipefs -a "$OLD"
sudo lvmdevices --deldev "$OLD"   # only on systems using /etc/lvm/devices/system.devices

pvremove 会擦除 LVM 标签,使设备不再是物理卷。如果设备仍属于某个卷组,该命令会拒绝执行,因此必须先完成步骤 4。wipefs -a 会清除残留的签名,包括任何分区表,避免磁盘被下一个扫描它的工具识别为状态不完整的设备。对于使用 devices 文件而不是 filter 的 lvm2 版本,还应从该文件中删除此路径,否则文件仍会记录一个已经不存在的设备。

现在在云服务提供商处分离该卷,然后使用 lsblk 和 sudo pvs 确认路径已消失,并确认卷组列表中只剩余仍在使用的成员。由于您先从元数据中删除了设备,再将其从计算机上移除,因此卷组会在下次启动时完整激活,不会产生任何警告。先分离设备则会导致相反的结果:卷组只能部分激活,每次执行 LVM 命令都会出现警告,并且任何在缺失磁盘上包含区段的逻辑卷都将不可用,直到您修复该卷组。

如果要删除逻辑卷,应先处理 fstab

有时磁盘需要移除,是因为其上的数据已经不再使用。此时需要删除逻辑卷,操作顺序比本指南中的其他场景更重要。先卸载文件系统,删除 /etc/fstab 行,然后再执行 lvremove。

sudo umount /path/to/mountpoint
sudo swapoff "/dev/$VG/<lvname>"   # only if this LV was swap
sudo nano /etc/fstab               # delete the line naming this LV
sudo systemctl daemon-reload
sudo lvremove "$VG/<lvname>"

这是因为 systemd 会在启动时将每个 /etc/fstab 行转换为挂载单元。如果该行仍在文件中就删除逻辑卷,单元会等待一个永远不会出现的设备,导致 local-fs.target 失败,启动过程停止在紧急提示符处,并要求在控制台输入 root 密码。对于租用的服务器,这意味着必须先找到服务商提供的控制台,机器才可能恢复网络访问。编辑 fstab 后运行 systemctl daemon-reload,systemd 会立即忘记生成的单元,因此在删除逻辑卷前的几秒内,不会有任何操作尝试重新挂载该卷。

如果 umount 报告文件系统正忙,请使用 sudo fuser -vm /path/to/mountpoint 或 sudo lsof +f -- /path/to/mountpoint 找出占用它的进程,并正常停止该进程。避免使用 umount -l。延迟卸载会将挂载点从命名空间中分离,但打开的文件仍可继续使用。这意味着你可能在某些进程仍向存储写入时就 lvremove 存储。还应检查不明显的路径使用者:/etc/exports 中的 NFS 导出、cron 中的备份和 rsync 任务,以及容器存储,因为 compose 文件中的绑定挂载指向主机路径;逻辑卷删除后,该路径会悄无声息地开始填满根文件系统。

卷组无处存放数据时

如果无法连接替换设备,且其他 PV 没有可用空间,则必须先缩小数据,才能移除该磁盘。这首先是文件系统操作,其次才是 LVM 操作;是否可行完全取决于文件系统。

  • ext4 可以缩小,但只能在卸载状态下进行。卸载卷,在该卷上运行 sudo e2fsck -f,然后运行 sudo resize2fs <lv> <new size>,再运行 sudo lvreduce -L <new size> "$VG/<lvname>"。sudo lvreduce --resizefs -L <new size> "$VG/<lvname>" 会执行这两个阶段,但底层规则相同。自 lvm2 2.03.17 起,它会直接调用 resize2fs,而不再通过 fsadm;后者现在是已弃用的 --fs resize_fsadm 选项。
  • XFS 只能在其最后一个 allocation group 内缩小。xfs_growfs -D 可以从最后一个 allocation group 的末尾裁剪未使用空间,但不能删除该 allocation group,也不能将文件系统缩小到只剩一个 allocation group。除此之外,只能先备份,再创建较小的新逻辑卷,并将数据恢复到其中。

需要特别注意:逻辑卷绝不能小于其中承载的文件系统。先缩小文件系统,再缩小逻辑卷,并让逻辑卷略大于文件系统要求的大小。lvreduce 会要求确认,因为顺序错误会截断文件系统末尾;之后再扩大逻辑卷也无法撤销该操作。逻辑卷缩小后,释放的 extents 会返回卷组,步骤 3 便有空间移动数据。

执行顺序

  1. 使用 pvs、pvdisplay 和 lvs -a -o lv_name,seg_pe_ranges,确认 PV 中存储的内容。
  2. 为卷组腾出空间:可以通过 vgextend 将数据移至替换设备,也可以从其他位置释放物理区段。
  3. 在线 pvmove 物理区段,使其离开待移除设备;此过程可恢复,并使用 --test 进行演练。
  4. 使用 vgreduce 将设备从卷组中移除。
  5. 使用 pvremove 移除标签,并使用 wipefs 清理剩余内容。
  6. 在服务提供商处分离设备,然后使用 lsblk 和 pvs 进行确认。

如果逻辑卷也要移除,前4行应改为:卸载、编辑 /etc/fstab、systemctl daemon-reload、lvremove。

FAQ

可以在不需要块存储卷中的数据时直接将其分离吗?

只有在没有其他对象需要该卷时才可以,而且必须先检查,不能想当然。使用 pvs -o pv_name,pv_used,pv_free 和 pvdisplay 确认该物理卷上是否仍有已分配的物理区段,再使用 lvs -a -o lv_name,seg_pe_ranges 确认这些区段属于哪些逻辑卷。如果其他逻辑卷在该磁盘上仍有区段,分离磁盘会损坏该逻辑卷,因为普通 LVM 不会在其他位置保留物理区段的第二份副本。使用 pvmove 移动物理区段,使用 vgreduce 将设备从卷组中移除,使用 pvremove 清除标签,然后再分离磁盘即可避免问题。

文件系统已挂载时 pvmove 是否可以运行?如果过程中断怎么办?

可以,这正是它的用途。pvmove 会在文件系统下方创建临时镜像,复制物理区段,然后在 device mapper 中将逻辑卷切换到新位置,因此应用在整个过程中都可以继续读写。如果重启或会话断开导致操作中断,请不带参数运行 sudo pvmove,以恢复未完成的移动;也可以运行 sudo pvmove --abort 停止操作,并让每个卷保留在一个一致的副本上。请在 tmux 或 screen 中启动该操作,因为它会复制每个已分配的物理区段,所需时间取决于磁盘速度。

为什么删除逻辑卷后服务器无法启动?

因为文件中仍然存在该卷的 /etc/fstab 条目。systemd 会为 fstab 中的每个条目生成挂载单元。该单元会等待已经不存在的设备,local-fs.target 失败,启动过程最终在控制台进入紧急提示符。恢复时,需要访问服务提供商的控制台或救援镜像,删除过时的条目,然后重启。为避免此问题,应先卸载文件系统,删除 fstab 条目,运行 systemctl daemon-reload,最后再运行 lvremove。在非根数据挂载的 fstab 条目中添加 nofail,可以让下次出现同类错误时系统仍能完成启动。

为什么 vgreduce 拒绝从卷组中移除磁盘?

通常是因为该物理卷上仍有已分配的物理区段,移除它会从正在使用的逻辑卷中截断部分数据。针对该设备使用 pvs -o pv_name,pv_used,pv_free 和 pvdisplay 检查,然后完成或恢复 pvmove,再重试。另一个常见原因是路径拼写错误,指定了属于其他卷组的设备。因此,在每个步骤前将路径设置到 shell 变量中并回显确认,值得花费这两秒。

#lvm#存储#block-storage#linux#disks