KVM 克隆 Ubuntu 虚拟机一个总算有解的 grub PV/VG UUID 问题

这件事情折腾有一段时间了。 virt-clone 然后 virt-sysprep 好的 Ubuntu 虚拟机因为 /boot/grub/grub.cfg 里面的 VG UUID 没有被修改, 导致虚拟机不能启动,必须手工操作。 笨办法是挂接 Rescue CD, 取到当前的 UUID,更新 grub.cfg 文件,稍微好一点的办法是用 NBD 方式直接挂接虚拟机映像,用同样的办法。

实际上 KVM 有自己的 virt-rescue 命令, 直接可以通过 libguestfs API 来和虚拟机映像通信的。 所以,新的办法是:
# sudo virt-rescue -d guest-domain 或者 -a guest-disk.qcow2
<rescue> mkdir /mnt
<rescue> vgchange -ay
<rescue> mount /dev/rootvg/root /mnt
<rescue> for i in /dev /dev/pts /proc /sys /run; do mount -B $i /mnt$i; done
<rescue> chroot /mnt
<rescue> grub-install /dev/sda
<rescue> update-grub
以上是更新 grub 的办法, 实际上我们后面要讲到 virt-sysprep 我们会绕过 pv/vg UUID 的初始化,但是为了实现完整的初始化过程,我们需要在 virt-sysprep 后,生成新的 PV/VG 的 UUID,步骤是:
<rescue> vgchange -an rootvg
<rescue> pvchange -u /dev/sda1
<rescue> pvdisplay
我们可以看到 UUID 已经更改了。

同样的,对 vg 做修改:
<rescue> vgchange -u rootvg

所以, 以上其实做了一个倒叙,理论上应该是先修改 UUID,再更新 grub。

下面我们介绍 virt-sysprep 的一些命令行参数。

虚拟机的克隆其实是非常简单的一个事情,下面是 shell 里的一个克隆函数:
-o 就是老的 domain , -n 就是新的 domain, -f 就是新的虚拟机磁盘映像文件

做完克隆以后,我们做 virt-sysprep,函数是这样写的:

检测虚拟机操作系统类别用了 virt-inspector 后面加 –no-applications 不输出安装的软件包,我们用了 xmllint 来取出输出的 xml 里操作系统类别的那个 distro 值,用 sed 去掉 xml tag. 如果 distro 这个值是 ubuntu 的话,我们就执行特别的一些操作,看 operations 里的列表,前面有减号的就是不要操作的,其中 –lvm-uuids 就是不重新生成 pv/vg 的 UUID。
如果按照以上 virt-sysprep 参数,直接克隆完成后,这个虚拟机就是直接可以启动的,不存在 grub 问题,但是显然可能会遇到虚拟机 PV/VG UUID 和其他冲突的问题,这就要回到最初需要手工 virt-rescue 初始化 UUID了。

总结: 对事物的认识总是一个进化的过程,重复造轮子是一件可耻的事情。有这么一个 virt-rescue,还有 virt-inspector ,其实还有另外一个 virt-filesystems 可以使用,如果自己还要用 nbd 挂接来做虚拟机的操作,就有点 LOW 了。

However,这件事情的全自动化,依然没有做到。因为 。。。
==============================================
当写到上面的因为时,突然想到是自己的 sysprep 部分函数原来的 –run-command 缺少了 grub-install 造成的,但是每次 sysprep 都是 lvm-uuid 最后才走,所以,我们需要走两次 sysprep,第一次只走 lvm-uuids 的初始化,第二次走其他的 operations ,然后运行 grub-install 和 update-grub ,这样子,一个全自动的流程就搞定了。 上完整的脚本:

部分脚本代码:(非完整代码)

echo “正在检查原始虚拟机的操作系统”
OS=$(sudo virt-inspector -a ${OLD_DISK} –no-applications |xmllint –xpath /operatingsystems/operatingsystem/distro -|sed -e ‘s/<[^>]*>//g’)
if [ “$OS” = “ubuntu” ]; then
echo “Ubuntu 系统先初始化 LVM UUID …”
sudo virt-sysprep –operations lvm-uuids -a ${DISK}
echo “重新运行初始化其余部分,设置主机名为: ${HOST} “
sudo virt-sysprep -a ${DISK} \
–hostname ${HOST} \
–ssh-inject ${user}:file:$key \
–operations ssh-hostkeys,defaults,-lvm-uuids,-puppet-data-log,-samba-db-log,-rhn-systemid,-rh-subscription-manager,-yum-uuid \
–run-command ‘/usr/sbin/grub-install /dev/sda’ \
–run-command ‘/usr/sbin/update-grub’

作者: 甬洁网络

--移动互联网&物联网技术提供商