用 guestfish 修改虚拟机镜像里的文件

接上文,在新宿主机上生成的新虚拟机为 vm2 ,镜像文件分别为 /vm_sp/vm2.img (/ 分区)和 /vm_sp/vm2_data.img (/home分区,做数据盘),格式都是 qcow2,/vm_sp/ 是基于 目录的存储池。

 

首先先把 vm2 关机,否则无法修改 虚拟机内容。

 

安装 guestfish 命令
# yum -y install guestfish

 

我们先来看看单条命令,比如你只想修改主机名,可以这样:

[shell]
guestfish -d vm2 -i sh \
"sed -i ‘s/HOSTNAME.*/HOSTNAME=test0.hlg01/g’ /etc/sysconfig/network"
[/shell]

-d 表示 指定 虚拟机 域,-i 会自动 挂载 域 的磁盘,当然也可以不用 -d 而用 -a ,表示直接指定一个 img 文件。

另外,修改文件也可以用 virt-edt 命令,但是没上条命令那么通用,上条命令所有的 shell 都能执行。
# virt-edit -d $name /etc/sysconfig/network -e “s/HOSTNAME=.*/HOSTNAME=test0.hlg01/g

 

再来看看交互情况下的用法。
# guestfish -d vm2

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> run
><fs> list-filesystems
/dev/sda1: ext3
/dev/sda2: swap
/dev/datavg/home: ext4
/dev/domovg/root: ext4
><fs> mount /dev/domovg/root /
><fs> mount /dev/datavg/home /home/
><fs> cat /etc/fstab

/dev/mapper/domovg-root / ext4 defaults,data=ordered 1 1
UUID=7415a5d3-bd20-4a08-9288-66c49ab20b8d /boot ext3 defaults,data=ordered,nodev,nosuid,noexec 1 2
UUID=df3f6ed1-40e7-4da4-b481-465c7ac5fc17 swap swap defaults 0 0
tmpfs /dev/shm tmpfs defaults 0 0
devpts /dev/pts devpts gid=5,mode=620 0 0
sysfs /sys sysfs defaults 0 0
proc /proc proc defaults 0 0
/dev/datavg/home /home ext4 defaults,data=ordered,nodev,nosuid 1 2

><fs> cat /etc/udev/rules.d/70-persistent-net.rules
# This file was automatically generated by the /lib/udev/write_net_rules
# program, run by the persistent-net-generator.rules rules file.
#
# You can modify it, as long as you keep each rule on a single
# line, and change only the value of the NAME= key.
# PCI device 0x1af4:0x1000 (virtio-pci)
SUBSYSTEM==”net”, ACTION==”add”, DRIVERS==”?*”, ATTR{address}==”52:54:00:b0:4a:a1″, ATTR{type}==”1″, KERNEL==”eth*”, NAME=”em2″

# PCI device 0x1af4:0x1000 (virtio-pci)
SUBSYSTEM==”net”, ACTION==”add”, DRIVERS==”?*”, ATTR{address}==”52:54:00:6a:e8:21″, ATTR{type}==”1″, KERNEL==”eth*”, NAME=”em1″

><fs> cat /etc/sysconfig/network
NETWORKING=yes
NETWORKING_IPV6=no
HOSTNAME=test0.hlg01

><fs> cat /etc/sysconfig/network-scripts/ifcfg-em2
DEVICE=”em2″
BOOTPROTO=”static”
DNS1=”10.0.12.234″
GATEWAY=”10.19.26.1″
IPADDR=”10.19.26.101″
NETMASK=”255.255.255.0″
ONBOOT=”yes”
TYPE=”Ethernet”

><fs> ls /home/
lost+found

 

上面的交互不易于自动化,这里我们可以编写脚本,如下:

[shell]

#!/bin/bash

name=$1
ip=$2
hostname=$3
hwaddr_em2=$4
hwaddr_em1=$5

netmask=255.255.255.0

x=`echo $ip | awk -F. ‘{print $1"."$2"."$3}’`
gateway="$x.1"

# 定义虚拟机, 否则无法根据域修改镜像.
virsh define /etc/libvirt/qemu/$name.xml || exit 1

# 修改系统镜像, 包括:
# 1. 修改主机名;
# 2. 修改网卡设置(增加HWADDR的原因是系统可以把eth变成em);
# 3. 修改路由信息;
# 4. 删除/etc/udev/rules.d/70-persistent-net.rules;
# 5. 删除/var/lib/puppet;
# 6. 删除/etc/sn;
# 然后启动.
guestfish –rw -d $name <<_EOF_
run
mount /dev/domovg/root /
mount /dev/datavg/home /home/
command "sed -i ‘s/HOSTNAME=.*/HOSTNAME=${hostname}/g’ /etc/sysconfig/network"

write /etc/sysconfig/network-scripts/ifcfg-em2 "DEVICE=em2\nHWADDR=${hwaddr_em2}\nBOOTPROTO=static\nIPADDR=$ip\nNETMASK=$netmask\nONBOOT=yes\nTYPE=Ethernet"

write /etc/sysconfig/network-scripts/ifcfg-em1 "DEVICE=em1\nHWADDR=${hwaddr_em1}"

write /etc/sysconfig/network-scripts/route-em2 "192.168.0.0/16 via 10.19.28.1\n10.0.0.0/8 via 10.19.28.1\n100.64.0.0/16 via 10.19.28.1\n0.0.0.0/0 via 10.19.28.1"

command "/bin/rm -rf /etc/udev/rules.d/70-persistent-net.rules"
command "/bin/rm -rf /var/lib/puppet/"
command "/bin/rm -rf /etc/sn"
_EOF_

[/shell]

说明一下,command 命令里 echo xxx > file 是无效的。

 

根据 qcow2 格式的虚拟机生成完全相同的虚拟机

其实这个才是我真实的需求,也就是根据任何一个虚拟机生成一个完全一样的虚拟机。

 

事实上,在新的宿主机先创建相同空间的 qcow2 镜像,然后把 原镜像文件 拷贝到 新宿主机,原镜像文件 覆盖 新创建镜像文件,然后再编辑  新虚拟机 的配置文件,就可以启动了,当然启动之后 新虚拟机的 IP 和主机名 等信息 和原虚拟机一样,这个我们之后再讨论如何修改。

 

这里举个例子,我们的目标是把 一台宿主机的 vm1 拷贝成 另一台宿主机 的 vm2 并启动,存储池都是 vm_sp ,基于「目录」。

 

原宿主机:

# virsh vol-info vm1.img –pool vm_sp
Name: vm1.img
Type: file
Capacity: 20.00 GiB
Allocation: 1.65 GiB

# virsh vol-info vm1_data.img –pool vm_sp
Name: vm1_data.img
Type: file
Capacity: 10.00 GiB
Allocation: 292.27 MiB

 

新宿主机:

# virsh vol-create-as –pool vm_sp –name vm2.img –capacity 20G –allocation 1G –format qcow2
Vol vm2.img created

# virsh vol-create-as –pool vm_sp –name vm2_data.img –capacity 10G –allocation 1G –format qcow2
Vol vm2_data.img created

 

原宿主机

把 vm1.img 和 vm1_data.img 拷贝到 新宿主机,并分别覆盖 vm2.img 和 vm2_data.img ;

然后 virsh dumpxml vm1 >/tmp/vm2.xml,把 /tmp/vm2.xml 拷贝到 新宿主机的 /etc/libvirt/qemu/vm2.xml

 

在 新宿主机:
修改 /etc/libvirt/qemu/vm2.xml 的几项内容:
1. sed -i “s/vm1/vm2/g” /etc/libvirt/qemu/vm2.xml
2. 修改掉 uuid
3. 修改 mac 地址
4. 把disk 和 interface 的 <address 一行都删掉

# virsh define /etc/libvirt/qemu/vm2.xml

然后我们要修改 虚拟机的网络、主机名、Puppet等配置,下篇文章中继续介绍。

 

在基于「目录」的存储池上 创建 qcow2 格式的虚拟机

我为了使用 「对一个虚拟机创建镜像,并恢复成另一个虚拟机」的功能,准备使用 qcow2 格式的镜像,同时使用 基于「目录」的存储池。


宿主机上的 KVM 等相关工具都已经装好了,而且配置了桥接网卡,br1 对应外网, br2 对应内网。

安装 qemu-img ,用于创建 qcow2 格式的镜像文件

yum -y install qemu-img

下面命令使用一个 lvm 的卷 来作为 存储池 的存储,使用基于 「目录」的存储池,如果想增加存储池的大小,增加卷的大小即可。

lvcreate -n vm_sp -L 100G vm_storage_pool_vg
mkfs.ext4 /dev/vm_storage_pool_vg/vm_sp
mkdir /vm_sp
chown root:root /vm_sp
chmod 700 /vm_sp
mount -t ext4 /dev/vm_storage_pool_vg/vm_sp /vm_sp/
virsh pool-define-as vm_sp --type dir --target /vm_sp
virsh pool-info vm_sp

查看存储池信息,开启存储池子,并设置为自动启动。

# virsh pool-list --all
Name State Autostart
-----------------------------------------
vm_sp inactive no

# virsh pool-start vm_sp
Pool vm_sp started

# virsh pool-list
Name State Autostart
-----------------------------------------
vm_sp active no

# virsh pool-autostart vm_sp
Pool vm_sp marked as autostarted

# virsh pool-list
Name State Autostart
-----------------------------------------
vm_sp active yes

# cat /etc/libvirt/storage/vm_sp.xml
<!–
WARNING: THIS IS AN AUTO-GENERATED FILE. CHANGES TO IT ARE LIKELY TO BE
OVERWRITTEN AND LOST. Changes to this xml configuration should be made using:
virsh pool-edit vm_sp
or other application using the libvirt API.
–>

<pool type=’dir’>
<name>vm_sp</name>
<uuid>784ffb8d-3269-3f92-6fe2-4f706c0c576b</uuid>
<capacity unit=’bytes’>0</capacity>
<allocation unit=’bytes’>0</allocation>
<available unit=’bytes’>0</available>
<source>
</source>
<target>
<path>/vm_sp</path>
<permissions>
<mode>0755</mode>
<owner>-1</owner>
<group>-1</group>
</permissions>
</target>
</pool>

另外,下面两个命令:
virsh pool-destroy 用于销毁存储池
virsh pool-undefine 用于取消存储池的定义


创建 vm1.img ,使用 qcow2 格式,初始分配 1G

# virsh vol-create-as --pool vm_sp --name vm1.img --capacity 20G --allocation 1G --format qcow2

# file /vm_sp/vm1.img
/vm_sp/vm1.img: Qemu Image, Format: Qcow , Version: 2

# virsh vol-info /vm_sp/vm1.img
Name: vm1.img
Type: file
Capacity: 20.00 GiB
Allocation: 136.00 KiB

再创建一个 数据盘

virsh vol-create-as --pool vm_sp --name vm1_data.img --capacity 10G --allocation 1G --format qcow2

安装虚拟机:

svirt-install --name vm1 --ram 16000 --vcpus=4 --disk path=/vm_sp/vm1.img -localtime --accelerate --location=/tmp/CentOS-6.3-x86_64_Multi_Choice_20120814.iso -x "ip=10.19.26.100 netmask=255.255.255.0 gateway=10.19.26.1 dns=10.0.12.234 dnsdomain=wandoujia.com ks=http://10.0.11.12/ks/centos_6.3_x64_kvm_guest.cfg console=tty0 console=ttyS0,115200n8" --nographics --network bridge=br2

这个命令仅供参考。

这里我修改 /etc/libvirt/qemu/vm1.xml ,增加第二块盘做数据盘,第二块网卡做公网网卡。

<disk type=’file’ device=’disk’>
    <driver name=’qemu’ type=’qcow2′ cache=’none’/>
    <source file=’/vm_sp/vm1_data.img’/>
    <target dev=’vdb’ bus=’virtio’/>
</disk>

<interface type=’bridge’>
    <mac address=’52:54:00:6a:e8:25’/>
    <source bridge=’br1’/>
    <model type=’virtio’/>
</interface>

然后重定义配置文件,使之生效。

virsh define /etc/libvirt/qemu/vm1.xml

然后重启 vm1 。

virsh destroy vm1
virsh start vm1

然后 virsh console 进入系统配置网卡和主机名等,使系统可用。


删除虚拟机:

name="vm1"
virsh destory $name
virsh undefine $name
virsh vol-delete --pool vm_sp ${name}.img
virsh vol-delete --pool vm_sp ${name}_data.img

那么现在有个需求,我们要对 qcow2 镜像增加大小,怎么做呢,看下面。

# qemu-img info vm1_data.img
image: vm1_data.img
file format: qcow2
virtual size: <span style="color: #0000ff;">10G</span> (10737418240 bytes)
disk size: 292M
cluster_size: 65536

# qemu-img resize vm1_data.img +10G
Image resized.

# qemu-img info vm1_data.img
image: vm1_data.img
file format: qcow2
virtual size: <span style="color: #0000ff;">20G</span> (21474836480 bytes)
disk size: 292M
cluster_size: 65536

# qemu-img resize vm1_data.img -- 10G

This image format does not support resize

可以看到,qcow2 只能增大,不能减小;但是注意,如果用存储池的话,这么加 在存储池里用下面命令是看不到的:

virsh vol-info vm1_data --pool vm_storage_pool

此时,可以用这个命令:

virsh vol-resize vm1_data --capacity 20G --pool vm_storage_pool

那么,增加的大小如何在虚拟机中生效呢,我们以 数据盘 vm1_data 举例,它在虚拟机中是 /dev/vdb,会被做成一个pv ,VG 名称是 datavg 。

通过 virsh destory vm1 和 virsh start vm1 的方式把虚拟机重启了(在虚拟机上 reboot 没用),可用看到 /dev/vdb 空间增加了10G,然后用 pvresize 命令使 pv 空间生效 。

# pvresize /dev/vdb
Physical volume "/dev/vdb" changed
1 physical volume(s) resized / 0 physical volume(s) not resized

# pvs
PV VG Fmt Attr PSize PFree
/dev/vdb datavg lvm2 a– 20.00g 10.00g

另外,如果要增加 vm1.img 这种系统盘大小,同样重启机器之后也会看到 /dev/vda 变大了,但是 pvresize 不管用了,因为我们把 /dev/vda 分了几个区,有一个还做了 swap,此时我们可以用「新建」分区然后加入 VG 的方式,这种方式比较稳妥。