跳到主要内容

4 篇博文 含有标签「libvirt」

查看所有标签

· 阅读需 14 分钟

逐步分析libvirt中的网络管理方法及实践,分析在nat网络中遇到的问题及解决思路

libvirt网络基本概念

libvirt默认使用了一个名为default的nat网络,这个网络默认使用virbr0作为桥接接口,使用dnsmasq来为使用nat网络的虚拟机提供dns及dhcp服务,dnsmasq生效后的配置文件默认保存在以下路径:

  • /var/lib/libvirt/dnsmasq/default.hostsfile mac&&ip绑定的配置文件
  • /var/lib/libvirt/dnsmasq/default.leases dhcp分配到虚拟机的ip地址列表
  • /var/lib/libvirt/network/default.xml default网络的配置文件

dnsmasq服务的启动脚本在/etc/init.d/dnsmasq ,但是我们如果手动使用此脚本来启动服务将会导致dnsmasq读取其自己的配置文件来启动此服务,因此这么做是不推荐的,因为这个服务完全由libvirtd在接管,当libvirtd服务启动的时候,它会将它管理的被标记为autostart的network一并启动起来,而启动network的时候就会自动调用dnsmasq并赋予其适宜的配置文件来运行服务。

使用libvirt管理的网络都会用到dnsmasq来产生相应的配置,比如定义了一个名为route110的network,那么这个route110将使用一个新的桥接接口virbr1来接入网络,并使用dnsmasq产生名为route110.hostsfile和route110.leases的配置文件。其实这里提到的virbr0和virbr1都是libvirt产生的虚拟网卡,其作用就相当于一个虚拟交换机,为虚拟机提供网络转发服务。

libvirt中网络的类型

首先分析一下libvirt所能提供的网络类型:isolated 和forwarding,其中,isolated意为绝对隔离的网络,也就是说处于此网络内的虚拟机对于外界是隔离的,这种模式可以用到一些特殊的场合,比如虚拟机只提供给内部使用,虚拟机只要求能相互通信而不需要与互联网通信。另外一类,forwarding,就是把虚拟机的数据forward到物理网络实现与外部网络进行通讯,其中forwarding又分为两种:nat和routed。

nat

就是把虚拟机的网络数据在经过物理机网络的时候进行ip伪装,这样所有虚拟机出去的网络数据都相当于是物理机出去的数据,也就是说,我们可以分配给使用nat网络的虚拟机一个内网ip,而这个内网ip的虚拟机访问出去的时候外部网络看到的是物理机的公网ip,这样做的用处就是实现多个虚拟机共享物理主机的公网ip,节省公网ip地址;如前所述,默认情况下libvirt已经提供了一个名为default的nat网络,在不需要进行任何配置的情况下使用default网络的虚拟机即可访问互联网,但是互联网却无法访问虚拟机提供的服务,这是因为default网络只对虚拟机的数据包进行了伪装,而没有进行dnat和snat。

需要注意的是libvirt所实现的这种nat网络是通过物理机的iptables规则来实现的,也即是在虚拟机数据经过nat表的postrouting链出去的时候对其进行了伪装。

routed

forwarding模式的另外一种,routed,就是将虚拟机的数据直接通过物理机route出去,和nat一样,也是需要一个virbr虚拟网卡接口来与外面进行通信,这种模式的不同之处在于虚拟机的数据没有经过伪装便直接交给了外部网络,也就是说,使用route模式网络的虚拟机可以使用公网ip地址,而物理机却恰恰在这个时候完全可以使用一个内网ip而不对外提供访问,这样,虚拟机的网卡仅仅把物理机当作一个route数据的工具,此模式应用的场合很多,比如需要让虚拟机运行在一个dmz网络中。但是使用route模式有诸多限制,例如物理机的网络接口不够用的情况下。

这里需要注意的是,nat模式和route模式的区别仅仅在于前者使用了iptables对虚拟机的数据包进行了伪装,而后者没有。

自定义routed网络

在实际的虚拟机使用过程中,我们可能会碰到下面的情况:

  • 1 使用nat网络的虚拟机也需要对外提供服务,
  • 2 物理机只有一个网卡和一个ip,而我们现在既需要通过这个网卡来管理虚拟机,又需要使用这个网卡来提供route网络。

当然你所能碰到的问题可能千奇百怪,也可能根本没有碰到过此类bt问题。下面的内容只作为分析和解决问题的思路,不能生搬。在了解了libvirt的网络管理模式之后,就可以自己动手解决这些限制,下面重点解释第二种问题的解决方法:

首先假定route网络使用的是virbr1虚拟网卡,而虚拟机使用virbr1来为虚拟机提供服务,而我本机又有了一个br0作为em1的桥接网卡来对外提供网络服务,br0的ip是192.168.1.51

首先禁用br0:

ifdown br0

并配置br0的onboot为no,配置文件为onboot=no

然后我们定义了一个名为route的网络,virbr1的ip设置为192.168.1.51 ,这样做的目的是让virbr1取代之前的br0.

<network>
<name>route</name>
<uuid>6224b437-386b-f510-11d5-58d58b1ce87a</uuid>
<forward mode='route'/>
<bridge name='virbr1' stp='on' delay='0' />
<mac address='52:54:00:C8:9F:07'/>
<ip address='192.168.1.51' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.1.128' end='192.168.1.254' />
</dhcp>
</ip>
</network>

接着生成并启用该网络

virsh net-define route.xml
virsh net-start route
virsh net-autostart route
  • /etc/libvirt/qemu/networks/ virsh net-define的network会保存到这
  • /var/lib/libvirt/network/ net-start启动了的network同时也会会保存到这
  • /etc/libvirt/qemu/networks/autostart/ net-autostart的network同时也会保存到这

接下来,我们需要修改em1的配置并将其桥接到virbr1上

ifcfg-em1

DEVICE="em1"
ONBOOT="yes"
BRIDGE=virbr1

接着启动em1

ifup em1

至此em1就被桥接到了virbr1上,可以使用下面的命令检查

brctl show

现在我们需要在本机添加一条默认路由,不然虚拟机是访问不了外面的:

route add default gw 192.168.1.1 dev virbr1

这里的192.168.1.1是真实的路由。至此,问题已经解决了。

自定义nat网络

下面说说问题1的解决方法: 既然知道了nat出去的虚拟机只能访问外网而外网却不能访问进来,nat又是通过iptables来做的,也就是当libvirt每次启动的时候都会往iptables最前面插入自己的规则以保证nat的虚拟机能正常访问外网,那么我们是不是可以通过修改iptables的规则来实现呢,比如我们需要一个内网ip的虚拟机对外提供80服务,那么我们就把物理机的80端口映射到这台虚拟机的80端口上,因为我们的物理机是可以直接和虚拟机通信的,只是外网不能而已,下面添加规则:

iptables -t nat -A PREROUTING -p tcp -i virbr1 --dport 80  -j DNAT --to-destination 192.168.122.2:80

这样我们对外部访问80端口进来的数据进行了dnat,而出去的我们不用snat,只需要再添加如下规则:

iptables -I FORWARD -i virbr1 -o virbr0 -p tcp -m state --state NEW -j ACCEPT

至此问题看似得到解决,但是我们忽略了一个关键的问题,那就是每当libvirt启动的时候就会往表的最前面插入它自己的规则,而iptables的规则是有先后顺序的,也就是说,我们自己添加的规则在libvirtd服务重启之后即被libvirt定义的规则所淹没,怎么办呢,我现在只想到了这么一个方法,直接修改libvirtd的启动脚本,在它的规则生效之后插入我们自定义的规则:

vi /etc/init.d/libvirtd

start() {
    echo -n $"Starting $SERVICE daemon: "
    initctl_check

    mkdir -p /var/cache/libvirt
    rm -rf /var/cache/libvirt/*
    KRB5_KTNAME=$KRB5_KTNAME daemon --pidfile $PIDFILE --check $SERVICE $PROCESS --daemon $LIBVIRTD_CONFIG_ARGS $LIBVIRTD_ARGS
    RETVAL=$?
    echo
    [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$SERVICE
    sleep 1
    iptables -D FORWARD -i virbr1 -o virbr0 -p tcp -m state --state NEW -j ACCEPT
    iptables -I FORWARD -i virbr1 -o virbr0 -p tcp -m state --state NEW -j ACCEPT
... ...

至此问题基本解决。

route网络转换nat网络

另外一个问题,我们前面有发现route和nat的网络区别仅仅是一个做了nat的iptables规则一个没有,那么我们可不可以自己在iptables里面添加相应的规则将route网络变身为nat网络呢?答案肯定是可以的,只需要添加上下面的规则即可,原理还请观看本文的同学自己分析,这里假设我们route网络给虚拟机分配的ip是192.168.100.0/24网段:

iptables -t nat -A POSTROUTING -s 192.168.100.0/24 -d ! 192.168.100.0/24 -j MASQUERADE
iptables -A FORWARD --destination 192.168.100.0/24 -m state --state RELATED,ESTABLISHED -j ACCEPT

自定义dnsmasq

这里再添加一个可以手工启动dnsmasq的小脚本

#!/bin/bash
brctl addbr routebr
ifconfig routebr 192.168.122.1 netmask 255.255.255.0
iptables -t nat -A POSTROUTING -s 192.168.122.0/24 -d ! 192.168.122.0/24 -j MASQUERADE
iptables -A FORWARD --destination 192.168.122.0/24 -m state --state RELATED,ESTABLISHED -j ACCEPT
/usr/sbin/dnsmasq \
--strict-order \
--bind-interfaces \
--pid-file=/usr/local/vps/network/default.pid \
--conf-file= \
--except-interface lo \
--listen-address 192.168.122.1 \
--dhcp-range 192.168.122.2,192.168.122.254 \
--dhcp-leasefile=/usr/local/vps/network/dnsmasq/default.leases \
--dhcp-lease-max=253 \
--dhcp-no-override \
--dhcp-hostsfile=/usr/local/vps/network/dnsmasq/default.hostsfile

重启network导致网络中断

当我们需要实时修改network的配置并使之生效的时候,就得重新启动此network,也就是需要net-destroy再net-start一下,我们的配置才能生效,但是随之而来的问题是,当network被重新启动之后,虚拟机便无法访问网络了,除非把虚拟机的network interface重新attach一下,或者等到虚拟机重新启动,那么为什么会出现这样的问题呢?我们先从它的表象开始分析,至于是否要追究到源码里面就取决于同学们自己了,反正我暂时没那功夫。这里仅仅是抛出来了一块砖。

当一个network启动之后,会自动生成一个虚拟网卡接口如virbr1,也会生成其他一些需要的东西,而重新启动了libvirt的network之后这个接口也会被重启,所以就导致了中途有一个中断的过程,

那事情就比较清晰了,如果你将libvirt启动网络的所有过程拆分开来一个一个的手动生成,需要修改某一部分配置的时候实际上你只需要修改对应的配置文件而不需要重新启动这个virbr1接口,比如上面提到的mac+ip的绑定,如果把dnsmasq独立出来,不让libvirt接管,那么增加了mac+ip绑定之后,仅仅需要重启dnsmasq这个服务。

· 阅读需 4 分钟

在kvm虚拟机管理的过程当中,对虚拟机带宽进行良好的控制是十分重要的。

linux系统当中对网络带宽的控制一般都是使用tc命令实现,tc即是traffic control的缩写,在这里可以找到有关tc命令的内容。

当然你可以手动使用tc命令来处理这些事情,比如使用cbq队列,htb队列等,都是可以实现的,网上找找应该有很多关于这方面的资料,

cbq队列示例

比如下面就是使用cbq队列限制src ip为192.168.1.102发送数据包的速率:

1.建立cbq队列

tc qdisc add dev eth0 root handle 1: cbq avpkt 1000 bandwidth 100mbit

2.建立带宽限制分类

tc class add dev eth0 parent 1: classid 1:1 cbq rate 60mbit allot 1500 prio 5 bounded isolated
tc class add dev eth0 parent 1: classid 1:2 cbq rate 70mbit allot 1500 prio 5 bounded isolated
tc class add dev eth0 parent 1: classid 1:3 cbq rate 80mbit allot 1500 prio 5 bounded isolated

3.建立过滤器

绑定指定带宽限制类型至指定虚拟机ip:

tc filter add dev eth0 parent 1: protocol ip prio 16 u32 match ip src 192.168.1.102 flowid 1:2

htb队列示例

我们可以在母机上给vm对应的虚拟网卡增加tc规则,使用htb队列,一个可用的脚本示例如下:

# add interface bandwidth limit
# usage: tc_add iface in_kbps out_kbps
tc_add() {
    local iface=$1
    local in_bw=$2
    local out_bw=$3
    local in_average="${in_bw}kbps"
    local in_peak="${in_bw}kbps"
    local out_average="${out_bw}kbps"
    local out_peak="${out_bw}kbps"
    local burst="2kb"
    local mtu=1500
    local r2q=$((in_bw*1000/mtu-1))
    local tc="/sbin/tc"
    [ $r2q -lt 1 ] && r2q=1
    if [ $in_bw != 0 ]; then
        $tc qdisc add dev $iface root handle 1: htb default 2 r2q $r2q
        $tc class add dev $iface parent 1: classid 1:1 htb rate $in_average \
            ceil $in_peak burst $burst cburst $burst
        $tc class add dev $iface parent 1:1 classid 1:2 htb rate $in_average \
            ceil $in_peak burst $burst cburst $burst
        $tc qdisc add dev $iface parent 1:2 handle 2: sfq perturb 10
        $tc filter add dev $iface parent 1:0 protocol ip handle 1 fw flowid 1
    fi
    if [ $out_bw != 0 ]; then
        $tc qdisc add dev $iface ingress
        $tc filter add dev $iface parent ffff: protocol ip u32 match ip src \
            0.0.0.0/0 police rate $out_average burst ${out_bw}kb mtu 64kb drop \
            flowid :1
    fi
}

# clean up interface bandwidth limit
# usage: tc_del iface
tc_del() {
    local iface=$1
    local tc="/sbin/tc"
    $tc qdisc del dev $iface root &>>/dev/null
    sleep 0.1
    $tc qdisc del dev $iface ingress &>>/dev/null
}

libvirt中的带宽控制

我比较推荐的方法还是直接使用libvirt,libvirt 中已经集成了带宽控制的功能,下面是关于带宽控制部分的xml描述:

使用方法:在网卡interface中加入

<bandwidth>
<inbound average='1000' peak='5000' burst='1024'/>
<outbound average='128' peak='256' burst='256'/>
</bandwidth>

以下是关于各项参数的解释,获取最新的信息可以参考libvirt文档.

  • mandatory attribute:

    • average: It specifies average bit rate on interface being shaped.
  • optional attributes:

    • peak: which specifies maximum rate at which interface can send data,
    • burst: amount of bytes that can be burst at peak speed.

Accepted values: integer numbers.

units:

  • average: kilobytes per second
  • peak: kilobytes per second
  • burst: kilobytes.

· 阅读需 17 分钟

这是一篇关于snapshots, blockpull, blockcommit的的介绍.作者和with Eric Blake, Jeff Cody,Kevin Wolf以及很多IRC和mailing lists里面的同学大量讨论以及作者大量的特向测试的基础之上总结出来的

基础知识

一个虚拟机快照可被看作是虚拟机的在某个指定时间的视图(包括他的操作系统和所有的程序).据此,某可以还原到一个之前的完整的状态,或者在guest运行的时候做个备份.所以,在我们继续深入之前我们必须搞懂两个名词:backing files和overlays .

QCOW2 backing files 与 overlays

qcow2(qemu copy-on-write)具有创建一个base-image,以及在base-image(即backing file)的基础上创建多个copy-on-write overlays镜像的能力.backing files和overlays十分有用,可以迅速的创建瘦装备虚拟机的实例,特别是在开发测试的时候可以让你迅速的回滚到之前的某个已知状态,丢弃overlay.

Figure-1

.--------------.    .-------------.    .-------------.    .-------------.
|              |    |             |    |             |    |             |
| RootBase     |<---| Overlay-1   |<---| Overlay-1A  <--- | Overlay-1B  |
| (raw/qcow2)  |    | (qcow2)     |    | (qcow2)     |    | (qcow2)     |
'--------------'    '-------------'    '-------------'    '-------------'

上图表明rootbase是overlay-1的backing file,以此类推.

Figure-2

.-----------.   .-----------.   .------------.  .------------.  .------------.
|           |   |           |   |            |  |            |  |            |
| RootBase  |<--- Overlay-1 |<--- Overlay-1A <--- Overlay-1B <--- Overlay-1C |
|           |   |           |   |            |  |            |  | (Active)   |
'-----------'   '-----------'   '------------'  '------------'  '------------'
   ^    ^
   |    |
   |    |       .-----------.    .------------.
   |    |       |           |    |            |
   |    '-------| Overlay-2 |<---| Overlay-2A |
   |            |           |    | (Active)   |
   |            '-----------'    '------------'
   |
   |
   |            .-----------.    .------------.
   |            |           |    |            |
   '------------| Overlay-3 |<---| Overlay-3A |
                |           |    | (Active)   |
                '-----------'    '------------'

上图表明我们可以只用单个backing file来创建多条链.

注意 : backing file 总是 只读 打开的. 换言之, 一旦新快照被创建,他的后端文件就不能被修改,(快照依赖于后端文件的这种状态).了解更多参见后面的('blockcommit' 节) .

示例 :

[FedoraBase.img] ----- <- [Fed-guest-1.qcow2] <- [Fed-w-updates.qcow2] <- [Fedora-guest-with-updates-1A]
                 \
                  \--- <- [Fed-guest-2.qcow2] <- [Fed-w-updates.qcow2] <- [Fedora-guest-with-updates-2A]

(注意箭头的方向,Fed-w-updates.qcow2 的backing file是 Fed-guest-1.qcow2)

上面的示例中可以看到 FedoraBase.img 安装了一个fedora17系统,并作为我们的backing file.现在这个backing file将作为模板快速的创建两个瘦装备实例,和 Figure-2 道理是一样的.

具体操作

使用qemu-img为单个backing file来创建两个fedora的瘦装备克隆:

# qemu-img create -b /export/vmimages/RootBase.img -f qcow2 \
  /export/vmimages/Fedora-guest-1.qcow2

# qemu-img create -b /export/vmimages/RootBase.img -f qcow2 \
  /export/vmimages/Fedora-guest-2.qcow2

现在,上面创建出来的两个镜像 Fedora-guest-1 & Fedora-guest-2 都可以用来启动一个虚拟机,继续我们的示例,现在我们需要创建一个f17的实例,但是这次我们需要创建的是具有完整的更新的实例,这时可以创建另外一个overlay(Fedora-guest-with-updates-1A)而这个overlay的backing file是'Fed-w-updates.qcow2'(一个包含了完整更新的镜像):

# qemu-img create -b /export/vmimages/Fed-w-updates.qcow2 -f qcow2 \
   /export/vmimages/Fedora-guest-with-updates-1A.qcow2

我们可以使用qemu-img命令来查看镜像的信息,包括虚拟磁盘大小,使用大小,backing file指向:

# qemu-img info /export/vmimages/Fedora-guest-with-updates-1A.qcow2

注意: 最新版本的qemu-img可以递归查询到整条完整的链:

# qemu-img info --backing-chain /export/vmimages/Fedora-guest-with-updates-1A.qcow2

名词解释Snapshot:

  • 内置快照(Internal Snapshots) -- 单个qcow2镜像文件存储了包括数据以及快照的状态信息,

内置快照又可以细分一下:-

  • 内置磁盘快照(Internal disk snapshot):

  • 快照点的磁盘状态,数据和快照保存在单个qcow2文件中,虚拟机运行状态和关闭状态都可以创建.

Libvirt 使用 'qemu-img' 命令创建关机状态的磁盘快照.Libvirt 使用 'savevm' 命令创建运行状态的磁盘快照.

  • 内置系统还原点(Internal system checkpoint):

内存状态,设备状态和磁盘状态,可以为运行中的虚拟机创建,所有信息都存储在同一个qcow2文件中,只有在运行状态才能创建内置系统还原点.

Libvirt 使用'savevm' 命令来创建这种快照

  • 外置快照(External Snapshots) -- 当一个快照被创建时,创建时当前的状态保存在当前使用的磁盘文件中,即成为一个backing file.此时一个新的overlay被创建出来保存往后的数据.

这个也可以细分一下:-

  • 外置磁盘快照(External disk snapshot): 磁盘的快照被保存在一个文件中,创建时间点以后的数据被记录到一个新的qcow2文件中.同样可以在运行和关闭状态创建.

Libvirt 使用 'transaction' 命令来为运行状态创建这种快照. Libvirt 使用'qemu-img' 命令为关闭状态创建这种快照(截止目前功能还在开发中).

  • 外置系统还原点(External system checkpoint):

虚拟机的磁盘状态将被保存到一个文件中,内存和设备的状态将被保存到另外一个新的文件中,

(这个功能也还在开发中).

VM状态(VM state):

保存运行状态虚拟机的内存设备状态信息至文件,可以通过此文件恢复到保存时的状态,有点类似系统的休眠.(注意创建VM状态保存的时候VM磁盘必须是未发生写入改动的)

Libvirt使用 'migrate' (to file)命令来完成VM状态转储.

创建快照

每次产生一个外置snapshot,一个 /new/ overlay 镜像就会随之生成,而前一个镜像就变成了一个快照.

diskonly内置快照创建

假如需要为名为'f17vm1'的虚拟机创建一个运行态或关闭态的内置快照snap1

# virsh snapshot-create-as f17vm1  snap1 snap1-desc

列出快照列表,使用qemu-img查看info

# virsh snapshot-list f17vm1
# qemu-img info /home/kashyap/vmimages/f17vm1.qcow2

disk-only外置快照创建 :

查看虚拟机磁盘列表

# virsh domblklist f17-base
Target     Source
---------------------------------------------
vda        /export/vmimages/f17-base.qcow2

创建外置disk-only磁盘快照(VM运行态):

# virsh snapshot-create-as --domain f17-base snap1 snap1-desc \
--disk-only --diskspec vda,snapshot=external,file=/export/vmimages/sn1-of-f17-base.qcow2 \
--atomic
Domain snapshot snap1 created
  • 一旦上面的命令被执行,则原来的镜像f17-base将变为backing file,一个新的镜像被创建.

现在再列表查看虚拟机磁盘,你会发现新产生的镜像已经投入使用.

# virsh domblklist f17-base
Target     Source
----------------------------------------------------
vda        /export/vmimages/sn1-of-f17-base.qcow2

快照回滚

截止写此文之时,回滚至'内置快照'(system checkpoint或disk-only)是可以使用的.

虚拟机f17vm1回滚至快照'snap1'

# virsh snapshot-revert --domain f17vm1 snap1

使用 snapshot-revert 回滚 '外置磁盘快照' 稍微复杂些,需要涉及到稍微复杂点的问题,需要考虑的是合并'base'至'top'还是合并'top'至'base'.也就是说,有两种方式可以选择,外置磁盘快照链的缩短可以使用 blockpull 或 blockcommit .截止目前上游社区仍然在努力完善这项功能.

合并快照文件

外置快照非常有用,但这里有一个问题就是如何合并快照文件来缩短链的长度,如上所述这里

有两种方式:

  • blockcommit: 从 top 合并数据到 base (即合并overlays至backing files).
  • blockpull: 将backing file数据合并至overlay中.从 base 到 top .

blockcommit

blockcommit可以让你将'top'镜像(在同一条backing file链中)合并至底层的'base'镜像.一旦 blockcommit 执行完成,处于最上面的overlay链关系将被指向到底层的overlay或base.这在创建了很长一条链之后用来缩短链长度的时候十分有用.

下面来个图说明下:

我们现在有一个镜像叫'RootBase',拥有4个外置快照,'Active'为当前VM写入数据的,

使用'blockcommit'可以有以下多种case :

合并Snap-1, Snap-2 and Snap-3 至 'RootBase' 只合并Snap-1 and Snap-2 至 RootBase 只合并Snap-1 至 RootBase 合并Snap-2 至 Snap-1 合并Snap-3 至 Snap-2 合并Snap-2 和 Snap-3 至 Snap-1 注: 合并'Active'层(最顶部的overlay)至backing_files的功能还在开发中.

(下图解释case (6))

Figure-3

.------------.  .------------.  .------------.  .------------.  .------------.
|            |  |            |  |            |  |            |  |            |
| RootBase   <---  Snap-1    <---  Snap-2    <---  Snap-3    <---  Snap-4    |
|            |  |            |  |            |  |            |  | (Active)   |
'------------'  '------------'  '------------'  '------------'  '------------'
                                 /                  |
                                /                   |
                               /  commit data       |
                              /                     |
                             /                      |
                            /                       |
                           v           commit data  |
.------------.  .------------. <--------------------'           .------------.
|            |  |            |                                  |            |
| RootBase   <---  Snap-1    |<---------------------------------|  Snap-4    |
|            |  |            |       Backing File               | (Active)   |
'------------'  '------------'                                  '------------'

举个例子,有以下场景:

当前: [base] <- sn1 <- sn2 <- sn3 <- sn4(this is active)

目标: [base] <- sn1 <- sn4 (如此来丢弃sn2,sn3)

下面有两种方式,method-a更快,method-b 慢些,但是sn2有效可用. (VM运行态).

(method-a):

           # virsh blockcommit --domain f17 vda --base /export/vmimages/sn1.qcow2  \

               --top /export/vmimages/sn3.qcow2 --wait --verbose

[OR] (method-b):

# virsh blockcommit --domain f17 vda  --base /export/vmimages/sn2.qcow2  \
    --top /export/vmimages/sn3.qcow2 --wait --verbose
# virsh blockcommit --domain f17 vda  --base /export/vmimages/sn1.qcow2  \
    --top /export/vmimages/sn2.qcow2 --wait --verbose

注: 如果手工执行qemu-img命令完成的话, 现在还只能用method-b. Figure-4

.------------.  .------------.  .------------.  .------------.  .------------.
|            |  |            |  |            |  |            |  |            |
| RootBase   <---  Snap-1    <---  Snap-2    <---  Snap-3    <---  Snap-4    |
|            |  |            |  |            |  |            |  | (Active)   |
'------------'  '------------'  '------------'  '------------'  '------------'
                  /                  |             |
                 /                   |             |
                /                    |             |
   commit data /         commit data |             |
              /                      |             |
             /                       | commit data |
            v                        |             |
.------------.<----------------------|-------------'            .------------.
|            |<----------------------'                          |            |
| RootBase   |                                                  |  Snap-4    |
|            |<-------------------------------------------------| (Active)   |
'------------'                  Backing File                    '------------'

上图演示了case1的blockcommit走向,现在sn4的backing file指向rootbase.

blockpull

blockpull(qemu中也称作'block stream')可以将backing合并至active,与blockcommit正好相反.截止目前只能将backing file合并至当前使用的active中,也就是说还不支持指定top的合并. 设想一个下面的场景:

Figure-5

.------------.  .------------.  .------------.  .------------.  .------------.
|            |  |            |  |            |  |            |  |            |
| RootBase   <---  Snap-1    <---  Snap-2    <---  Snap-3    <---  Snap-4    |
|            |  |            |  |            |  |            |  | (Active)   |
'------------'  '------------'  '------------'  '------------'  '------------'
                         |                 |              \
                         |                 |               \
                         |                 |                \
                         |                 |                 \ stream data
                         |                 | stream data      \
                         | stream data     |                   \
                         |                 |                    v
     .------------.      |                 '--------------->  .------------.
     |            |      '--------------------------------->  |            |
     | RootBase   |                                           |  Snap-4    |
     |            | <---------------------------------------- | (Active)   |
     '------------'                 Backing File              '------------'

使用blockpull我们可以将snap-1/2/3中的数据合并至active层,最终rootbase将变成active的直接后端.

命令如下:

假设快照已经使用 创建Snapshots 小节中的方式完成:

Figure-5中描述的-- [RootBase] <- [Active].

# virsh blockpull --domain RootBase  \
  --path /var/lib/libvirt/images/active.qcow2  \
  --base /var/lib/libvirt/images/RootBase.qcow2  \
  --wait --verbose

后续的工作是我们需要使用virsh来清理掉不用的快照

# virsh snapshot-delete --domain RootBase Snap-3 --metadata
# virsh snapshot-delete --domain RootBase Snap-2 --metadata
# virsh snapshot-delete --domain RootBase Snap-1 --metadata

Figure-6

.------------.  .------------.  .------------.  .------------.  .------------.
|            |  |            |  |            |  |            |  |            |
| RootBase   <---  Snap-1    <---  Snap-2    <---  Snap-3    <---  Snap-4    |
|            |  |            |  |            |  |            |  | (Active)   |
'------------'  '------------'  '------------'  '------------'  '------------'
      |                  |              |                  \
      |                  |              |                   \
      |                  |              |                    \  stream data
      |                  |              | stream data         \
      |                  |              |                      \
      |                  | stream data  |                       \
      |  stream data     |              '------------------>     v
      |                  |                                    .--------------.
      |                  '--------------------------------->  |              |
      |                                                       |  Snap-4      |
      '---------------------------------------------------->  | (Active)     |
                                                              '--------------'
                                                                'Standalone'
                                                                (w/o backing
                                                                file)

上图表示的是将所有backing file全部合并至active

如下执行命令:

(1) 在我们执行合并 之前 查看一下快照的大小(注意观察'Active'): ::

        # ls -lash /var/lib/libvirt/images/RootBase.img
        608M -rw-r--r--. 1 qemu qemu 1.0G Oct 11 17:54 /var/lib/libvirt/images/RootBase.img

        # ls -lash /var/lib/libvirt/images/*Snap*
        840K -rw-------. 1 qemu qemu 896K Oct 11 17:56 /var/lib/libvirt/images/Snap-1.qcow2
        392K -rw-------. 1 qemu qemu 448K Oct 11 17:56 /var/lib/libvirt/images/Snap-2.qcow2
        456K -rw-------. 1 qemu qemu 512K Oct 11 17:56 /var/lib/libvirt/images/Snap-3.qcow2
        2.9M -rw-------. 1 qemu qemu 3.0M Oct 11 18:10 /var/lib/libvirt/images/Active.qcow2

(2) 单独检查下 'Active' 所指向的backing file ::

        # qemu-img info /var/lib/libvirt/images/Active.qcow2
        image: /var/lib/libvirt/images/Active.qcow2
        file format: qcow2
        virtual size: 1.0G (1073741824 bytes)
        disk size: 2.9M
        cluster_size: 65536
        backing file: /var/lib/libvirt/images/Snap-3.qcow2

(3) 开始 blockpull 操作. ::

        # virsh blockpull --domain ptest2-base --path /var/lib/libvirt/images/Active.qcow2 --wait --verbose
        Block Pull: [100 %]
        Pull complete

(4) 再检查下快照大小, 'Active'变得很大 ::

        # ls -lash /var/lib/libvirt/images/*Snap*
         840K -rw-------. 1 qemu qemu 896K Oct 11 17:56 /var/lib/libvirt/images/Snap-1.qcow2
         392K -rw-------. 1 qemu qemu 448K Oct 11 17:56 /var/lib/libvirt/images/Snap-2.qcow2
         456K -rw-------. 1 qemu qemu 512K Oct 11 17:56 /var/lib/libvirt/images/Snap-3.qcow2
        1011M -rw-------. 1 qemu qemu 3.0M Oct 11 18:29 /var/lib/libvirt/images/Active.qcow2

(5) 检查'Active'信息,现在它已经不需要backing file了,正如Figure-6所示::

        # qemu-img info /var/lib/libvirt/images/Active.qcow2
        image: /var/lib/libvirt/images/Active.qcow2
        file format: qcow2
        virtual size: 1.0G (1073741824 bytes)
        disk size: 1.0G
        cluster_size: 65536

(6) 清理现场 ::

        # virsh snapshot-delete --domain RootBase Snap-3 --metadata

(7) 现在还可以使用下 guestfish READ-ONLY 模式来检查下磁盘内容( --ro 选项) ::

        # guestfish --ro -i -a /var/lib/libvirt/images/Active.qcow2

快照删除 (and 'offline commit')

删除(live/offline)状态的内置快照很方便 ::

# virsh snapshot-delete --domain f17vm --snapshotname snap6

[OR]

# virsh snapshot-delete f17vm snap6

libvirt现在还没有删除外置快照的功能,但是可以使用qemu-img命令来完成.

比如我们有这样一条链(VMoffline状态): base <- sn1 <- sn2 <- sn3

现在删除第二个快照(sn2).有两种方式:

* Method (1): base <- sn1 <- sn3 (by copying sn2 into sn1)
* Method (2): base <- sn1 <- sn3 (by copying sn2 into sn3)

Method (1)

(by copying sn2 into sn1)

注意: 必须保证sn1没有被其他快照作为后端,不然就挂了!!

offline commit

# qemu-img commit sn2.qcow2

将会commit所有在sn2中的改动到sn2的backing file(sn1). qemu-img commit和virsh blockcommit类似 现在把sn3的后端指向到sn1.

# qemu-img rebase -u -b sn1.qcow2 sn3.qcow2

注意: -u代表'Unsafe mode' -- 此模式下仅仅修改了指向到的backing file名字,必须谨慎操作. 现在可以直接删除sn2

# rm sn2.qcow2

Method (2)

(by copying sn2 into sn3)

合并数据,rebase后端:

# qemu-img rebase -b sn1.qcow2 sn3.qcow2

未使用-u模式的rebase将把数据也一并合并过去,即sn2的数据写入到sn3. 换言之: 这里使用的'Safe mode',也是默认模式 --对sn3而言任何从 qemu-img rebase(没有-u)和和virsh blockpull类似. backingfile(sn1)到旧的backingfile(sn2)之间发生的差异改动都将被合并到sn3中.

现在可以删除sn2了

# rm sn2.qcow2

· 阅读需 3 分钟

简单演示如何在libvirt中创建storage pool

libvirt提供了存储管理的功能,可以管理的存储类型有 目录 lvm逻辑卷 磁盘 iscsi存储 scsi存储 mpath netfs等,这里以最基本的目录类型为例

基本概念:

在libvirt里保存虚拟机磁盘镜像的目录或设备称作存储池 即pool ,每个虚拟机所使用的虚拟磁盘镜像称作卷 即vol ,vol是存储在pool里面的。

我们可以使用命令行的virsh工具来管理,pool有两种基本状态:活动和非活动,查看当前存储池的状态

virsh # pool-list --all 
Name State Autostart 
-----------------------------------------
default inactive yes 
disk active yes

新建一个基于目录的存储池bigpool 存储路径为 /bigpool

virsh # pool-define-as bigpool dir - - - - /bigpool
Pool bigpool defined

这时候pool仅仅是定义出来了,可以用pool-list --all查看到。但是相应的目录是不存在的,接着需要建立这个pool

virsh # pool-build bigpool
Pool bigpool built

这个时候才是真正的建立起这个pool,libvirt会自动创建/bigpool目录,并设置相应的权限,如果你有用selinux作为libvirt的安全措施的话它还能自动设置上下文

# ls -Zld /bigpool/
drwx------ 2 ? root root 4096 Jul 13 12:54 /bigpool/

我这里由于没有使用selinux所以没有上下文的

pool创建好之后就可以启动了

virsh # pool-start bigpool
Pool bigpool started

virsh # pool-list --all 
Name State Autostart 
-----------------------------------------
bigpool active no 
default inactive yes 
disk active yes

还可以设置pool为自动启动

virsh # pool-autostart bigpool
Pool bigpool marked as autostarted

virsh # pool-list --all 
Name State Autostart 
-----------------------------------------
bigpool active yes 
default inactive yes 
disk active yes

基于目录的一个存储池就这样建立完成了,是不是很简单?