快照(Snapshot)是Copy-on-write的一種應用。QEMU支持兩種快照:
- 內部快照(internal snapshot):在qcow2鏡像的snapshot table中維護的快照,所有快照都存放在一個鏡像文件中
- 外部快照(external snapshot):與Backing file很類似,在外部文件中創建新的鏡像,原先的鏡像只讀
內部快照的原理是:
- 創建一個Snapshot後,在Snapshot Table中新增一項,複製L1 Table
- 當L2 Table或者Data Cluster發生改變,則把改變前的數據複製一份(Copy-on-write),由新創建的Snapshot的L1 Table來管理
- L2 Table或者Data Cluster的變化,直接寫到原始位置
- 要刪除快照,很簡單,直接把Snapshot Table對應項、以及複製的L1-L2-DS刪除即可
- 要加載快照,則需要依據L1-L2-DS信息,將其合併到鏡像的L1-L2-DS信息中
可以使用Monitor來創建、加載、刪除內部快照:
# 保存一個內部快照
(qemu) savevm snapshot-1
qemu-img info hda.img
# 輸出如下:
#Snapshot list:
#ID TAG VM SIZE DATE VM CLOCK
#1 snapshot-1 112M 2016-09-07 18:05:48 00:00:21.536
#Format specific information:
# compat: 1.1
# lazy refcounts: false
# 加載內部快照
(qemu) loadvm snapshot-1
# 刪除內部快照
(qemu) delvm snapshot-1
外部快照與內部快照相反:內部快照是原數據變化,外部快照則是新文件變化。
可以使用Monitor來管理外部快照:
snapshot_blkdev ide0-hd0 snapshot.img qcow2
有了磁盤鏡像文件後,你需要爲qemu-system-*指定參數,給客戶機增加磁盤。有幾種不同的配置方式:
# 最簡單的方式
-hda hda.img
# 使用-drive配置塊設備,可以指定if爲virtio來提升性能
-drive file=hda.img,index=0,media=disk,if=virtio
# 使用-device配置通用設備
-drive file=hda.img,if=none,id=virtio-disk0,format=qcow2,cache=none
# 可以指定virtio-blk-pci來提升性能
-device virtio-blk-pci,scsi=off,bus=pci.0,addr=0x4,drive=virtio-disk0,bootindex=1
QEMU中的網絡,包含兩部分的內容:
- 客戶機使用的虛擬網絡設備
- 和上述虛擬設備通信的網絡後端,這些後端負責把虛擬設備的數據包發到宿主機的網絡中
要創建一個網絡後端,可以指定如下選項:
# TYPE爲後端類型:user、tap、bridge、socket、vde等
# id爲一個標識符,將虛擬網絡設備和網絡後端關聯在一起
# 如果客戶機有多個虛擬網絡設備,則每一個都需要自己的網絡後端
-netdev TYPE,id=NAME,...
QEME支持多種網絡後端:
如果沒有指定網絡選項,QEMU默認會模擬單張Intel e1000 PCI網卡,該網卡基於user後端(SLIRP)連接到宿主機:
# 不指定網絡
qemu
# 等價配置。自0.12開始廢棄的配置方式 -net nic相當於-device DEVNAME;-net TYPE相當於-netdev TYPE
qemu -hda disk.img -net nic -net user
# 等價配置。-netdev指定網絡後端,-device指定虛擬網絡設備,後者通過netdev字段引用後端的ID
qemu -netdev user,id=network0 -device e1000,netdev=network0
在客戶機看來:
- 本身的IP地址被分配爲 10.0.2.15+
- 分配IP的虛擬DHCP爲 10.0.2.2
- 虛擬DNS服務器爲 10.0.2.3
- 虛擬Samba服務器爲 10.0.2.4,客戶機可以通過此服務器訪問宿主機的文件系統
用戶模式網絡可以很方便的訪問網絡資源。但是它有很多限制:
- 默認的,它運作方式類似於防火牆,且不允許任何入站流量。這個限制可以通過端口重定向解決
- 僅僅支持TCP、UDP協議,對於ICMP則不支持
- 性能比較差
爲了支持入站請求,你可以使用端口重定向(Redirecting ports)——把針對宿主機某個端口的請求轉發給客戶機的某個端口。映射後,客戶機可以對外提供SSH、HTTP等服務:
# 把宿主機的7080端口重定向到客戶機的80端口;把宿主機的7022端口重定向到客戶機的22端口
qemu-system-x86_64 -redir tcp:7080::80 -redir tcp:7022::22 -hda ~/Vmware/KVM/centos7-base.img -m 512
# 從宿主機SSH到客戶機
ssh [email protected] -p 7022
你可以不使用默認的10.0.2網段:
Shell
1
-netdev user,id=network0,net=192.168.5.0/24,dhcpstart=192.168.5.9
依據客戶機安裝的操作系統,可能需要進行一些配置,才能正常使用網絡。以CentOS 7 Minimal + 用戶模式網絡爲例,需要修改以下配置文件:
NETWORKING=yes
# 如果不使用IPV6
NETWORKING_IPV6=no
QEMU的TAP後端利用宿主機的TAP設備,爲客戶機提供完整的橋接網絡支持,如果外部需要使用標準端口連接到客戶機, 或者多個客戶機需要相互通信,可以使用該方式。 TAP後端還具有以下優勢:
- 非常好的性能
- 可以配置以支持各種網絡拓撲
但是,你需要在宿主機上進行網絡拓撲的配置,而且各種系統的配置不同。
使用TAP後端前,你需要確認你的宿主機的內核支持TAP網絡接口: /dev/net/tun 文件存在則說明支持。如果沒有這樣的文件,可以嘗試手工創建:
sudo mkdir /dev/net
sudo mknod /dev/net/tun c 10 200
sudo /sbin/modprobe tun
首先,添加一個以太網橋設備:
sudo ip link add br0 type bridge
# 也可以使用:sudo brctl addbr br0添加網橋
# 要刪除網橋,執行: ip link delete br0
# 注意:網橋會在重啓後消失
# 啓用此網橋
sudo ip link set br0 up
# 爲網橋分配IP地址
sudo ip addr add 10.0.0.1 dev br0
# 在宿主機添加一條直接路由,便於它能和客戶機通信
sudo ip route add 10.0.0.0/8 dev br0
創建一個創建TAP設備並橋接到網橋的腳本:
#!/bin/sh
switch=br0
if [ -n "$1" ];then
# tunctl -u `whoami` -t $1
# 添加一個tap設備,在我的機器上不需要,原因見下面
# ip tuntap add $1 mode tap user `whoami`
# 不知道從什麼時候開始,QEMU會在執行此腳本之前就創建好tap設備,因此會報下面的錯誤
# ioctl(TUNSETIFF): Device or resource busy
# 啓動tap設備
ip link set $1 up
# brctl addif $switch $1
# 將網橋和tap設備進行橋接
ip link set $1 master $switch
exit 0
else
echo "Error: no interface specified"
exit 1
fi
創建一個生成隨機MAC地址的腳本:
#!/bin/bash
# generate a random mac address for the qemu nic
printf 'DE:AD:BE:EF:%02X:%02X\n' $((RANDOM%256)) $((RANDOM%256))
啓動客戶機的腳本:
#!/bin/bash
# $1 base name of virtual disk
# $2 memory size
# $3 tap device id
mac=`/usr/bin/qemu-genmac`
src=/usr/bin/qemu-ifup
sudo qemu-system-x86_64 -enable-kvm -device e1000,netdev=$3,mac=$mac -netdev tap,id=$3,script=$src,downscript=no \
-hda ~/Vmware/KVM/$1.img -m $2
爲上面的腳本文件添加可執行權限:
sudo chmod +x /usr/bin/qemu-ifup-br0
sudo chmod +x /usr/bin/qemu-genmac
sudo chmod +x /usr/bin/qemu-start-br0
執行下面的命令,啓動一臺客戶機(或者更多虛擬機,但是命令中的tap0要更換爲不同的名字):
/usr/bin/qemu-start centos7-base 512 tap0
修改客戶機的IP地址,使用10.0.0.0/8網段:
TYPE=Ethernet
BOOTPROTO=static
DEFROUTE=yes
PEERDNS=yes
PEERROUTES=yes
IPV4_FAILURE_FATAL=no
IPV6INIT=no
NAME=ens3
UUID=d9f47102-b177-4a27-ae98-86f6939d6680
DEVICE=ens3
ONBOOT=yes
IPADDR=10.0.0.10
PREFIX=8
GATEWAY=10.0.0.1
現在可以互相ping客戶機和宿主機,應該可以正常連通了。
上一節介紹的這種基於TAP的私有橋接網絡,可以讓客戶機、宿主機相互連通,但是客戶機無法訪問互聯網。
要解決此問題,你可以選擇以下方法之一:
- 讓客戶機通過宿主機暴露的HTTP/SOCKS代理上網
- 配置宿主機的路由規則,設置好源地址轉換即可:
sudo sysctl -w net.ipv4.ip_forward=1
# 對客戶機網段進行源地址轉換
sudo iptables -t nat -A POSTROUTING -s 10.0.0.0/255.0.0.0 ! -d 10.0.0.0/255.0.0.0 -j MASQUERADE
此方式和私有橋接網絡類似,主要區別是,除了TAP設備橋接到網橋之外,以太網卡(例如eth0)也橋接到網橋(例如br1)。
你可以通過發行版的配置文件來配置網橋:
# 注意網絡管理器組件的影響
# 去掉 auto eth0,改爲:
auto br1
# 配置br1
iface br1 inet dhcp
bridge_ports eth0
bridge_stp off
bridge_maxwait 0
bridge_fd 0
# 這裏附加上原來屬於eth0的配置
或者基於腳本來配置:
sudo ip link add br1 type bridge
sudo ip link set br1 up
sudo ip link set eth0 master br1
# DHCP
sudo killall dhclient && sudo ip addr flush dev eth0
sudo dhclient br1
無論用哪種方式,都應該注意到eth0的IP地址需要轉移給br1,這樣才能確保網絡正常運作——br1必須在鏈路層接收到相關ARP請求,並決定是否需要轉發給客戶機,eth0沒有這種轉發能力。
如果eth0所在網絡是基於DHCP的,那麼客戶機配置爲DHCP後,會自動獲取公共IP地址。否則,需要手工設置客戶機的IP地址。
現在QEMU支持自動橋接TAP設備到宿主機的一個網橋,因此你不再需要編寫腳本,修改網絡後端爲bridge即可:
-netdev bridge,id=tap0,br=br0
注意,使用上述選項時,QEMU需要讀取配置文件/etc/qemu/bridge.conf,你只需在此文件中添加一行代碼:
allow br0
你可以編寫如下腳本自動創建網橋、配置iptables規則。示例:
# Create private bridge link
for QEMU
/sbin/ip
link
add
br0
type
bridge
/sbin/ip
link
set
br0
up
/sbin/ip
addr
add
10.0.0.1
dev
br0
/sbin/ip
route
add
10.0.0.0/8
dev
br0
# NAT for 10.0.0.0/8
/sbin/iptables
-t
nat
-A
POSTROUTING -s
10.0.0.0/255.0.0.0
!
-d
10.0.0.0/255.0.0.0 -j
MASQUERADE
# Create public bridge link
for QEMU
/sbin/ip
link
add
br1
type
bridge
/sbin/ip
link
set
br1
up
/sbin/ip
link
set
eth0
master
br1
/usr/bin/killall
dhclient
&&
/sbin/ip
addr
flush
dev
eth0
/sbin/dhclient
br1
關於MacVTap的相關知識,參考Linux知識集錦。建議和libvirt一起使用macvtap。
在使用libvirt時,客戶機(Domain)的網絡接口配置可以簡化爲:
<interface type='bridge'>
<mac address='DE:AD:BE:EF:F1:00'/>
<source bridge='br0'/>
<target dev='tap0'/>
<model type='virtio'/>
</interface>
可以使用libvrit的虛擬局域網,這樣宿主機上不會爲客戶機創建專門的tap設備,那些手工編寫的腳本也全都不需要了。虛擬網絡配置示例:
<network>
<name>default</name>
<uuid>9bae4de8-ca58-48c5-ba58-109aebf8b954</uuid>
<forward mode='nat'>
</forward>
<bridge name='virbr0' stp='on' delay='0'/>
<ip address='10.0.0.1' netmask='255.0.0.0'>
<dhcp>
<range start='10.0.0.100' end='10.0.0.200'/>
</dhcp>
</ip>
</network>
客戶機(Domain)的網絡接口配置示例:<interface type='network'>
<mac address='DE:AD:BE:EF:F1:00'/>
<source network='default'/>
<model type='virtio'/>
</interface>
另外,libvirt的虛擬網絡提供了DHCP功能,因此客戶機的IP地址不需要靜態設置。