
1. 在线扩容前必须想清楚的三件事在 KVM 虚拟化环境里磁盘扩容是个绕不开的日常操作。业务跑着跑着磁盘满了、日志分区告警、数据库文件增长超预期这些都是最常见的触发场景。而在线扩展这个词听起来很美好——不关机、不停业务、热插拔式地把空间加上去实际上也确实能做到但里面藏着不少细节。这一篇是系列的第二篇上一篇讲过离线扩容的基本操作和磁盘格式的基础概念这篇专门把在线这条链路从头到尾拆开讲透。拿到一个扩容需求时先别急着敲命令。我自己的习惯是先在脑子里过三件事宿主机上的磁盘镜像文件是什么格式、客户机里的分区表是什么类型、文件系统是什么类型。这三个问题直接决定了后面每一步该怎么做、哪些能做、哪些绝对不能做。怎么查宿主机上一条命令就够# 查看虚拟机磁盘镜像信息 qemu-img info /data/kvm/images/web01.qcow2 # 查看KVM虚拟机的当前配置 virsh dumpxml web01 | grep -A 5 disk客户机内部用下面这几条# 查看所有块设备 lsblk # 查看分区表类型dos表示MBRgpt表示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或者growpartcloud-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却还是老样子。文件系统扩容命令取决于文件系统类型ext4resize2fs /dev/mapper/centos-rootxfsxfs_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 虚拟机配置了cachenone或者ionative在扩容过程中会有额外的约束但通常不影响在线扩展本身。主要的影响在于性能表现差异比如扩容后首次集中写入新空间时raw 格式的稀疏特性可能导致实际写入延迟偏高或者 qcow2 格式在部分宿主机文件系统上出现块分配延迟这些在验证时要注意观察是否有明显 IO 波动。4.2 典型坑位一分区表未生效文件系统仍显示旧容量表现qemu-img info显示镜像已扩容到 100Glsblk显示 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_part8 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 还是其他虚拟化平台磁盘扩容这件事的底层逻辑是相通的宿主机层扩展镜像 - 客户机层识别新容量 - 分区层扩展边界 - 逻辑卷层合并空间 - 文件系统层吸纳新空间。把这五层链路刻在脑子里不管平台怎么换思路都能复用得上。