☰
KVM虚拟机在线磁盘扩容:从qemu-img到文件系统的完整实践指南
2026/10/14 13:09:05 网站建设 项目流程

1. 在线扩容前必须想清楚的三件事

在 KVM 虚拟化环境里,磁盘扩容是个绕不开的日常操作。业务跑着跑着磁盘满了、日志分区告警、数据库文件增长超预期,这些都是最常见的触发场景。而"在线扩展"这个词听起来很美好——不关机、不停业务、热插拔式地把空间加上去,实际上也确实能做到,但里面藏着不少细节。这一篇是系列的第二篇,上一篇讲过离线扩容的基本操作和磁盘格式的基础概念,这篇专门把"在线"这条链路从头到尾拆开讲透。

拿到一个扩容需求时,先别急着敲命令。我自己的习惯是先在脑子里过三件事:宿主机上的磁盘镜像文件是什么格式、客户机里的分区表是什么类型、文件系统是什么类型。这三个问题直接决定了后面每一步该怎么做、哪些能做、哪些绝对不能做。

怎么查?宿主机上一条命令就够:

# 查看虚拟机磁盘镜像信息 qemu-img info /data/kvm/images/web01.qcow2 # 查看KVM虚拟机的当前配置 virsh dumpxml web01 | grep -A 5 disk

客户机内部用下面这几条:

# 查看所有块设备 lsblk # 查看分区表类型(dos表示MBR,gpt表示GPT) fdisk -l /dev/vda # 查看文件系统类型和挂载情况 df -hT blkid /dev/vda2

为什么要先查清楚这三件事?因为在线扩容并不只是把虚拟磁盘文件变大一点而已。你在宿主机层面对镜像扩容之后,客户机里的操作系统需要重新扫描总线、识别新容量,然后分区和文件系统也要各自扩展一遍。这个链条上任何一个环节的类型不匹配,都会导致扩容失败,甚至造成数据损坏。下面逐个展开。

2. 宿主机层镜像扩展:qemu-img resize 背后的原理与格式差异

2.1 raw 格式和 qcow2 格式的扩容差异

在线扩容的第一步,是在宿主机上把虚拟磁盘镜像文件变大。用得最多的是qemu-img resize命令:

# 将web01的磁盘扩容到100G qemu-img resize /data/kvm/images/web01.qcow2 100G

如果是 raw 格式:

qemu-img resize /data/kvm/images/web01.raw 100G

看起来不过是一句话,但这里藏着一个很关键的细节:raw 格式扩容,新增长的空间是全零填充的;qcow2 格式扩容,新增空间是稀疏的(实际并不占用宿主机物理磁盘)。所以 raw 格式扩容之后要注意宿主机上真实磁盘空间是否够用,qcow2 则完全不用操心这个问题,新加的空间只有在客户机真正写入数据时才会一点点占满宿主机空间。这也是生产环境里我更推荐 qcow2 的原因之一,灵活性确实高很多。

另一个容易踩的坑是:qemu-img resize支持三种单位,G、T这类大写单位是二进制换算,1G = 1024M;小写g则是十进制换算,1g = 1000M。虽然多数情况下你不会为了几 M 的差别纠结,但如果在脚本里自动化扩容,这个细节会让最终容量和预期差出一截,建议统一用大写单位。

2.2 扩容后宿主机侧需要确认的两件事

镜像文件扩容完成之后,别急着进客户机操作。先做两件事:

第一,确认虚拟机的 XML 配置里的磁盘容量没有被写死。如果当初创建虚拟机的时候指定了sda的具体 size,某些版本的 libvirt 可能会记录分配的容量,虽然大多数情况下virsh会自动识别镜像真实大小,但为了保险起见可以用virsh dumpxml检查磁盘总线类型和源文件路径是否正确。

第二,检查块设备是否已经自动识别了新容量。KVM 客户机里的 virtio-blk 驱动正常情况下会自动检测到底层磁盘容量的变化,但这并不是 100% 可靠的。如果客户机里lsblk看到的还是旧容量,可以在客户机里手动触发一次总线重扫,不同发行版方式略有差异,常见的方法是:

# 触发SCSI设备重扫(适用于virtio-scsi) echo 1 > /sys/class/scsi_device/*/device/rescan # 或者使用下面的方式重新读取分区表 partprobe

这一步做完之后,客户机里的lsblk应该就能看到新的总容量了。很多新手在这里犯迷糊:为什么镜像文件明明已经变大,客户机里lsblk却毫无变化?原因就是对 virtio-blk 的自动识别机制理解不深,或者磁盘设备本身处于一个不支持在线重扫的状态。碰到这种情况,必须确认客户机内核已加载virtio_blk模块,且设备名如/dev/vda能被正常枚举。

2.3 一个新规范的在线扩展功能:块设备在线调整

有时你会遇到virsh blockresize这个命令:

# 在线调整虚拟机磁盘大小 virsh blockresize web01 vda --size 100G

这是 libvirt 提供的在线调整磁盘大小的接口,是对qemu-img resize在虚拟机运行时状态下的补充。实际上,virsh blockresize不仅调整镜像文件的逻辑大小,还会通知 QEMU 在线调整块设备大小,效率更高,尤其是配合raw或qcow2时可以省去手工重扫的步骤。它和qemu-img resize的区别在于:后者是纯镜像文件操作,不关心虚拟机是否在运行;前者则要求虚拟机在线,并且调整的是运行中的设备。两者并不冲突,我通常的做法是:如果虚拟机在运行,就先用virsh blockresize在线调整,再用qemu-img info验证明面大小;如果虚拟机是关机状态,直接用qemu-img resize更省事。

3. 客户机内的存储链路处理:从分区到文件系统的完整扩张流程

3.1 分区表扩展:MBR 与 GPT 的分水岭

这一步是整个在线扩容过程中最需要谨慎对待的环节。客户机里的存储链路一般是:磁盘 -> 分区 -> 分区上的物理卷(PV)或者直接是文件系统。你要做的事情是"沿着一整条链路把空间逐级放大",顺序不能乱:先扩底层磁盘容量,再扩分区容量,最后再扩文件系统。

先说分区表类型。如果你在客户机里执行fdisk -l /dev/vda,输出末尾有Disklabel type: dos,那就是 MBR 分区表;如果是gpt,则是 GPT。MBR 分区表有一个众所周知的上限——支持的最大分区大小约2TB,这不仅是理论值,实际操作中 MBR 也没法管理超过 2T 的单分区。所以如果磁盘扩容之后的总空间超过 2T,而你还在用 MBR,那就不是"扩"的问题了,得考虑迁移到 GPT。在线把 MBR 转成 GPT 是非常危险的操作,基本等同于重做分区,需要在业务低峰期、严格备份的前提下进行。

MBR 分区表在线扩容的命令(假设要扩展 /dev/vda2):

# 使用fdisk交互式操作:删除分区记录、重新创建、但保持起始扇区不变 fdisk /dev/vda

过程比较繁琐,而且一旦开始扇区号写错,分区表就等于毁了。更推荐的方式是用parted或者growpart(cloud-utils-growpart 包提供):

# growpart会自动增长分区到磁盘末端 growpart /dev/vda 2 # 或者parted方式(也可以用resizepart) parted /dev/vda resizepart 2 100%

growpart 2的数字是分区号,不是设备号,注意别搞错。这个操作的本质是:删除旧分区记录,在相同起始扇区下重新创建分区,并将结束扇区延长到新磁盘容量。它不是"无损追加",而是"有限度的重写分区表",只要起始扇区不变、分区类型不变,数据就不会缺席。但风险依然很高,任何意外断电都可能导致分区表损坏,所以生产环境建议先做好备份,理解并接受这个操作的风险等级。

GPT 分区表也是用growpart或parted来扩展,不过 GPT 在设计层面对大磁盘的支持比 MBR 好很多,没有 2T 的硬上限,在线扩展的稳定性和成功率也更高。如果你的客户机是近几年创建的系统,大概率已经用了 GPT。

3.2 LVM 层的处理逻辑

分区扩展完成之后,接着看 LVM。如果当初装系统时用的是 LVM 布局(CentOS/RHEL 系的默认选项),那你需要先确认分区下的 PV 有没有识别到新空间:

# 检查PV是否已扩大到分区大小 pvdisplay pvs

如果没有自动识别,可以手动执行pvresize让它把新空间合并进物理卷:

pvresize /dev/vda2

此时vgs会看到卷组的可用空间增加了。然后把空间分配给逻辑卷:

# 将根分区所在逻辑卷扩展到全部剩余空间 lvextend -l +100%FREE /dev/mapper/centos-root # 或者指定扩展到具体大小 lvextend -L +20G /dev/mapper/centos-root

扩完逻辑卷后,还需要最后一步:扩展文件系统。这一步很多人会漏掉,导致明明"分区变大了"、df -hT却还是老样子。文件系统扩容命令取决于文件系统类型:

  • ext4:resize2fs /dev/mapper/centos-root
  • xfs:xfs_growfs /(注意xfs_growfs传的是挂载点或者块设备都行)

一个关键点,xfs 只支持扩大,不支持缩小。所以如果你的根分区当初建得过大,现在想"回缩"一部分给其他逻辑卷,xfs 是做不到的,只能做文件系统迁移或重建。ext4 虽然支持缩小,但缩小的操作同样风险极高,我从不建议在在线状态下做缩小操作,迫不得已时要停业务、卸载文件系统、在只读状态下操作,并且做好完整备份。

3.3 非 LVM 场景的文件系统扩展

很多纯模板镜像或者容器类系统喜欢直接用分区挂载文件系统,不引入 LVM 这一层(比如很多 Debian/Ubuntu 云镜像默认就是如此)。这种场景的分区扩完之后,不需要pvresize,但文件系统的扩容命令还是需要的:

# 扩展挂载在 / 上的ext4文件系统 resize2fs /dev/vda2 # 扩展挂载在 /data 上的xfs文件系统 xfs_growfs /data # 直接指定设备也可以,但某些系统版本要求传挂载点 xfs_growfs /dev/vda3

如果是 btrfs,则直接:

btrfs filesystem resize max /

一次到位,非常方便。如果你和数据盘打交道比较多,大概率会遇到 ext4 和 xfs 这两个主流选手。我的经验是:ext4 的 resize2fs 在扩展和收缩上都比较稳定,xfs_growfs 只做扩张时也很可靠,但两者都不支持跨分区扩展,也就是说不能直接从相邻分区借空间给当前文件系统,那是 LVM 才有的能力。

4. 在线扩容的完整性验证与常见坑的复盘

4.1 扩容后的验证指标体系

链路都走完之后,别急着收工。我给自己定了一套验证清单,每次扩容后按顺序跑一遍,缺一不可:

# 1. 确认磁盘总容量 lsblk # 2. 确认分区容量 fdisk -l /dev/vda # 3. 确认PV/LV容量(如果有LVM) pvs && vgs && lvs # 4. 确认文件系统识别到的新空间 df -hT # 5. 确认文件系统完整性(只读检查) # 注意:xfs_repair不要随便在在线状态下跑,etx4的e2fsck也要小心

df -hT的输出从扩容前的旧值变成新值,这只是最基础的验收。更深入的验证是:在扩容后的目录里实际写入一个大文件,确认空间真实可用,再删掉测试文件。这一步能暴露出很多"显示已经扩容但实际上无法写入"的隐藏问题。

另一个容易被忽略的地方是虚拟机的磁盘 IO 队列深度和缓存模式。如果你的 KVM 虚拟机配置了cache=none或者io=native,在扩容过程中会有额外的约束,但通常不影响在线扩展本身。主要的影响在于性能表现差异,比如扩容后首次集中写入新空间时,raw 格式的稀疏特性可能导致实际写入延迟偏高,或者 qcow2 格式在部分宿主机文件系统上出现块分配延迟,这些在验证时要注意观察是否有明显 IO 波动。

4.2 典型坑位一:分区表未生效,文件系统仍显示旧容量

表现:qemu-img info显示镜像已扩容到 100G,lsblk显示 vda 总容量已经是 100G,但/dev/vda2的大小没变,df -hT也还是旧值。

排查链路:先看分区表是否重新读取了。cat /proc/partitions有没有显示出新的分区大小?如果没有,手动执行partprobe或者partx -u /dev/vda。这两个命令的作用是让内核重新读取分区表,而不是重启系统。partprobe通常用于正式的分区工具修改后触发,partx -u则是直接更新某个块设备的分区信息。

如果partprobe之后还是没生效,多半是虚拟机内部的内核版本或设备驱动在重读分区时受限。此时可以检查 dmesg 里有没有相关的错误信息:

dmesg | tail -20

如果看到"partition table read failed"之类的字样,可能需要重启客户机才能让分区表生效。但在线扩容的场景下重启就失去意义了,所以更推荐在扩容前就确保分区表类型是 GPT 且内核版本较新,避免陷入这种"必须重启才能识别新分区"的困境。

4.3 典型坑位二:LVM 元数据大小和 PE 对齐问题

第二个高频坑是 LVM 的元数据保留区和物理区域大小(PE)设置不当导致空间"浪费"。比如你扩容了 20G 到卷组,但lvextend -l +100%FREE之后发现逻辑卷并没有增加 20G,只增加了 19.5G。这一般是 PE 对齐计算的结果:LVM 的物理区域默认大小是 4MiB,扩展空间会按 PE 对齐,不足一个 PE 的部分无法使用,属于正常现象。

如果空间差额特别大(超过一个 PE 大小),就要检查是不是存在多个 PV 或卷组里还有其他逻辑卷在占用空间。用vgdisplay查看Free PE / Size就能看到剩余可分配空间的具体数值。

还有一种情况是你在扩容时用了lvextend -L +20G,但系统里同时存在快照卷或物理卷碎片,导致空间分配失败。此时建议改为lvextend -l +100%FREE,让 LVM 自动把全部剩余空间都分给目标逻辑卷,省去手动计算偏移量的麻烦。对于在线扩容场景,我倾向直接给满,以后要减再走迁移或其他手段,这样最省心。

4.4 典型坑位三:gcow2 镜像的块分配导致宿主机短时间内 I/O 骤增

很多人在扩容完成、客户机开始往新空间大量写数据时,发现宿主机磁盘 I/O 使用率飙升甚至出现假死。这其实不是扩容命令本身的问题,而是 qcow2 的写时分配机制在作怪。qcow2 新空间默认是未分配的,客户机首次写入某个数据块时,QEMU 需要实时从宿主机文件系统申请物理块并更新 L2 表,这个过程比直接写 raw 镜像多了元数据开销。如果宿主机本身磁盘性能一般或正在跑其他高 I/O 虚拟机,就会出现瞬间卡顿。

应对办法有几种:第一,扩容后用qemu-img map检查块分配情况,必要时可以执行qemu-img convert把 qcow2 转成 raw 使用,但在线场景下不能直接做,需要离线转换或另建新盘迁移,会额外增加不少操作复杂度,所以通常不推荐仅因为一次扩容就转换格式。第二,在业务低峰期执行扩容,避免写入压力叠加。第三,给客户机配置discard或 fstrim 定时任务,让删除的数据块能及时释放回宿主机,减少长期运行中的块碎片。

5. Windows 客户机的在线扩容差异

5.1 Windows 磁盘管理的扩展操作

前面讲的主要是 Linux 客户机,但 KVM 环境里跑 Windows 虚拟机的场景也很普遍。Windows 的在线扩容可以走磁盘管理的图形界面,也可以直接用 PowerShell 脚本化完成。

在 Windows 客户机里,镜像扩容完成后,一般会自动识别新容量,但磁盘分区往往需要手动扩展。打开"磁盘管理"(diskmgmt.msc),如果磁盘总容量已经变大,但分区没有变化,在 C 盘上右键选择"扩展卷",按向导操作即可。

5.2 为什么 Windows 里经常遇到"扩展卷"选项灰色

这是 Windows 在线扩容最坑的地方。Windows 的"扩展卷"要求目标分区后面必须紧跟未分配空间,中间不能有任何其他分区或系统保留分区。很多 Windows 系统盘默认有 100M 的 EFI/系统保留分区,后面才跟着 C 盘,所以 C 盘后面根本没有连续的空闲空间,"扩展卷"选项就是灰色的。

解决办法通常有两种路径:

一是改用第三方分区软件(比如常见的磁盘管理工具),这些工具可以在不删除分区的情况下移动分区边界,把系统保留分区挪到磁盘起始位置,腾出 C 盘后面的连续空间,再执行扩展。但在线移动系统保留分区有引导失败的风险,操作前必须做快照或完整备份。

二是放弃扩展 C 盘的念头,改用"把剩余空间新建为独立分区"的方式,比如 D 盘直接使用剩余空间。这种方式更稳妥,但要注意 D 盘的权限、卷标、以及后续文件路径的组织逻辑。如果 C 盘真的不够用,完全可以在规划时就预留一块独立的数据盘,避免后期在系统盘里折腾。

5.3 Windows 下 NTFS 的在线扩展要点

如果你在 Windows 客户机里通过磁盘管理或者第三方工具成功扩展了 NTFS 分区,理论上是不需要重启的。但有时候扩展卷过程会提示"需要重新启动系统才能完成操作",这通常是因为磁盘上有文件被占用,或者系统正在使用该分区的某些关键文件。

我的建议是:Windows 下在线扩容尽量安排在山业务时段。即使某些场景提示重启,也比 Linux 下多了一步重启成本,但至少不用迁移数据。另外,Windows 扩容完成后建议执行一次chkdsk检查文件系统一致性,特别是经过第三方工具调整过分区边界的情况,这一步不能省。

6. 从一次真实故障反推在线扩容的正确姿势

6.1 故障现象:扩容后客户机无法启动

某次我在测试环境给一台跑着 MySQL 的 KVM 虚拟机扩容,系统是 CentOS 7,根分区是 LVM 上的 xfs。扩容过程走得很顺:qemu-img resise、pvresize、lvextend、xfs_growfs全部执行完毕。我以为万事大吉,结果几分钟后 MySQL 开始疯狂报错,紧接着虚拟机失去响应,最后连 SSH 都进不去。

强制重启后,系统卡在 grub 界面,起不来。

6.2 根因分析:xfs 扩容后的元数据损坏

排查过程很复杂,先是进救援模式检查 xfs 文件系统,用xfs_repair尝试修复,但修复过程本身就异常漫长,说明元数据损坏程度不低。仔细回溯后发现问题出在两个细节上:

第一,我使用qemu-img resize后没有同步确认客户机内部设备重扫成功,直接执行了partprobe,但当时/dev/vda2的内核分区信息其实还停留在旧容量。partprobe在部分内核版本上对"正在使用的磁盘分区"并不是总能生效,分区表直接没更新,我后续的pvresize实际作用在了错误的设备状态上。

第二,xfs_growfs在读取到不一致的设备大小时,把文件系统元数据写到了旧边界之外的区域,导致 xfs 的关键超级块和 AG(分配组)元数据错乱。xfs 虽然以稳健著称,但在"设备容量被外部修改且内部状态未同步"这种时序下,同样会翻车。

6.3 复盘得到的四条铁律

这个事故让我后面做了四次复盘调整,也成了我所有扩容操作的默认守则,你可以直接抄用:

第一,扩容后务必等客户机内部lsblk确认新容量再往下走。没看到新容量之前,不要碰partprobe、不要碰pvresize。如果partprobe不生效,优先用partx -u或者重启客户机。判断标准很简单:/dev/vda的总容量必须是新值,且/dev/vda2的分区记录也要同步更新到新边界,否则后面的一切都没有意义。

第二,xfs 扩容前必须确认文件系统本身没有挂在被占用的挂载点上。比如/home如果被进程持续写入,xfs_growfs 操作时会有竞态,虽然多数情况下没问题,但还是安排在业务写入低峰期执行更安全。

第三,所有在线扩容操作开始前,建议先做一个虚拟机快照。KVM 原生的快照虽然不适合作为长期备份替代品,但作为扩容前的回滚手段非常有效。万一扩容过程中出现问题,一条virsh snapshot-revert就能回到操作前状态,比事后修文件系统省太多时间。

第四,日志先行,命令后置。每次扩容之后,记录下执行过的每一条命令和输出,尤其是qemu-img info前后的输出差异、lsblk前后对比、vgdisplay的 Free PE 变化。下次遇到类似问题时,这些日志就是你排查的第一手线索。

6.4 还是建议你给自己留一条退路

在线扩容确实方便,但"在线"两个字并不代表零风险。任何涉及分区表和文件系统元数据修改的操作,都应该被当成"小型手术"来对待。快照 + 备份 + 低峰期执行,这三条缺一不可。

我自己后来养成的习惯是:给所有 KVM 虚拟机默认启用定期快照策略(按天保留最近 3 个),同时在宿主机上准备一套离线的磁盘检测工具镜像,万一客户机真的起不来,可以用救援模式挂载镜像文件,从宿主机侧直接检查和修复客户机文件系统,不需要依赖客户机本身的网络和 SSH。具体做法是:

# 将qcow2镜像挂载到宿主机本地(需要客户机处于关机状态) sudo modprobe nbd max_part=8 sudo qemu-nbd -c /dev/nbd0 /data/kvm/images/web01.qcow2 sudo partprobe /dev/nbd0 sudo mount /dev/nbd0p2 /mnt/rescue

这套操作虽然看起来有点"绕",但在关键时刻能救回一整台业务机器。

7. 扩展思考:在线扩容之外,磁盘规划还有什么可优化

7.1 虚拟机初始磁盘大小的"适度前瞻"策略

在线扩容能解决问题,但频繁扩容绝不是健康的运维状态。我更建议在创建虚拟机时就根据业务趋势做一定量的空间预留。比如某个日志服务目前每月增长 30G,你给它分配 100G,看着是够半年用的,但实际三个月就见底了,反而不如一开始给 200G,把扩容周期拉长到一年以上。qcow2 格式下,多分配的空间不会实际占用宿主机磁盘,唯一的成本是逻辑容量上限变大,风险可控。建议在 qcow2 里建立合理的"逻辑超额分配"策略,结合宿主机物理磁盘总容量和使用率严格控制,防止发生 "快照写满宿主" 的隐患。

7.2 用 virtio-blk 还是 virtio-scsi

在线扩容的稳定性和虚拟磁盘的总线类型也有关系。virtio-blk 是 KVM 里最常用的块设备类型,简单高效,但在线重扫机制相对粗糙。virtio-scsi 则支持更标准的 SCSI 命令和在线设备添加/移除,在需要频繁扩容、热插拔、以及某些特殊驱动场景下更合适。

如果你在规划新虚拟机,建议直接选择 virtio-scsi,系统会自动映射为/dev/sda之类的设备名,同时配合 SCSI 的 rescan 机制,在线扩容的稳定性会好很多。我手上好几台使用 virtio-scsi 的虚拟机,在线扩容时几乎没有遇到过需要重启的情况。

7.3 在线扩容和文件系统收缩的本质区别

文章里多次说了收缩风险高,这里再展开剖析一句:文件系统的在线收缩需要非常精细的元数据重写,并且需要保证所有块都被安全移动到新边界内,整个过程就是一个大范围的块迁移,一旦中断,后果是灾难性的。在线扩展则是"追加"新空间,原有块分布不变,风险自然低得多。理解了这点,你就知道为什么 LVM 的好处不只是灵活,它能把这种风险隔离在逻辑卷管理层,而不是直接暴露在裸分区上。

所以我在所有新部署的 Linux 虚拟机里,一律默认 LVM + xfs/ext4 的组合。这并不只是因为扩容方便,更是因为当你需要做快照、迁移、甚至跨磁盘复制时,LVM 都能提供一层额外的操作空间。在线扩容只是它众多好处中的一个典型代表。

另外想补一句,无论是 KVM 还是其他虚拟化平台,磁盘扩容这件事的底层逻辑是相通的:宿主机层扩展镜像 -> 客户机层识别新容量 -> 分区层扩展边界 -> 逻辑卷层合并空间 -> 文件系统层吸纳新空间。把这五层链路刻在脑子里,不管平台怎么换,思路都能复用得上。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询