引言
这是一篇短文,记录一下 KVM 硬盘扩容,需要在 host 和 VM 中所做的操作,因为其中有一些坑害得我 google 了好久,因此记下来备忘。另一方面,关于 KVM 硬盘扩容的文章数不胜数,但方法各异,让人眼花缭乱,这里也算把我觉得比较理想的完整方案做个展示,从而不需要在不同方案间“博采众长”,反而出现奇怪的问题。
注意本文讲述的是对于虚拟机启动盘硬盘对应镜像文件的直接扩容方案,并且 host 和 VM 都没有设置 LVM 的存储中间层。最终效果是虚拟机的根目录存储空间变大,而没有增加新的分区。而非添加硬盘或硬盘分区并在虚拟机中进行 mount 的方案,后者非常直接,这里不予讨论。
备份
- 在进行扩容操作前,请先备份数据,保存快照!
- 在进行扩容操作前,请先备份数据,保存快照!
- 在进行扩容操作前,请先备份数据,保存快照!
重要的事情说三遍。
host 中的操作
请先将待操作的虚拟机(假设 domain 名为 master)关机,sudo virsh shutdown master
,并等待一定时间,确保其已完全关机,同时请注意确认待扩容的硬盘镜像,没有和其他虚拟机有关联。现在也有一些虚拟机开机状态的在线扩容方案1,但是这里就不冒这个险了。总之我们需要保证在 host 中对镜像文件扩容时,没有其他程序(虚拟机)在对该文件做 IO,否则很容易造成 disk corrupted。表现为再次进入虚拟机系统后,大部分操作都会报错 -bash: Input/output error
,这就说明对应的硬盘已经损坏。
首先,我们需要在 host 主机中找到 master 虚拟机对应的硬盘镜像,sudo virsh domblklist master
可以查看对应的硬盘镜像位置。一般来讲如果路径是 /var/lib/libvirt/images/
开始的话,对应的 image 都在 default pool 里。这可以通过 sudo virsh pool-list
查看现有的硬盘池,并且通过sudo virsh vol-list <pool-name>
找到对应的镜像的 vol 和 pool 名。扩容命令为 sudo virsh vol-resize <imagename.qcow2> --pool <poolname> 12G
,扩容后可通过sudo virsh vol-info <imagename.qcow2> --pool <poolname>
查看硬盘信息,可以发现 Capacity 空间大小已经变化。这之后 sudo virsh start master
重启虚拟机。
VM 中的操作
真正的坑都在 VM 之中,重启虚拟机并 ssh 连接后,df -h
发现一切如常,并没有自动扩容。但是sudo hdparm -I /dev/sda|grep "device size"
可以发现,硬盘的大小确实变大了,因此我们需要重新调整分区。可以将新增的空间分成一个新分区,并 mount 在 VM 上,这里我们讨论另一种更自然的想法,即把根文件系统自动扩容到整个变大的硬盘上。本文实验环境的虚拟机没有单独的 swap
分区,不然很可能 swap 分区卡在硬盘中间某个地方,需要先移位,才能顺利的将/
文件分区一直延伸到盘的结尾。这一问题解决的总体思路当然是使用 fdisk,事实上最好使用 gdisk (GPT fdisk)来操作,其操作交互方式和 fdisk 基本一致,但可能对 GPT 分区表更友好一些。因为虚拟机默认的是 GPT 分区,分区类型可通过 sudo fdisk -l|grep "type"
查看。大体的重新分区的操作思路,可以查看这篇2。但是这里边有些坑,很少有教程提到,下面开讲重点。
sudo fdisk /dev/sda
进入fdisk 交互界面,fdisk 系列的好处就是所有的分区修改操作都只停留在内存里,只要不输入 w
写入修改,就总可以 q
退出,装作什么都没发生过。在 fdisk 界面输入 F
查看未分区的硬盘空间,会发现显示的数值为0或者极小(几kb)(或者按 F 之后,fdisk 直接报错退出 fdisk: libfdisk/src/table.c:416: new_freespace: Assertion end > start failed.)。如果不信邪,可以 n
创建一个新分区试试,其 lastsector 果然就只有这么大,我们根本无法利用新扩容的空间来分区。这个问题需要在 gdisk 解决,sudo gdisk
进入 gdisk 交互,输入驱动文件名 /dev/sda
,输入 v
检查硬盘,之后果然报错,还有建议。
Problem: The secondary header's self-pointer indicates that it doesn't reside
at the end of the disk. If you've added a disk to a RAID array, use the 'e'
option on the experts' menu to adjust the secondary header's and partition
table's locations.
Identified 1 problems!
这段话是什么意思呢,可以参考3。简单说,GPT 分区表信息会在硬盘的开始和结束位置都存一份,防止硬盘坏道之后,无法读取硬盘信息。而由于扩容,以前存在硬盘结尾的 GPT 分区信息现在变到了新盘的中间位置,而所有的基于 GPT 的分区工具,都默认只能分到结尾 GPT 分区信息为止,不然就把该信息给覆盖了。这种情况在真实硬件中,只会发生在 RAID 这一中间层存在的情况,这也是错误提示的内容。因为不是 RAID 的话,很难想象,一个真实的硬盘容量凭空变大了。而在虚拟机中,硬盘硬件容量大小的改变则是一件非常随便的事情。因此我们要做的,是把现在处在硬盘中间的分区信息移到硬盘的新末尾,在 gdisk 中就是先 x
开启高阶选项,再 e
把该信息移到盘尾,最后 w
保存。解决了这个大坑,之后我们就可以如下愉快的重新进行分区了。
$ sudo fdisk /dev/sda
Welcome to fdisk (util-linux 2.31.1).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.
Command (m for help): F
Unpartitioned space /dev/sda: 4 GiB, 4295998976 bytes, 8390623 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
Start End Sectors Size
16775168 25165790 8390623 4G
Command (m for help): d
Partition number (1,2, default 2): 2
Partition 2 has been deleted.
Command (m for help): n
Partition number (2-128, default 2): 2
First sector (4096-25165790, default 4096):
Last sector, +sectors or +size{K,M,G,T,P} (4096-25165790, default 25165790):
Created a new partition 2 of type 'Linux filesystem' and of size 12 GiB.
Partition #2 contains a ext4 signature.
Do you want to remove the signature? [Y]es/[N]o: N
Command (m for help): w
The partition table has been altered.
Syncing disks.
注意我们为了实现将 sda2 分区扩容,先删除了该分区,又重新添加,千万要注意起始 sector 和以前对齐(默认对齐)。至此我们就实现了一个更大的 sda2 分区。
然而 df -h
发现文件系统大小还是没变。因为我们只是将分区扩容,其上面的文件系统还保持着原状。为了解决这一问题,首先sudo partprobe
更新系统的分区信息。之后使用 sudo resize2fs /dev/sda2
则可自动将文件系统扩容到整个分区的大小,注意该命令仅支持 ext 系的分区。此时再 df -h
发现我们的分区终于扩容完成,全剧终。