尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

KVM虚拟机系统盘扩容实战:从qcow2镜像到文件系统完整链路

KVM虚拟机系统盘扩容实战:从qcow2镜像到文件系统完整链路 1. 整体思路一次把“镜像扩到根目录”的完整链路理清楚KVM 虚拟机调整系统盘大小看似是个简单操作真正做起来却牵涉四层宿主机上的磁盘镜像raw/qcow2、虚拟机里的磁盘设备/dev/vda、分区表、最后的文件系统/dev/vda1 或 vg 里的逻辑卷。很多人折腾半天没成功往往是在某一层卡住了——比如只扩容了镜像分区没动进系统一看根目录还是原来的大小或者分区扩了但 PV、LV、文件系统没跟上df -h 依旧显示旧容量。所以动手之前先把这条链路的四个环节记在脑子里镜像文件扩容让宿主机上的磁盘文件变大对应虚拟机的“硬盘”物理容量变大。分区扩容让虚拟机内的系统盘分区填满新空间让内核识别到新的分区大小。PV/LV 扩容仅 LVM 布局把新增空间划给卷组再分配给根目录所在的逻辑卷。文件系统扩容xfs 用 xfs_growfsext4 用 resize2fs让 df -h 最终生效。这四步缺一不可顺序也不能乱。我习惯把整个流程比喻成“给房子扩建”先把院墙往外推镜像扩容再把房间隔断重新打掉重砌分区扩容最后才能让客厅真正变大文件系统扩容——顺序反了空间就浪费了。另外动手前务必确认两件事一是虚拟机是否使用了 LVM 布局CentOS 7/8 默认安装基本都是 LVMUbuntu 默认安装则常常是普通分区二是文件系统类型CentOS 7 以后默认 xfsCentOS 6、Ubuntu 默认 ext4。这两个信息决定了后面用哪些命令也决定了根目录能不能在线扩容。可以用下面这两条命令在虚拟机内快速摸清现状df -hT / lsblkdf -hT 看文件系统类型和当前使用率lsblk 看磁盘和分区结构。如果 vda1 下面还有 vg 相关的映射那就是 LVM如果直接就是 vda1 挂载到 /那就是普通分区。确认清楚再继续能少走一大截弯路。这篇文章适用的场景包括根目录快满了、业务数据增长快、想给系统盘一次性加容量或者只是想把镜像文件做大的后备容量。无论哪种情况只要按下面这套流程走基本都能平稳搞定。2. 镜像扩容实操raw 格式直接 truncateqcow2 建议用 qemu-img先说宿主机这一层。很多人第一反应是“直接用 qemu-img resize”这个命令确实万能但不同镜像格式的最佳实践不一样我先说结论再讲为什么。2.1 raw 格式用 truncate 还是 qemu-img如果你的虚拟机磁盘是 raw 格式最省事的做法其实是# 先看当前镜像大小 qemu-img info /var/lib/libvirt/images/vm1.img # 注意必须确保虚拟机已关机 virsh shutdown vm1 # 扩容 50G truncate -s 50G /var/lib/libvirt/images/vm1.img # 确认结果 qemu-img info /var/lib/libvirt/images/vm1.imgtruncate 的原理是直接扩展文件长度新增部分全部是空洞sparse不真正占用宿主机磁盘空间等虚拟机里写数据时才逐步占满。这样做速度极快几乎瞬间完成而且不会破坏原有数据。不过有两个前提必须满足一是虚拟机必须处于关机状态二是镜像文件本身没有快照依赖。如果虚拟机在线时执行文件系统正在写入扩容后可能导致数据不一致。有人会问qemu-img resize 不是也能扩 raw 吗确实可以但 qemu-img 对 raw 格式的扩容本质上也是把文件变长和 truncate 效果一样没必要多绕一圈。相比之下qemu-img 强在能处理 qcow2 这种带元数据的格式。另外注意 truncate 的语法50G 表示在原有大小基础上增加 50G也可以直接写 truncate -s 100G 表示扩大到 100G。我建议一律用 N 这种相对写法避免把原有大小记错导致扩过头或缩错。2.2 qcow2 格式必须用 qemu-img resizeqcow2 格式带 COW写时复制和快照机制文件末尾有元数据直接用 truncate 拉长会破坏镜像结构导致虚拟机无法启动。正确姿势是# 提前关闭虚拟机 virsh shutdown vm1 # 扩容 50G qemu-img resize /var/lib/libvirt/images/vm1.qcow2 50G # 查看镜像信息确认 qemu-img info /var/lib/libvirt/images/vm1.qcow2输出里会看到 virtual size 从原来的值变成了新大小disk size 保持不变说明扩容是在“虚拟层”完成的没有立刻占用宿主机空间。这里我要多说一句qcow2 扩容后新增空间默认也是稀疏的不会导致宿主机磁盘瞬间爆满这算是 qcow2 的一个隐藏好处。2.3 镜像扩容的常见雷区镜像这一层看着简单踩坑的人其实不少。我把遇到过的问题列一下注意扩容前最好先对虚拟机做一次完整备份或者至少备份 /etc 下的关键配置和数据库数据。虽然 resize 本身风险不大但后续分区操作一旦失误有备份就有后悔药。雷区一虚拟机没关机就扩容。raw 格式运气好没出问题qcow2 格式大概率直接损坏。qcow2 扩容过程中如果有写入元数据会错乱虚拟机再启动就起不来了。所以无论什么格式都先关机这是铁律。雷区二快照链没理清。qcow2 如果有快照当前活动层的大小只是整个链的一部分直接对活动层 resize 往往达不到预期效果甚至可能破坏快照结构。遇到这种情况我的建议是先 blockcommit 合并快照再做扩容别在链上一头雾水地操作。雷区三扩容后忘了“通知虚拟机磁盘大小变化”。有些场景下虚拟机磁盘配置是直接写在 XML 里的比如用了 指定了镜像路径但没写 size那 resize 镜像后虚拟机内看到的磁盘大小会自动变化因为 KVM 每次启动都会重新读取镜像头。但如果用了持久化配置且缓存了容量信息可能需要 detach/attach 一次磁盘设备或者干脆重启虚拟机。镜像层搞定后下一步进入虚拟机内部操作。如果你在虚拟机里执行 fdisk -l 看到的磁盘大小已经变了说明这一层成功了。3. 分区层面处理普通分区与 LVM 的两种走法镜像变大只是第一步虚拟机里识别到的 /dev/vda 设备大小变了但分区表还停留在旧状态/dev/vda1 或 vg 都拿不到新增空间。这一节按 LVM 布局来讲因为 CentOS 默认安装基本都是 LVM这也是大家最容易卡住的地方。3.1 先确定分区布局LVM 还是普通分区在虚拟机里执行lsblk fdisk -l /dev/vda如果看到 vda1 下面还有 vg 相关的映射那就是 LVM如果直接就是 vda1 挂载到 /那就是普通分区。这一节先讲 LVM因为 CentOS 默认安装基本都是 LVM这也是大家最容易卡住的地方。es3.2 用 growpart 扩展分区既然根目录在 LVM 里那要做的就是扩展 /dev/vda1 分区让分区表把新增空间纳入进来然后 pvresize 让 PV 感知新空间再 lvextend 给根目录逻辑卷最后 xfs_growfs 生效。第一步是扩展分区。网上很多文章让用 fdisk 把分区删了重建我强烈不建议在生产环境这么搞删错一个数字数据就全没了。更稳妥的方式是用 cloud-utils-growpart 提供的 growpart 命令# 安装工具若无 yum install -y cloud-utils-growpart # 扩展第一个分区把 vda 的所有剩余空间都给它 growpart /dev/vda 1growpart 的原理就是重写分区表把分区结束扇区改为磁盘末尾对已有数据零影响。执行后会有类似 outputCHANGED: partition1 start2048 old: size... end... new: size... end...看到 CHANGED 就说明分区已扩展。如果提示分区表已被占用可以先 partprobe 或重启虚拟机。对系统盘来说我建议直接重启一次让内核完全刷新分区表后面操作更干净。注意如果是最小化安装的 CentOS可能没有 growpart 命令也没有 cloud-utils 这个包名。CentOS 7 里是 cloud-utils-growpart装完就有 growpart 命令。Ubuntu 上是 cloud-guest-utils。别装错包名。3.3 扩展 PV、LV 和文件系统分区扩展完成后依次执行# 让 PV 感知新空间 pvresize /dev/vda1 # 查看卷组剩余空间 vgdisplay # 把卷组剩余空间全部给根目录逻辑卷 lvextend -l 100%FREE /dev/centos/root # 扩展文件系统CentOS 7 默认 xfs xfs_growfs /这里有个关键点lvextend 的目标逻辑卷路径要从 vgdisplay 或 lvs 里确认不同系统卷组名不一样CentOS 7 常见是 centosCentOS 8 可能是 clUbuntu 自定义安装可能是 ubuntu-vg。一定先用 lvs 看清楚再下手。xfs_growfs 和 resize2fs 的区别是xfs 文件系统只能在线扩展不能缩小ext4 既可以扩也可以缩。所以执行前先用 df -hT / 确认文件系统类型。xfs 用 xfs_growfs /ext4 用 resize2fs /dev/mapper/centos-root。命令用错了会报错但这还不是最麻烦的最麻烦的是明明命令对了df 还是没变——这种情况九成是因为 PV 没扩LV 没跟着扩或者扩了但文件系统没刷新。全部执行完df -h / 应该能看到根目录变成了扩展后的容量。建议再用 lsblk 对比一下 vda、vda1、vg 的容量是否一致确认每个层级都吃到了新增空间。3.4 如果根目录是普通分区非 LVM不是所有系统都走 LVM。有些模板或自定义安装直接把 /dev/vda1 挂到 /文件系统是 xfs 或 ext4。这种情况不需要管 PV/LV分区扩展后直接扩文件系统growpart /dev/vda 1 # xfs xfs_growfs / # ext4 resize2fs /dev/vda1流程短了一半但容易踩的坑一点不少。比如 Ubuntu 20.04 的默认安装有时候是 LVM有时候不是必须 lsblk 看清楚再决定走哪条路再比如云镜像cloud image很多默认把根分区做成了 ext4用 resize2fs 时要确保分区已经扩展完成否则会提示 nothing to do。4. 实操过程中的完整记录与验证光给命令不够我把一次完整的实操记录放在这里。我的测试环境是宿主机 CentOS 7 跑 libvirt虚拟机 CentOS 7.9磁盘镜像 /var/lib/libvirt/images/centos7-test.qcow2原始大小 20G根目录已用 15G需要扩到 70G。4.1 宿主机侧操作记录# 查看镜像当前状态 qemu-img info /var/lib/libvirt/images/centos7-test.qcow2 # virtual size: 20G, disk size: 6.8G # 关闭虚拟机 virsh shutdown centos7-test # 等待虚拟机完全关机 virsh list --all # 确认状态为 shut off # 扩容 50G qemu-img resize /var/lib/libvirt/images/centos7-test.qcow2 50G # 确认扩容结果 qemu-img info /var/lib/libvirt/images/centos7-test.qcow2 # virtual size: 70G (75161927680 bytes) # disk size: 6.8G宿主机实际占用不变仍是稀疏文件注意 disk size 没变这个细节很关键——它说明新增 50G 是“纸面容量”不会立刻把宿主机 /var/lib/libvirt/images 所在分区塞满。但这种“纸面容量”也会带来一个副作用虚拟机里如果实际写入 50G 数据宿主机磁盘会被真正吃掉。所以扩容之前先看看宿主机剩余空间别扩完就后悔。4.2 虚拟机内操作记录启动虚拟机登录系统先摸底df -hT / # Filesystem Type Size Used Avail Use% Mounted on # /dev/mapper/centos-root xfs 17G 15G 2.0G 88% / lsblk # vda 70G # └─vda1 20G # └─centos-root 17G看到 vda 已经变成 70G但 vda1 还是 20G这就是“镜像层成功、分区层未动”的典型状态。接下来按顺序处理# 安装 growpart yum install -y cloud-utils-growpart # 扩展分区 vda1 growpart /dev/vda 1 # CHANGED: partition1 start2048 old: size... end... new: size... end... # 刷新分区表保险起见也可以直接 reboot partprobe # PV 扩容 pvresize /dev/vda1 # Physical volume /dev/vda1 changed # 1 physical volume(s) resized or updated / 0 physical volume(s) not resized # 确认卷组剩余空间 vgdisplay # Free PE / Size 12799 / 50.00 GiB # LV 扩容 lvextend -l 100%FREE /dev/centos/root # Logical volume centos/root successfully resized. # 扩展文件系统 xfs_growfs / # meta-data/dev/mapper/centos-root isize512 agcount4, agsize... blks # data blocks changed from ... to ... # 验证 df -hT / # Filesystem Type Size Used Avail Use% Mounted on # /dev/mapper/centos-root xfs 67G 15G 52G 22% /df 显示 67G 而不是 70G因为 LVM 默认会有少量元数据和 PE 对齐损耗这是正常的。如果你把 70G 全分给根目录可用容量在 67G 左右别觉得是出了问题。4.3 在线扩容还是离线扩容上面我的记录里全程没有重启虚拟机只有第一次 growpart 之后用 partprobe 刷新了分区表。实际上对系统盘 vda1 来说partprobe 不一定百分之百生效因为内核可能持有旧的分区表。如果 partprobe 后 pvresize 报“找不到设备”或者识别大小不对不要死磕直接重启虚拟机再继续。重启一次不丢人死磕半天才是浪费时间。顺带说一句如果你用的是 virtio-blk 驱动有些内核版本支持在线重新读取分区表partx -a /dev/vda这个命令能把新分区表注册到内核比 partprobe 对 virtio 盘更可靠。我实测下来CentOS 7 上 partx -a 成功率高于 partprobe值得优先尝试。5. 常见问题与排查技巧实录这部分是整篇文章的精华全是实际操作中碰到的硬问题。5.1 df -h 显示容量没变这是问得最多的问题。按这个顺序排查lsblk 看 vda 是否是新大小——如果 vda 还是旧容量说明镜像扩容没生效回去查 qemu-img resize 是否执行成功、虚拟机是否重启过。lsblk 看 vda1 是否是新大小——如果 vda1 还是旧容量说明分区没扩重新执行 growpart 或 partx -a。pvs 看 PV 是否识别新容量——如果 PV 还是旧容量说明 pvresize 没生效多半是分区表没被内核刷新重启虚拟机。lvs 看 LV 是否扩大——如果 LV 还是旧容量说明 lvextend 没执行或目标选错。最后看文件系统——df 没变但 lvs 已经变大那就是缺了 xfs_growfs 或 resize2fs。这几步可以收敛到 80% 的问题。剩下的 20% 往往是对着 LVM 路径写错了比如系统里卷组名不叫 centos 而叫 vg_rootlvextend 找不到路径就直接报错。5.2 扩容后虚拟机启动失败如果 qemu-img resize 之后虚拟机起不来第一反应查日志tail -n 50 /var/log/libvirt/qemu/vm1.log journalctl -u libvirtd -n 50常见原因有三类一是 qcow2 镜像存在快照resize 时没合并快照导致元数据异常二是扩容过程中宿主机磁盘写满镜像损坏三是镜像文件权限被改qemu 进程读不了。第三类很少见但 vda 设备在虚拟机里消失时先检查 /var/lib/libvirt/images 下文件的属主是不是 qemu:qemu。5.3 ext4 扩容时 resize2fs 报错Couldnt find valid filesystem superblock.多半是因为你对着整个磁盘 /dev/vda 执行 resize2fs而不是对分区 /dev/vda1 或 LV。文件系统是建在分区/LV 上的不是建在整个磁盘上的。这个报错对新手特别有迷惑性记住resize2fs 的对象永远是文件系统所在的那个分区设备。5.4 为什么扩容后虚拟机里看到的磁盘大小没变但宿主机的 qemu-img info 已经变了这通常发生在「虚拟机没有完全关停」的情况下比如 virsh shutdown 后系统还在软关机流程中你又执行了 resize。更隐蔽的情况是虚拟机有内存态比如用了 virsh managedsave保存了运行状态重启后会从快照恢复设备状态。解决办法先 virsh undefine 前把内存状态清掉或者干脆 virsh destroy 强制关机确保数据已落盘再做 resize。5.5 宿主机空间不够导致扩容失败qcow2 resize 虽然看起来瞬间完成但如果有块分配底层需要临时空间。更常见的是后面虚拟机里写入数据时直接把宿主机磁盘写满导致整个 hypervisor 出问题。所以扩容前必查df -h /var/lib/libvirt/images我的习惯是预留宿主机磁盘至少 30% 空闲再操作。如果不够先清理旧镜像、删快照或者用 qemu-img 做一次离线压缩把磁盘瘦身。5.6 我对这套操作的核心体会做完一次全流程扩容后你会发现 KVM 扩容本身不复杂难点全在“层次是否清晰”。从镜像文件到分区表到 PV/LV 再到文件系统每一层都有各自的“容量感知”任何一层没跟上下一次层最终 df -h 就是不对。所以我不建议记一堆死命令而是建议理解这条链路然后在每一步完成后用 lsblk、df -h 对照检查形成“检查-前进-再检查”的习惯。另一个体会是能在线扩就绝不离线扩。尊重正在运行的服务先关机再动镜像层虽然最稳妥但很多生产环境经不起长时间停机。好在这一套流程中唯一真正需要关机的是镜像层操作后面的分区、LV、文件系统全部可以在线完成。如果条件允许把扩容尽量放在业务低峰期同时准备好回滚方案心里就不慌。最后分享一个小技巧如果你做完了所有操作但 df 显示很怪比如某个文件系统显示 67G 但另一块盘也变 67G不要慌用 df -hT 看清挂载点和设备路径再用 lsblk 对一遍链路问题基本一目了然。KVM 扩容这事上手一次终身受益以后系统盘根目录再满你就能淡定处理了。
返回列表