一旦你获得了虚拟机镜像,上传到镜像服务之前你可能希望对其进行一些修改。这里我们介绍几个可用的工具,可以让你修改镜像。
警告
不要尝试使用这些工具来修改正在运行的虚拟机镜像。这些工具仅设计用于修改当前未运行的镜像
guestfish
guestfish程序是来自libguestfs项目的一个工具,允许你修改虚拟机镜像中的文件。
注意
guestfish不直接将镜像挂载到本地文件系统中。相反,它提供了一个shell界面,使你能够查看、编辑和删除文件。guestfish的许多命令,例如touch、chmod和rm,类似于传统的bash命令。
guestfish session 的例子
有时候你必须修改虚拟机镜像,以删除在镜像创建时分配给虚拟网络接口卡的MAC地址的任何痕迹。这是因为当虚拟机镜像启动时,MAC地址是不同的。以下示例演示如何使用guestfish通过删除 /etc/udev/rules.d/70-persistent-net.rules
文件和从 /etc/sysconfig/network-scripts/ifcfg-eth0
文件中删除 HWADDR 行来删除旧MAC地址的引用。
假设你有一个名为 centos63_desktop.img 的 CentOS qcow2
镜像。以root身份在读写模式下挂载该镜像,如下:
# guestfish --rw -a centos63_desktop.img
Welcome to guestfish, the libguestfs filesystem interactive shell for
editing virtual machine filesystems.
Type: 'help' for help on commands
'man' to read the manual
'quit' to quit the shell
><fs>
注意
guestfish 提示看起来像一条鱼:><fs>
。
我们必须先在 guestfish 提示符下使用run
命令,然后才能执行其他任何操作。 这将启动一个虚拟机,该虚拟机将用于执行所有文件操作:
><fs> run
现在我们可以使用list-filesystems命令查看镜像中的文件系统:
><fs> list-filesystems /dev/vda1: ext4 /dev/vg_centosbase/lv_root: ext4 /dev/vg_centosbase/lv_swap: swap
我们需要挂载包含根分区的逻辑卷:
><fs> mount /dev/vg_centosbase/lv_root /
接下来,我们想要删除一个文件。我们可以使用guestfish的rm命令,其工作方式与传统shell中的rm命令相同。
><fs> rm /etc/udev/rules.d/70-persistent-net.rules
我们想要编辑 ifcfg-eth0 文件,以删除 HWADDR 行。edit命令将把文件复制到主机,调用你的编辑器,然后再将文件复制回来。
><fs> edit /etc/sysconfig/network-scripts/ifcfg-eth0
如果你想修改这个镜像,在启动时加载8021q内核,你必须在 /etc/sysconfig/modules/ 目录中创建一个可执行的脚本。你可以使用guestfish的touch命令创建一个空文件,使用edit命令编辑它,然后使用chmod命令使其可执行。
><fs> touch /etc/sysconfig/modules/8021q.modules ><fs> edit /etc/sysconfig/modules/8021q.modules
我们向该文件添加以下行并保存:
modprobe 8021q
然后我们设置为可执行文件:
><fs> chmod 0755 /etc/sysconfig/modules/8021q.modules
完成后,我们可以使用exit命令退出:
><fs> exit
继续学习guestfish
guestfish中有大量的功能,完整的介绍超出了本文档的范围。相反,我们建议你阅读guestfs-recipes文档页面,以了解使用这些工具可能实现的功能。
guestmount
对于某些类型的更改,你可能会发现直接在虚拟机中挂载镜像的文件系统更容易。guestmount程序,也来自于libguestfs项目,可以让你这样做。
例如,要将centos63_desktop.qcow2镜像中的根分区挂载到/mnt目录,我们可以执行以下操作:
# guestmount -a centos63_desktop.qcow2 -m /dev/vg_centosbase/lv_root --rw /mnt
如果我们事先不知道在guest中的挂载点是什么,我们可以使用
-i(inspect)
标志,告诉guestmount自动确定要使用的挂载点:# guestmount -a centos63_desktop.qcow2 -i --rw /mnt
一旦挂载成功,我们可以执行像使用 rpm 列出已安装软件包这样的操作:
# rpm -qa --dbpath /mnt/var/lib/rpm
完成后,我们卸载:
# umount /mnt
virt-* tools
libguestfs项目还有其他一些有用的工具,包括:
- virt-edit 用于编辑镜像内的文件。
- virt-df 用于显示镜像内的可用空间。
- virt-resize 用于调整镜像大小。
- virt-sysprep 用于准备分发镜像(例如,删除SSH主机密钥、删除MAC地址信息或删除用户帐户)。
- virt-sparsify 用于使镜像变得稀疏。
- virt-p2v 用于将物理机转换为在KVM上运行的镜像。
- virt-v2v 用于将Xen和VMware镜像转换为KVM镜像。
修改镜像中的单个文件
本示例展示如何使用virt-edit修改文件。该命令可以使用-a标志将文件名作为参数,也可以使用-d标志将域名作为参数。下面的示例展示如何在当前正在运行的名为instance-000000e1的libvirt域中修改/etc/shadow文件:
# virsh shutdown instance-000000e1
# virt-edit -d instance-000000e1 /etc/shadow
# virsh start instance-000000e1
调整镜像大小
以下是如何使用 virt-resize 来调整镜像大小的示例。假设我们有一个 qcow2 格式的 16GB Windows 镜像,我们想要将其调整为 50GB。
首先,我们使用 virt-filesystems 来识别分区:
# virt-filesystems --long --parts --blkdevs -h -a /data/images/win2012.qcow2 Name Type MBR Size Parent /dev/sda1 partition 07 350M /dev/sda /dev/sda2 partition 07 16G /dev/sda /dev/sda device - 16G -
在这种情况下,我们要调整大小的是
/dev/sda2
分区。我们创建一个新的qcow2镜像,然后使用virt-resize命令将原始镜像的调整大小副本写入新镜像中:
# qemu-img create -f qcow2 /data/images/win2012-50gb.qcow2 50G
# virt-resize --expand /dev/sda2 /data/images/win2012.qcow2 \
/data/images/win2012-50gb.qcow2
Examining /data/images/win2012.qcow2 ...
Summary of changes:
/dev/sda1: This partition will be left alone.
/dev/sda2: This partition will be resized from 15.7G to 49.7G. The
filesystem ntfs on /dev/sda2 will be expanded using the
'ntfsresize' method.
**********
Setting up initial partition table on /data/images/win2012-50gb.qcow2 ...
Copying /dev/sda1 ...
100% [ ] 00:00
Copying /dev/sda2 ...
100% [ ] 00:00
Expanding /dev/sda2 using the 'ntfsresize' method ...
Resize operation completed with no errors. Before deleting the old
disk, carefully check that the resized disk boots and works correctly.
Loop设备、kpartx、网络块设备
如果您无法访问libguestfs,可以使用loop驱动、kpartx和网络块设备直接在主机上挂载镜像文件系统。
警告
使用本节中描述的工具挂载不受信任的guest镜像是一种安全风险,如果可以访问libguestfs工具,应始终使用guestfish和guestmount。有关更多详细信息,请参见Daniel Berrangé的文章“A reminder why you should never mount guest disk images on the host OS”。
挂在原生镜像(没有LVM)
如果你有一个未使用LVM来管理其分区的原始虚拟机镜像,可以使用losetup命令找到一个未使用的loop设备。
# losetup -f
/dev/loop0
在这个例子中,/dev/loop0 是空闲的。将一个 loop 设备与原始镜像关联起来:
# losetup /dev/loop0 fedora17.img
如果镜像只有一个分区,您可以直接挂载loop设备:
# mount /dev/loop0 /mnt
如果镜像有多个分区,则使用 kpartx 将这些分区作为单独的设备映射出来(例如 /dev/mapper/loop0p1),然后挂载对应于根文件系统的分区。具体操作如下:
# kpartx -av /dev/loop0
如果镜像有三个分区(/boot
,/
,swap
),则应为每个分区创建一个新设备:
$ ls -l /dev/mapper/loop0p*
brw-rw---- 1 root disk 43, 49 2012-03-05 15:32 /dev/mapper/loop0p1
brw-rw---- 1 root disk 43, 50 2012-03-05 15:32 /dev/mapper/loop0p2
brw-rw---- 1 root disk 43, 51 2012-03-05 15:32 /dev/mapper/loop0p3
作为 root 用户,可以执行以下命令来挂载第二个分区:
# mkdir /mnt/image
# mount /dev/mapper/loop0p2 /mnt/image
完成后,进行清理:
# umount /mnt/image
# rmdir /mnt/image
# kpartx -d /dev/loop0
# losetup -d /dev/loop0
挂在一个原始镜像(用LVM)
如果你的分区是由 LVM 管理的,可以使用上一个示例中的 losetup 和 kpartx 命令将分区暴露给主机。
# losetup -f
/dev/loop0
# losetup /dev/loop0 rhel62.img
# kpartx -av /dev/loop0
接下来,需要使用 vgscan 命令来识别 LVM 卷组,然后使用 vgchange 命令将卷作为设备暴露出来:
# vgscan
Reading all physical volumes. This may take a while...
Found volume group "vg_rhel62x8664" using metadata type lvm2
# vgchange -ay
2 logical volume(s) in volume group "vg_rhel62x8664" now active
# mount /dev/vg_rhel62x8664/lv_root /mnt
完成后进行清理:
# umount /mnt
# vgchange -an vg_rhel62x8664
# kpartx -d /dev/loop0
# losetup -d /dev/loop0
挂载 qcow2 镜像(不使用 LVM)
你需要加载nbd
(网络块设备)内核模块来挂载qcow2镜像。这将使它支持16块设备,这对我们的目的来说是很好的。以root运行:
# modprobe nbd max_part=16
假设第一个块设备 (/dev/nbd0) 目前没有被使用,我们可以使用 qemu-nbd 和 partprobe 命令来暴露磁盘分区。使用 root 用户:
# qemu-nbd -c /dev/nbd0 image.qcow2
# partprobe /dev/nbd0
如果映像有三个分区(例如/boot
、/
、swap
),应为每个分区创建一个新的设备:
$ ls -l /dev/nbd0*
brw-rw---- 1 root disk 43, 48 2012-03-05 15:32 /dev/nbd0
brw-rw---- 1 root disk 43, 49 2012-03-05 15:32 /dev/nbd0p1
brw-rw---- 1 root disk 43, 50 2012-03-05 15:32 /dev/nbd0p2
brw-rw---- 1 root disk 43, 51 2012-03-05 15:32 /dev/nbd0p3
注意
如果你选择的网络块设备已经被使用,初始的qemu-nbd命令将默默失败,/dev/nbd0p{1,2,3}设备文件将不会被创建。
如果镜像分区没有用LVM管理,它们可以直接挂载。
# mkdir /mnt/image
# mount /dev/nbd0p2 /mnt/image
当你完成后,要进行清理:
# umount /mnt/image
# rmdir /mnt/image
# qemu-nbd -d /dev/nbd0
挂载一个qcow2镜像(使用LVM)
如果镜像的分区是由 LVM 管理的,在使用 qemu-nbd 和 partprobe 命令之后,你必须使用 vgscan 和 vgchange -ay 命令,以便将 LVM 分区公开为可以挂载的设备:
# modprobe nbd max_part=16
# qemu-nbd -c /dev/nbd0 image.qcow2
# partprobe /dev/nbd0
# vgscan
Reading all physical volumes. This may take a while...
Found volume group "vg_rhel62x8664" using metadata type lvm2
# vgchange -ay
2 logical volume(s) in volume group "vg_rhel62x8664" now active
# mount /dev/vg_rhel62x8664/lv_root /mnt
完成后,进行清理:
# umount /mnt
# vgchange -an vg_rhel62x8664
# qemu-nbd -d /dev/nbd0