Menu

  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay
  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay

libvirt学习笔记

4
Aug
2015

libvirt学习笔记

By Alex
/ in Virtualization
/ tags libvirt, 学习笔记
0 Comments
简介

libvirt是广泛使用的、通用虚拟化管理工具,它提供多种命令行工具、多种语言的编程API。

libvirt的目标是:提供一个通用、稳定的抽象层,来安全有效的远程管理一个节点(node)之上的域(domains),因此它需要提供全套的API来完成管理,这些API必须完成Domain的创建、修改、配置、监控、迁移、停止。

libvirt可以管理的虚拟化机制(hypervisor或container)包括:KVM/QEMU、Xen、LXC、OpenVZ、VirtualBox、VMware ESX/GSX、VMware Workstation/Player、Microsoft Hyper-V、IBM PowerVM。

名词术语
术语 说明
node 一台物理机器 
hypervisor 

node上面的一个软件层,它能虚拟化node,并在其上建立多个虚拟机 

libvirt通过所谓driver和各种不同的hypervisor打交道

domain 运行在受hypervisor管理的虚拟化node之上的一个操作系统,当基于容器虚拟化时,则是一个子系统
安装libvirt

libvirt的二进制组件可能已经随操作系统安装,如果没有,你可以:

Shell
1
sudo apt-get install libvirt-bin
辅助工具

可以安装virt-install,这是一个用来创建基于KVM、XEN或者Linux容器的客户机的工具:

Shell
1
sudo apt-get install virtinst

可以安装virt-manager,它提供了基于libvirt的图形化管理工具:

Shell
1
sudo apt-get install virt-manager

可以安装virt-viewer,它用于连接到虚拟机的Graphical Console:

Shell
1
2
3
4
# 安装
sudo apt-get install virt-viewer
# 使用
virt-viewer -c qemu:///system
使用Virsh

virsh是libvirt提供的一个命令行工具,利用它你可以通过命令行,交互式的管理你的虚拟机(Domain)。使用此命令,你可以创建、暂停、关闭domain,可以列出当前的domain。

libvirt会在宿主机上运行一个libvirtd守护进程,此进程可以被本地/远程的virsh调用。libvirtd则可以直接调用qemu-kvm来操控客户机。大部分virsh命令需要libvirtd处于运行状态才可用。

Domain管理

使用virsh的define、edit、start、shutdown|destroy、reboot、suspend、resumen、undefined子命令,分别可以定义、编辑、启动、关闭、暂停、唤醒、删除Domain。这些命令比较简单,参考virsh命令详解一节。

快照管理
快照分类

快照可以分为三个级别:

  1. 卷管理器(Volume Manager) 级别,例如LVM的Snapsot功能
  2. 文件系统级别,常用的Ext3不支持,OCFS2支持
  3. 文件级别,Raw格式的镜像不支持快照,qcow2格式则支持,且快照分为两类:
    1. 内部快照:保存在qcow2文件内部的快照:
      1. 虚拟机状态快照(VM State snapshot):整个虚拟机的状态,不仅仅是磁盘
      2. 磁盘状态快照(Disk State snapshot):仅仅针对磁盘的快照
    2. 外部快照:将原先(Backing)的qcow2镜像设置为只读,新的改变保存到另外的qcow2文件
内存快照

使用virsh save / virsh restore命令,可以仅仅将Domain的内存状态保存,然后停止Domain,最后恢复。恢复时假设磁盘没有任何改动:

Shell
1
2
3
4
5
# 保存内存快照
virsh save fedora-10 fedora-10.vmstate
 
# 恢复内存快照
virsh restore fedora-10.vmstate
内部快照

内部快照、外部快照使用同一组命令来管理的。这些快照默认包含内存、磁盘、设备等全部状态。内部快照示例:

Shell
1
2
3
4
5
6
7
8
9
10
11
12
# 创建一个快照
virsh snapshot-create fedora-10
# Domain snapshot 1473667716 created
 
# 列出Domain的快照
virsh snapshot-list fedora-10
#  Name                 Creation Time             State
# ------------------------------------------------------------
#  1473667716           2016-09-12 16:08:36 +0800 running
 
# 创建的是内部快照,可以使用底层命令查看
qemu-img info ~/Vmware/KVM/fedora-10/hda.img

注意,一旦创建了快照,Domain就不能被undefine。

要删除内部快照,可以执行:

Shell
1
2
virsh snapshot-delete fedora-10 1473667716
# Domain snapshot 1473667716 deleted
外部快照

执行下面的命令创建一个外部快照: 

Shell
1
2
3
# 这里我们仅针对vda磁盘创建了快照,内存状态没有做快照
snapshot-create-as fedora-10 blankos "Initial snapshot"
    --diskspec=vda,file=/home/alex/Vmware/KVM/fedora-10/blankos.vda.qcow2 --disk-only --atomic

现在查看客户机关联的块设备:

Shell
1
2
3
4
virsh domblklist fedora-10
# Target     Source
# ------------------------------------------------
# vda        /home/alex/Vmware/KVM/fedora-10/blankos.vda.qcow2

可以发现关联性转移到外部快照上了,原先的磁盘镜像成为Backing file。注意:Domain的后续写操作都发生在新创建的磁盘上

要删除外部快照,执行:

Shell
1
virsh snapshot-delete fedora-10 --metadata blankos
快照链管理

我们来创建三个快照:

Shell
1
2
3
4
DIR=/home/alex/Vmware/KVM/fedora-10
virsh snapshot-create-as fedora-10 snap0 "snap0" --diskspec=vda,file=$DIR/snap0.vda.qcow2 --disk-only --atomic
virsh snapshot-create-as fedora-10 snap1 "snap1" --diskspec=vda,file=$DIR/snap1.vda.qcow2 --disk-only --atomic
virsh snapshot-create-as fedora-10 snap2 "snap2" --diskspec=vda,file=$DIR/snap2.vda.qcow2 --disk-only --atomic

查看当前快照:

Shell
1
2
# 默认的,新创建的快照作为当前快照
virsh snapshot-current fedora-10 --name

查看快照链(Backing chain):

Shell
1
2
3
4
5
6
7
virsh snapshot-list fedora-10 --tree
# vda.qcow2 是base
# snap0
#   |
#   +- snap1
#       |
#       +- snap2    这个是top

libvrit支持多种方式来管理磁盘的快照链:

方式一:基于blockcommit,合并到base镜像

我们可以清理快照链条,将snap2、snap1、snap0中的变更都提交到vda.qcow2中

Shell
1
2
3
# 必须在Domain运行着的情况下执行命令
virsh blockcommit fedora-10 vda --base $DIR/vda.qcow2 --top $DIR/snap2.vda.qcow2 --wait --verbose
# 目前带--delete参数会导致 error: unsupported flags (0x2) in function qemuDomainBlockCommit

提交后,可以安全的删除快照及其元数据(snapshot-delete --metadata),libvrit是分开管理backing链和snapshot列表的。 

方式二:基于blockpull,合并到top镜像

也可以反过来,把base一直pull到top位置(必须是叶子节点)的snapshot,然后此snapshot就成为完整的磁盘镜像了(不依赖backing镜像): 

Shell
1
virsh blockpull fedora-10 --path $DIR/snap2.vda.qcow2 --base $DIR/vda.qcow2 --wait –verbose

方法三:基于blockcopy,可以在线迁移磁盘

首先,需要取消Domain定义,将其变为transient的:

Shell
1
2
3
4
# 导出Domain配置
virsh dumpxml --inactive fedora-10 $DIR/domain.xml
# 取消定义
virsh undefine fedora-10

然后执行拷贝:

Shell
1
2
3
# --shallow 浅拷贝,copy.vda.qcow2与snap2.vda.qcow2将具有相同的backing chain即base ⇦ snap0 ⇦ snap1
# --pivot  操作完成后,此Domain改用copy
virsh blockcopy --domain fedora-10 vda $DIR/copy.vda.qcow2 --wait --verbose --shallow --pivot

拷贝完成后,瞬时的Domain使用copy继续运行:

Shell
1
2
3
4
virsh domblklist fedora-10
# Target     Source
# ------------------------------------------------
# vda        /home/alex/Vmware/KVM/fedora-10/copy.vda.qcow2 

而原先的磁盘可以迁移走了。

远程访问

要通过virsh来访问远程宿主机上的Domain时,需要提供URI。URI的格式如下:

1
driver[+transport]://[username@][hostname][:port]/[path][?extraparameters]

URI各部分说明如下:

部分 说明
driver 驱动,不同驱动对应了不同的Hypervisor
transport

传输协议,主要包括以下几种:

  1. unix,使用Unix Domain Socket,仅能在本地使用,不加密,示例:
    1
    qemu+unix:///system?socket=/opt/libvirt/run/libvirt/libvirt-sock 
  2. ssh,通过SSH隧道进行连接,相当于通过SSH隧道在目标宿主机上执行Unix Domain Socket,示例:
    1
    qemu+ssh://root@tokyo.gmem.cc/system
  3. tcp,通过TCP进行远程连接,通过DIGEST-MD5进行加密,使用SASL/Kerberos进行身份验证,示例:
    1
    qemu+tcp://tokyo.gmem.cc/system 
  4. tls,类似于tcp,但是使用SSL对TCP进行加密,需要配置密钥和证书,使用SASL/Kerberos进行身份验证,示例:
    1
    qemu+tls://tokyo.gmem.cc/system

要连接到远程宿主机,可以使用-c选项或者connect子命令:

Shell
1
virsh -c qemu+ssh://root@zircon.local/system
使用unix传输

使用该transport时,需要注意配置文件:

/etc/libvirt/libvirtd.conf
1
2
3
4
# 这些项都是默认值
unix_sock_group = "libvirtd"
unix_sock_ro_perms = "0777"
unix_sock_rw_perms = "0770"

也就是说,用户必须加入到libvirtd组,才可以使用unix传输,否则会报错:error: Failed to connect socket to '/var/run/libvirt/libvirt-sock': Permission denied。执行下面的命令添加用户到组:

Shell
1
sudo usermod -a -G libvirtd alex

注意:连接到qemu时,不指定主机名默认使用unix socket。

使用tcp传输

在目标宿主机上,修改配置文件:

/etc/default/libvirt-bin
1
2
# 启用TCP监听
libvirtd_opts="-d -l"

然后再修改配置文件:

/etc/libvirt/libvirtd.conf
1
2
3
4
5
6
7
# 默认TCP监听是禁用的
listen_tcp = 1
# 可以修改监听地址和端口
listen_addr = "0.0.0.0"
tcp_port = "16509"
# 可以不启用验证,但是缺乏安全性,所有流量都是明文
auth_tcp = "none"

最后重启libvirtd即可。  

资源管理

相关文章:Linux知识集锦 - cgroup

libvirt基于cgroup来限制客户机对宿主机资源的访问。libvirt不会尝试加载任何controllers,它只会检测哪些controllers被mount。

QEMU驱动支持cpuset, cpu, memory, blkio, devices这几个controller,修改配置文件/etc/libvirt/qemu.conf可以针对QEMU禁用某些controller。

LXC驱动支持 cpuset, cpu, cpuacct, freezer, memory, blkio,devices 这几个controller, 其中cpuacct, devices, memory是必须的,如果这几个controller没有被mount则容器不会被启动。

cgroups布局

libvrit引入两个概念,以方便cgroups管理:

  1. partitions:不包含任何进程的cgroup,仅仅包含资源控制规则,它可以包含多个子目录,这些子目录要么是partition要么是consumers
  2. consumers:是包含了单个虚拟机/容器进程的cgroup

对于不使用systemd的宿主机,consumers命名规则为 $VMNAME.libvirt-{qemu,lxc} ,其中VMNAME为虚拟机的名称。默认的,所有consumer都挂在名为machine的partition下:

Shell
1
2
ls /sys/fs/cgroup/cpu/machine
# fedora-10.libvirt-qemu  ...

直到cgroups布局后,你就可以直接读写cgroups文件系统,来控制客户机的资源访问。但是virsh也提供了一些命令在运行时控制资源访问。

资源管理命令
  1. 对于CPU访问控制,可以使用virsh schedinfo命令
  2. 对于块设备的访问控制,可以使用virsh blkiotune命令
  3. 对于网卡流量的控制,可以使用domiftune或者tc命令
virsh命令详解

该命令最常见的调用形式为: virsh [OPTION]... <command> <domain> [ARG]... 。其中:

  1. command 是一个virsh子命令
  2. domain 是操控的虚拟机的名称、ID或者UUID
  3. ARG是针对特定子命令的参数
  4. OPTION为一般性选项
一般选项
选项 说明
-c --connect URI    连接到指定的URI,而不是默认的连接。此选项的效果如同调用了connect子命令
-d --debug LEVEL    设置调试级别,级别范围0-4,默认4
-k --keepalive-interval INTERVAL   设置确认服务器连接未断开的心跳的发送间隔,单位秒,设置为0则不检测
-K --keepalive-count COUNT    确认连接端口之前,发送心跳的次数
-l --log FILE 输出日志到文件
-q --quiet    安静模式,避免不必要的信息打印
-t --timing    为每个命令打印消耗的时间信息
一般子命令
子命令 说明
help

显示帮助信息:

Shell
1
2
3
4
# 列出子命令列表
virsh help
# 显示一个子命令的用法
virsh help define
quit, exit 退出交互式的Terminal
version 显示版本信息:libvir库版本、API版本、运行中的hypervisor版本
cd 改变当前目录,禁用与交互式的terminal
pwd 打印当前目录名
connect

connect [URI] [--readonly]

(重)连接到一个hypervisor,URI指明如何连接到hypervisor,例如:
xen:///    连接到本地XEN hypervisor
qemu:///system    以root身份连接到本地管理QEMU/KVM domain的hypervisor
qemu:///session   以普通用户身份连接到本地,管理他自己的QEMU/KVM domain
lxc:///    连接到本地的LXC容器

uri 打印当前连接到的hypervisor的URI
hostname 打印hypervisor的主机名
capabilities 打印一个描述当前连接到的hypervisor的能力(capabilities)的XML文档
list 列出存在的Domain,如果不指定参数,则打印所有运行中的Domain信息
Domain子命令
子命令 说明
autostart autostart [--disable] domain
用于配置一个Domain随着宿主机而启动
console console domain [devname] [--safe] [--force]
连接到客户机的虚拟串口控制台:
devname  设置为一个备选控制台、串口/并口设备的别名,如果不指定则连接到primary控制台
create create FILE [--console] [--paused] [--autodestroy] [--pass-fds N,M,...]
从XML文件FILE创建一个Domain。创建XML的简便方法是调用 dumpxml 子命令来获得既有实例的XML配置:
--paused    新的Domain将会暂停,不指定则运行
--console    创建后连接到Domain的console
--autodestroy    如果virsh断开到libvirt的连接,则自动销毁此domain
define define FILE
从XML文件FILE定义一个Domain,此Domain会注册,但是不会自动启动。如果Domain已经在运行,则对其配置的变更在下次启动时生效
undefine undefine domain [--managed-save] [--snapshots-metadata] [ {--storage volumes | --remove-all-storage} --wipe-storage]
解除一个Domain的定义,如果此Domain正在运行,它会被转换为transient的;如果Domain没有运行,则移除它的配置
desc desc domain [[--live] [--config] | [--current]] [--title] [--edit] [--new-desc  new_desc_msg]
显示或者修改Domain的描述、标题,标题通常比较简短
destroy destroy domain [--graceful]
立即终止一个Domain,客户机将没有反应时间,相当于拔掉机器的电源
--graceful    避免极度的手段销毁(SIGKILL),如果客户机一段时间后没有关闭,返回一个错误消息
reboot reboot domain [--mode MODE-LIST]
重启一个Domain,效果类似于执行reboot命令
reset reset domain
重置一个Domain,效果类似于按主机上的重置按钮,客户机将没有反应时间
shutdown shutdown domain [--mode MODE-LIST]
优雅的关闭Domain,此命令将和客户机协商以关机,因此不一定成功,可能消耗较长时间
start start name-or-uuid [--console] [--paused] [--autodestroy] [--bypass-cache] [--force-boot] [--pass-fds N,M,...]
启动一个已经定义的Domain:
--paused    此Domain将会暂停
--console    连接到客户机的控制台
--autodestroy    当virsh断开到libvirtd的连接后,自动销毁Domain
suspend suspend domain
暂停一个运行中的Domain,它会维持在内存中,但不再参与调度
resume resume domain
从暂停中恢复
dumpxml dumpxml domain [--inactive] [--security-info] [--update-cpu] [--migratable]
输出Domain的XML配置信息到屏幕:
--migratable    输出一个可迁移的配置
--inactive    dump出Domain下次启动时使用的配置,而不是当前正在使用的配置
--update-cpu    根据宿主机的CPU,更新Domain配置中的CPU部分
edit edit domain
编辑一个Domain的XML配置,并在下次启动Domain时生效
save

save domain state-file [--bypass-cache] [--xml file] [{--running | --paused}] [--verbose]

保存一个运行中的Domain的内存(而不是磁盘)状态到一个状态文件中,以便后续恢复。一旦被保存,则Domain不再继续运行,分配给Domain的内存可以被其它程序使用。该命令类似于Hibernate功能

state-file    状态文件路径
--bypass-cache    不包含文件系统缓存,会加快保存速度
--verbose    显示保存进度
--running    --paused    在恢复后,将Domain变为运行或暂停状态

可以基于domjobinfo子命令监控进度,或者利用domjobabort子命令取消保存,对当前Terminal发送SIGINT(Ctrl + C)也会取消保存

restore

restore state-file [--bypass-cache] [--xml file] [{--running | --paused}]

将Domain从virsh save状态中还原

domblkstat

domblkstat domain [block-device] [--human]

输出块设备的统计信息
block-device    块设备名称(<target dev='name'/>)或块源文件(<source file='name'/>)
--human    输出易读的格式

输出列说明:
rd_req    读操作次数
rd_bytes    读字节数
wr_req    写操作次数
wr_bytes    写字节数
errs    错误计数
flush_operations    刷出磁盘的操作次数
rd_total_times    读操作总计消耗ns数
wr_total_times    写操作总计消耗ns数
flush_total_times    刷出操作总计消耗ns数

举例: virsh domblkstat fedora-10 vda 

domblkerror

domblkerror domain

显示块设备错误

domblkinfo

domblkinfo domain block-device

显示块设备的尺寸相关信息

domblklist

domblkinfo domain block-device

以表格形式打印与Domian相关联的块设备的简要信息

blockcommit

blockcommit domain path [bandwidth] {[base] | [--shallow]} [top] [--delete] [--wait [--verbose] [--timeout seconds] [--async]]

减少backing镜像链条的长度,将top(最新的)中的变化提交到backing镜像中去。默认的:

  1. 此命令flatten整个链条
  2. 此命令立即返回,commit操作在后台进行,可以使用blockjob检查进度

path    磁盘的全限定路径,<target dev='name'/>的name或者<source file='name'/>的file
base top    如果指定之一或都指定,则限制commit操作链条的范围
--shallow    提交到top直接的backing镜像
--delete     操作完成后,删除被合并的文件
--wait    阻塞直到操作完成
--timeout    阻塞最多的秒数
--verbose    显示进度的详细信息
--async    尽快的返回,否则在完成commit后还要等待一些清理操作

blockpull

blockpull domain path [bandwidth] [base] [--wait [--verbose] [--timeout seconds] [--async]]

从backing镜像链生成一个磁盘。默认的:

  1. 此命令flatten整个链条
  2. 此命令立即返回,commit操作在后台进行,可以使用blockjob检查进度

path    磁盘的全限定路径,<target dev='name'/>的name或者<source file='name'/>的file
base   backing链条中的这一成员保留,仅它与top之间的backing镜像被合并到top

blockcopy

blockcopy domain path dest [bandwidth] [--shallow] [--reuse-external] [--raw] [--wait [--verbose] [{--pivot | --finish}] [--timeout seconds] [--async]]

拷贝磁盘的backing镜像链到dest。默认的:

  1. 此命令flatten整个链条
  2. 此命令立即返回,commit操作在后台进行,可以使用blockjob检查进度

domain    操作针对的Domain
path     操作针对的磁盘,全限定路径,<target dev='name'/>的name或者<source file='name'/>的file
bandwidth    带宽占用限制,MiB/s
--shallow  共享backing链,即对于base ⇦ snap0 ⇦ snap1,dest与src共享base ⇦ snap0
–pivot    转移,即拷贝完成后,Domain改用dest,不再使用src
--reuse-external    指定该选项则dest必须存在,且内容与resulting backing file等同
--raw    指定dest的格式,如果指定--reuse-external,则使用dest文件的格式

该命令主要用途是虚拟机的在线磁盘映像拷贝(live disk image copying)或镜像(mirroring),在存储迁移时很有用。应用场景包括:

  1. 在线磁盘存储迁移
  2. 在线磁盘映像、及其backing链的备份
  3. 高效的非共享( non-shared )存储迁移

一个blockcopy操作可以分为两个阶段:

  1. 所有的源磁盘内容被拷贝到dest。在此阶段,任务可以被取消,dest的状态没有保证
  2. source、dest的内容变得等同,它们将保持mirrored状态,直到调用blockjob  --abort以结束mirroring
domifstat

domifstat domain interface-device

输出网络接口的统计信息

输出列说明:
 rx_bytes 收字节数
 rx_packets收IP封包数
 rx_errs 错误封包数
 rx_drop 丢弃封包数
 tx开头的表示发送的统计数据

举例: virsh domifstat fedora-10 tap0 

domif-setlink

domif-setlink domain interface-device state [--config]

修改网络接口的状态
state    目标状态,up/down
--config    --persistent    仅仅修改Domain的持久化配置,不立即改变接口状态

domif-getlink

domif-getlink domain interface-device [--config]

获得网络接口的状态

domiflist

domiflist domain [--inactive]

以表格形式打印与Domian相关联的网络接口的简要信息

dommemstat

dommemstat domain [--period seconds] [[--config] [--live] | [--current]]

获得运行中的Domain的内存统计信息

blkdeviotune

blkdeviotune domain device [[--config] [--live] | [--current]] [[total-bytes-sec] | [read-bytes-sec] [write-bytes-sec]] [[total-iops-sec] | [read-iops-sec] [write-iops-sec]]

查询或者设置Domain的某个块设备的I/O参数:
domain   目标客户机
device    操作针对的磁盘,<target dev='name'/>的name或者<source file='name'/>的file
--total-bytes-sec    设置每秒总计吞吐量的限制,单位Byte
--read-bytes-sec    设置每秒读取吞吐量的限制,单位Byte
--write-bytes-sec   设置每秒写入吞吐量的限制,单位Byte
--total-iops-sec    设置每秒IO操作次数限制
--read-iops-sec    设置每秒读次数限制
--write-iops-sec   设置每秒写次数限制
--live    影响正在运行的客户机
--config    在下次重启后,影响非瞬时Domain
--current    影响当前客户机的状态

 domiftune

domiftune domain interface-device [[--config] [--live] | [--current]] [--inbound average,peak,burst] [--outbound average,peak,burst]

查询或者修改网络接口的带宽参数:
interface-device    目标网络接口
--inbound    --outbound   修改入站还是出站带宽参数,如果都不指定,则执行查询操作。average/peak以KiB/s解释,burst以单次burst消耗的KiB解释
        average  设置网络接口期望的平均速率
        peak 设置峰值速率限制
        burst 以峰值速率爆发式的传送数据,最多连续传送多是KiB
--live    影响当前正在运行的Domain

举例: virsh domiftune fedora-10 tap0 

schedinfo 

schedinfo domain [[--config] [--live] | [--current]] [[--set] parameter=value]...

schedinfo [--weight number] [--cap number] domain

显示或者设置Domain进程在宿主机中调度参数,可用参数:
LXC (posix scheduler):cpu_shares
QEMU/KVM (posix scheduler):cpu_shares, vcpu_period, vcpu_quota, emulator_period, emulator_quota
Xen (credit scheduler): weight, cap
ESX (allocation scheduler):reservation, limit, shares

--live    影响正在运行的Domain
--config    在下次重启后,影响非瞬时Domain
--set    执行设置操作

cpu_shares    处理器占用权重,范围0-262144,负值被转换为正值因此-1即262144,超过最大值都相当于262144
vcpu_period     对调度进行干预的周期,单位us,范围1000-1000000或者0,100000表示100ms干预一次
emulator_period
vcpu_quota    在干预周期内,进程能得到的时间片数,单位us,25000表示在一个周期内得到25ms的时间片
emulator_quota

screenshot

screenshot domain [imagefilepath] [--screen screenID]

对Domain当前Console进行截屏,并保存到文件

send-key

send-key domain [--codeset codeset] [--holdtime holdtime] keycode..

将keycode的序列转换为按键动作,并发送到Domainkey,可用参数:
code可以是数字或者是codeset中的符号名称
--holdtime  每个按键按下持续的毫秒数
--codeset 指定代码集,默认Linux

举例:

Shell
1
2
3
4
5
6
# 发送右侧Ctrl + C到fedora-10
virsh send-key fedora-10 KEY_RIGHTCTRL KEY_C
# 发送Ctrl + Alt + Del
virsh send-key debian-20 KEY_LEFTCTRL KEY_LEFTALT KEY_DELETE
# 发送TAB,按下1秒
virsh send-key fedora-10 --holdtime 1000 0xf 
迁移子命令
子命令 说明
migrate

migrate [--live] [--offline] [--direct] [--p2p [--tunnelled]] [--persistent] [--undefinesource] [--suspend] [--copy-storage-all] [--copy-storage-inc] [--change-protection] [--unsafe] [--verbose] [--compressed] [--abort-on-error] domain desturi [migrateuri] [graphicsuri] [listen-address] [dname] [--timeout seconds] [--xml file]

将客户机迁移到另外一台宿主机上,可用参数:
--live  在线迁移,迁移期间,源宿主机上的客户机不被暂停
--p2p  使用点对点迁移
--direct  使用直接迁移
--tunnelled  使用隧道迁移
--offline  不在目标机器上启动客户机,也不再源机器上停止客户机。通常用于inactive客户机的迁移
--persistent  让Domain在目标机器上持久化
--undefinesource  取消Domain在源机器上的定义
--suspend  让客户机在目标机器上停留在suspend状态
--copy-storage-all  提示不使用共享存储,进行完整的磁盘拷贝
--copy-storage-inc  提示不使用共享存储,进行增量的磁盘拷贝(即源、目标的共享backing file不拷贝)
注意:上面两个参数要求目标镜像文件存在于目标机器的对应位置
--change-protection  确保在迁移完成前,不得对Domain执行不兼容的配置更改。如果Hypervisor支持,该参数会自动包含,手工指定此参数的话,如果Hypervisor不支持change protection,则迁移操作被libvirt拒绝
--verbose  显示迁移进度
--compressed  对于需要在迁移过程中反复传输的内存页,执行压缩
--abort-on-error  当发生软错误(Soft error,例如I/O错误)时取消迁移
--unsafe  允许不安全的迁移(可能导致数据损坏)
desturi  目标宿主机的连接URI。对于点对点迁移,该参数是源宿主机看到的目标主机的URI;对于普通迁移则是客户端看到的URI
domain  被迁移的客户机名称
dname   在迁移时,修改Domain的名称
--xml  在目标机器上,为Domain指定机器特定的Domain配置信息,例如存储卷的名称(对于同一底层存储两台机器的命名可能不同)
--timeout  如果在线迁移超过指定的秒数,则Domain被强制suspend,然后转入离线迁移,必须和--live联用
listen-address  设置目标主机上的Hypervisor监听迁移请求的地址和端口

注意:

  1. Hypervisor可能不支持所有的迁移类型,例如QEMU不支持直接迁移
  2. 如果迁移可能导致数据损坏,libvirt可能拒绝迁移请求。例如对于QEMU,除非设置磁盘的缓存模式(cache mode)为none或者存储位于一致性的集群文件系统(GFS或者GPFS),迁移会被拒绝。这时可以使用--unsafe强制迁移
migrate-compcache

migrate-compcache domain [--size bytes]

设置或者取得在线迁移过程中,重复的用来压缩被传输的内存页的缓存的大小,单位字节
--size 如果指定此参数,则为设置操作,否则为读取操作

migrate-setmaxdowntime

migrate-setmaxdowntime domain downtime

设置domain在线迁移时,能够容忍的最大宕机时间,单位毫秒

migrate-compcache  
migrate-setspeed

migrate-setspeed domain bandwidth

设置domain迁移到其它宿主机上时,最大使用的带宽,单位MiB/s

migrate-getspeed

migrate-getspeed domain

获取domain迁移时的最大带宽

设备子命令
子命令 说明
attach-device

attach-device domain FILE [[[--live] [--config] | [--current]] | [--persistent]]

为domain添加一个新的设备
FILE  设备的XML配置,以interface/disk之类的元素为根元素
--config   在永久Domian下次启动时生效
--live  影响运行中的Domain
--current  影响当前Domain状态
注意:--config --live可以联用,但是--current不能和它们联用

attach-disk

attach-disk domain source target [[[--live] [--config] | [--current]] | [--persistent]] [--driver driver] [--subdriver subdriver] [--cache cache] [--type type] [--mode
mode] [--config] [--sourcetype soucetype] [--serial serial] [--wwn wwn] [--rawio] [--address address] [--multifunction] [--print-xml]

添加一个磁盘,参数与磁盘的XML配置元素对应

attach-interface

attach-interface domain type source [[[--live] [--config] | [--current]] | [--persistent]] [--target target] [--mac mac] [--script script] [--model model] [--config]
[--inbound average,peak,burst] [--outbound average,peak,burst]

添加一个磁盘,参数与网络接口的XML配置元素对应

detach-device

detach-device domain FILE [[[--live] [--config] | [--current]] | [--persistent]]

移除一个设备

detach-disk

detach-disk domain target [[[--live] [--config] | [--current]] | [--persistent]]

移除一个磁盘

detach-interface

detach-interface domain type [--mac mac] [[[--live] [--config] | [--current]] | [--persistent]]

移除一个网络接口

update-device

update-device domain file [--force] [[[--live] [--config] | [--current]] | [--persistent]]

更新设备的配置,可用参数:
--force  强制设备更新

change-media

change-media domain path [--eject] [--insert] [--update] [source] [--force] [[--live] [--config] | [--current]]

改变光驱或者软驱的媒体(盘),可用参数:
path  光驱/软驱的全限定路径或者名称,例如hdc
source   更换的新媒体镜像的路径
--eject  弹出媒介
--insert  插入媒介

举例:

Shell
1
2
3
4
5
6
7
virsh # domblklist debian-20
# Target     Source
# ------------------------------------------------
# vda        /home/alex/Vmware/KVM/debian-20/vda.qcow2
# hdd        /home/alex/Software/OS/debian-8.6.0-amd64-netinst.iso
 
change-media debian-20 --eject --live
虚拟网络子命令
子命令 说明
net-autostart

net-autostart network [--disable]

启用/禁用虚拟网络的自动启动

net-create

net-create file

从XML配置创建一个临时的虚拟网络并立即启动

net-define

net-define file

从XML配置文件创建一个永久的虚拟网络

net-destroy

net-destroy network

根据名称或者UUID停止一个虚拟网络

net-dumpxml

net-dumpxml network [--inactive]

导出虚拟网络的XML配置

net-edit

net-edit network

编辑一个虚拟网络的配置

net-info

net-info network

显示一个虚拟网络的详细信息

net-list

net-list [--inactive | --all] [--persistent] [<--transient>] [--autostart] [<--no-autostart>]

显示虚拟网络的列表

net-start

net-start network

启动一个虚拟网络

net-undefine

net-undefine network

取消虚拟网络的定义

net-update

net-update network command section xml [--parent-index index] [[--live] [--config] | [--current]]

更新虚拟网络的指定配置片断,可用参数:
section 片断名称:bridge, domain, ip, ip-dhcp-host, ip-dhcp-range, forward, forward-interface, forward-pf, portgroup, dns-host, dns-srv,这些名称对应了虚拟网络XML配置的相应子代元素
xml  配置片断,要么是XML片断文本,要么是包含XML片断的文件名称
--live  影响正在运行的虚拟网络
--config  在永久虚拟网络重启后生效

快照子命令

这些子命令用来管理Domain的快照,快照是Domain的磁盘、内存、设备在某一个时刻的状态,这些状态可以在未来恢复。每个快照由唯一性的名字来识别。

子命令 说明
snapshot-create

snapshot-create domain [xmlfile] {[--redefine [--current]] | [--no-metadata] [--halt] [--disk-only] [--reuse-external] [--quiesce] [--atomic] [--live]}

为domain创建一个快照,创建快照期间客户机通常处于暂停状态。新创建的快照将成为当前快照,可以通过子命令snapshot-current查看。子命令参数:
xmlfile    指定此快照的属性,一般仅仅包含name、description元素,如果指定--disk-only则可以包含disks元素
--halt    Domain在创建快照后,进入停止(inactive)状态
--disk-only    仅仅对磁盘进行快照,与--halt联用则所有没有flush到磁盘的数据丢失
--redefine    如果指定,则snapshot-dumpxml生成的所有元素均有效,可用于跨机器迁移快照层次
--no-metadata    丢弃源数据,快照不被标记为current,除非后续使用--redefine,无法恢复快照
--reuse-external    重用此文件指向的外部快照,目标文件必须存在
--live    创建快照时,客户机将处于运行状态,仅支virsh snapshot-create-as fedora-10 snap2 "snap2" --diskspec=vda,file=$DIR/snap2.vda.qcow2 --disk-only --atomic
持外部检查点

snapshot-create-as

snapshot-create-as domain {[--print-xml] | [--no-metadata] [--halt] [--reuse-external]} [name] [description] [--disk-only [--quiesce]] [--atomic] [[--live] [--memspec memspec]] [--diskspec] diskspec]...

以指定的name和description创建快照
--print-xml    仅创建snapshot-create使用的xmlfile,不进行快照创建
--memspec=[file=]name[,snapshot=type]    控制内存快照的创建方式,type可取值none,internal,external
--diskspec=disk[,snapshot=type][,driver=type][,file=name]    控制--disk-only和外部检查点创建外部文件的方式。此参数可以出现多次(对应Domain配置中磁盘的个数)
--atomic  libvrit保证原子性操作,快照要么完整的创建成功,要么彻底失败

snapshot-current

snapshot-current domain {[--name] | [--security-info] | [snapshotname]}

查看和设置当前快照:如果不指定snapshotname,则输出Domain当前快照的XML。否则把snapshotname设置为当前快照
--name    仅仅输出当前快照的名称,而非XML
--security-info    在XML中包含安全性敏感的信息

snapshot-edit

snapshot-edit domain [snapshotname] [--current] {[--rename] | [--clone]}

编辑snapshotname的XML信息
--current    编辑当前快照,如果同时指定snapshotname,则snapshotname被设置为当前快照
--rename    允许设置快照名称
--clone    改变快照名称后,会创建一份快照元数据的克隆

snapshot-info

snapshot-info domain {snapshot | --current}

输出当前快照或者指定快照的基本信息

snapshot-list

snapshot-list domain [--metadata] [--no-metadata] [{--parent | --roots | [{--tree | --name}]}] [{[--from] snapshot | --current} [--descendants]] [--leaves] [--no-leaves] p[--inactive] [--active] [--disk-only] [--internal] [--external]

列出Domain所有可用的快照,默认输出列:快照名称、创建时间、Domain的状态

--parent   输出parent列,显示父快照名称
--roots    仅列出没有parent快照的那些快照
--tree    以树状输出结果,显示快照父子关系
--name    仅仅显示快照名称,与--tree互斥
--from    仅仅显示指定快照的子快照,联用--current则显示当前快照的子快照
--descendants    包含所有后代快照
--leaves    仅仅输出没有子代的那些快照
--inactive    --active    根据快照中Domain的状态过滤
--internal   --external    根据内/外部快照过滤

snapshot-dumpxml

snapshot-dumpxml domain snapshot [--security-info]

显示指定快照的XML

snapshot-parent

snapshot-parent domain {snapshot | --current}

显示父快照的名称

snapshot-revert

snapshot-revert domain {snapshot | --current} [{--running | --paused}] [--force]

恢复Domain到指定的快照状态,此最后一次快照以来对Domain的变更将消失
--running    --paused 通常恢复后Domain处于创建快照时的状态,这两个参数可以覆盖之
--force    强制恢复快照

snapshot-delete

snapshot-delete domain {snapshot | --current} [--metadata] [{--children | --children-only}]

删除Domian的快照
--children   删除指定的快照及其子代
--children-only    仅仅删除子代,指定的快照本身被保留
--metadata    仅仅删除libvirt管理的元数据,不理会快照文件

存储池子命令
子命令 说明
find-storage-pool-sources

find-storage-pool-sources type [srcSpec]

返回一个XML,描述所有能够找到的type类型的存储池
srcSpec  包含额外限制条件的XML

find-storage-pool-sources-as

find-storage-pool-sources-as type [host] [port] [initiator]

类似上面,host port initiator限制查询条件

pool-autostart

pool-autostart pool-or-uuid [--disable]

配置存储池的自动启动

pool-build

pool-build pool-or-uuid [--overwrite] [--no-overwrite]

构建一个存储池
--overwrite  --no-overwrite 仅仅用于文件系统池,指定覆盖时,如果文件系统已经存在于目标设备上,mkfs也会执行,既有文件系统将被破坏

pool-create

pool-create file

从配置文件定义并启动一个存储池

pool-create-as

pool-create-as name --print-xml type [source-host] [source-path] [source-dev] [source-name] [<target>] [--source-format format]

类似上面,但是通过命令行来指定所需参数:
--print-xml  打印生成的池的XML配置

pool-define

pool-define file

定义,但不启动池

pool-define-as

pool-define-as name --print-xml type [source-host] [source-path] [source-dev] [source-name] [<target>] [--source-format format]

类似上面,但是通过命令行来指定所需参数:
--print-xml 打印生成的池的XML配置

pool-destroy

pool-destroy pool-or-uuid

停止一个存储池,池中的数据不会消失

pool-delete

pool-delete pool-or-uuid

销毁池使用的所有资源,但是池本身仍然存在,你可以在其中存储新的卷

pool-dumpxml

pool-dumpxml [--inactive] pool-or-uuid

打印池的配置信息

pool-edit

pool-edit pool-or-uuid

编辑池的XML配置

pool-info

pool-info pool-or-uuid

显示池的基本信息

pool-list

pool-list [--inactive] [--all] [--persistent] [--transient] [--autostart] [--no-autostart] [[--details] [<type>]

列出libvirt所知道的全部存储池

pool-name
pool-uuid

pool-name uuid
pool-uuid pool

显示指定uuid对应的池名称,或者显示池名称对应的UUID 

pool-refresh

pool-refresh pool-or-uuid

刷新池中卷的列表

pool-start

pool-start pool-or-uuid

启动一个存储池

pool-undefine

pool-undefine pool-or-uuid

解除一个存储池的定义

vol-create

vol-create pool-or-uuid FILE [--prealloc-metadata]

在池中创建一个卷,对于基于目录/文件系统的池,卷的本质就是一个镜像文件。可用参数:
pool-or-uuid 池的名称或者UUID
FILE  卷的配置XML
--prealloc-metadata  预分配元数据,用于qcow2之类不支持完全分配的镜像格式,该参数可以提高性能

示例: vol-create default definitions/volumes/fedora-10.xml 

vol-create-from

vol-create-from pool-or-uuid FILE [--inputpool pool-or-uuid] vol-name-or-key-or-path [--prealloc-metadata]

创建一个卷,使用另外一个卷vol-name-or-key-or-path作为输入

vol-create-as

vol-create-as pool-or-uuid name capacity [--allocation size] [--format string] [--backing-vol vol-name-or-key-or-path] [--backing-vol-format string] [--prealloc-metadata]

创建一个卷,从命令行参数读取配置信息

Shell
1
virsh vol-create-as v12n1 centos7-base 128G --format qcow2
vol-clone

vol-clone [--pool pool-or-uuid] vol-name-or-key-or-path name [--prealloc-metadata]

克隆一个既有的卷,没有vol-create-from强大但是比它简单。可用参数:
--pool  指定在其中创建新卷的池
vol-name-or-key-or-path  被克隆的卷
name  新的卷的名称

vol-delete

vol-delete [--pool pool-or-uuid] vol-name-or-key-or-path

删除一个卷,底层的镜像文件将被删除,示例:

Shell
1
vol-delete --pool default fedora-10.qcow2
vol-upload

vol-upload [--pool pool-or-uuid] [--offset bytes] [--length bytes] vol-name-or-key-or-path local-file

上传本地文件的内容到一个卷,可用参数:
--pool  目标存储池
vol-name-or-key-or-path  目标卷
local-file  被上传的本地文件
--offset  在存储卷的什么位置开始写入数据
--length  写入数据的长度

vol-download

vol-download [--pool pool-or-uuid] [--offset bytes] [--length bytes] vol-name-or-key-or-path local-file

下载存储卷中的内容到本地文件

vol-wipe

vol-wipe [--pool pool-or-uuid] [--algorithm algorithm] vol-name-or-key-or-path

擦除一个卷的内容,可用参数:
--algorithm  擦除算法:zero nnsa dod bsi gutmann schneier pfitzner7 pfitzner33 random

vol-dumpxml

vol-dumpxml [--pool pool-or-uuid] vol-name-or-key-or-path

打印卷的XML配置,示例:

Shell
1
vol-dumpxml --pool default coreos.qcow2
vol-info

vol-info [--pool pool-or-uuid] vol-name-or-key-or-path

显示卷的基本信息

vol-list

vol-list [--pool pool-or-uuid] [--details]

列出一个存储池中的卷,示例:

Shell
1
vol-list default
vol-pool

vol-pool [--uuid] vol-key-or-path

根据卷的名称或者路径,返回其所在存储池的信息
--uuid 返回UUID而不是池名称

vol-path

vol-path [--pool pool-or-uuid] vol-name-or-key

返回指定卷的路径

vol-name
vol-key

vol-name vol-key-or-path
vol-key [--pool pool-or-uuid] vol-name-or-path

查询卷的名称或者key

vol-resize

vol-resize [--pool pool-or-uuid] vol-name-or-path pool-or-uuid capacity [--allocate] [--delta] [--shrink]

重新设定卷的容量,以字节为单位,可用参数:
--allocate  分配空间,否则新容量是稀疏的
capacity  新的容量,如果指定了--delta,则是增加的容量
--shrink  缩小卷大小,不指定此参数,降低容量会报错

结合QEMU/KVM

模拟器QEMU和hypervisor KVM可以被libvirt管理。

QEMU驱动

如果driver检测到/usr/bin/qemu-system-*则QEMU可用;如果driver检测到设备节点/dev/kvm和可执行文件/usr/bin/qemu-kvm则支持KVM全虚拟化和客户机硬件加速。

QEMU的驱动是一个多实例驱动,包含一个系统级别的特权驱动(system实例)和多个用户级别的非特权驱动。驱动的URI的协议名为qemu,URI示例:

1
2
3
4
5
6
7
8
9
10
11
12
# 本地访问per-user的实例
qemu:///session
# 本地访问per-user的实例
qemu+unix:///session
# 本地访问系统级实例
qemu:///system
# 本地访问系统级实例
qemu+unix:///system
# 基于 TLS/x50的远程访问
qemu://example.com/system
# 基于SSH隧道远程访问
qemu+ssh://root@example.com/system
导入/出QEMU配置
导入

virsh domxml-from-native 命令可以将QEMU命令行选项转换为libvirt的Domain配置格式:

  1. 将QEMU命令行保存到文件qemu.cmd: /usr/bin/qemu-system-x86_64 -name fedora-10 -enable-kvm -cpu Haswell -daemonize -display none -m 512 -drive file=/home/alex/Vmware/KVM/fedora-10/hda.img,index=0,media=disk,if=virtio -netdev bridge,id=tap0,br=br0 -device virtio-net-pci,netdev=tap0,mac=DE:AD:BE:EF:F1:00 
  2. 执行命令: virsh domxml-from-native qemu-argv ~/Vmware/KVM/fedora-10/qemu.cmd ~/Vmware/KVM/fedora-10/domain.xml 

生成的配置文件内容如下:

domain.xml
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
  <name>fedora-10</name>
  <uuid>51480ab5-864e-4eb7-9e1c-55b56105139e</uuid>
  <memory unit='KiB'>524288</memory>
  <currentMemory unit='KiB'>524288</currentMemory>
  <vcpu placement='static'>1</vcpu>
  <os>
    <type arch='x86_64' machine='pc'>hvm</type>
  </os>
  <features>
    <acpi/>
  </features>
  <cpu mode='custom' match='exact'>
    <model fallback='allow'>Haswell</model>
  </cpu>
  <clock offset='utc'/>
  <on_poweroff>destroy</on_poweroff>
  <on_reboot>restart</on_reboot>
  <on_crash>destroy</on_crash>
  <devices>
    <emulator>/usr/bin/qemu-system-x86_64</emulator>
    <disk type='file' device='disk'>
      <driver name='qemu' type='raw'/>
      <source file='/home/alex/Vmware/KVM/fedora-10/hda.img'/>
      <target dev='vda' bus='virtio'/>
    </disk>
    <controller type='usb' index='0'/>
    <controller type='pci' index='0' model='pci-root'/>
    <input type='mouse' bus='ps2'/>
    <input type='keyboard' bus='ps2'/>
    <graphics type='sdl'/>
    <video>
      <model type='cirrus' vram='9216' heads='1'/>
    </video>
    <memballoon model='virtio'/>
  </devices>
  <qemu:commandline>
    <qemu:arg value='-daemonize'/>
    <qemu:arg value='-display'/>
    <qemu:arg value='none'/>
    <qemu:arg value='-netdev'/>
    <qemu:arg value='bridge,id=tap0,br=br0'/>
    <qemu:arg value='-device'/>
    <qemu:arg value='virtio-net-pci,netdev=tap0,mac=DE:AD:BE:EF:F1:00'/>
  </qemu:commandline>
</domain>

可以看到,很多QEMU选项没有对应到常规的Domain配置元素,而是使用qemu:commandline的形式,在启动客户机的时候直接传递给QEMU了。因此,新建客户机时,不要使用这种导入配置的方法,而应调用libvirt API或者手工创建Domain的XML配置。

注意:virsh自动导入得到XML配置存在不少错误,需要调整后才能使用。上例修改后的配置如下:

XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
    <name>fedora-10</name>
    <memory unit='KiB'>524288</memory>
    <currentMemory unit='KiB'>524288</currentMemory>
    <vcpu placement='static'>1</vcpu>
    <os>
        <type arch='x86_64' machine='pc'>hvm</type>
    </os>
    <features>
        <acpi/>
    </features>
    <cpu mode='custom' match='exact'>
        <model fallback='allow'>SandyBridge</model>
    </cpu>
    <clock offset='utc'/>
    <on_poweroff>destroy</on_poweroff>
    <on_reboot>restart</on_reboot>
    <on_crash>destroy</on_crash>
    <devices>
        <emulator>/usr/bin/qemu-system-x86_64</emulator>
        <disk type='file' device='disk'>
            <driver name='qemu' type='qcow2'/> <!-- 镜像格式要设置对 -->
            <source file='/home/alex/Vmware/KVM/fedora-10/hda.img'/>
            <target dev='vda' bus='virtio'/>
            <boot order='1'/>  <!-- 要指定启动顺序,否则不会从此硬盘启动系统 -->
        </disk>
        <controller type='usb' index='0'/>
        <controller type='pci' index='0' model='pci-root'/>
        <!-- 把基于qemu:commandline的网络配置改为标准的Domain配置方式 -->
        <interface type='bridge'>
            <mac address='DE:AD:BE:EF:F1:00'/>
            <source bridge='br0'/>
            <target dev='tap0'/>
            <model type='virtio'/>
        </interface>
        <!-- 添加串口控制台配置,去掉视频相关配置 -->
        <serial type='pty'>
            <target port='0'/>
        </serial>
        <console type='pty'>
            <target type='serial' port='0'/>
        </console>
        <input type='mouse' bus='ps2'/>
        <input type='keyboard' bus='ps2'/>
        <memballoon model='virtio'/>
    </devices>
</domain> 
导出

类似的,可以把Domain配置文件转换为QEMU命令行:

Shell
1
virsh domxml-to-native qemu-argv ~/Vmware/KVM/fedora-10/domain.xml
Domain配置文件
基本信息

libvirt使用XML文件描述一个Domain的全部配置信息:

XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!--
任何Domain配置的根元素都是domin,它有两个属性:
    type 驱动(hypervisor)的类型,可选值:xen | kvm | qemu | lxc | kqemu
    id 正在运行的Domain的唯一标识,整数。非活动Domain没有id
-->
<domain type='kvm' id='1'>
    <!-- 在单个node下唯一的虚拟机名字 -->
    <name>fedora-10</name>
    <!-- RFC 4122兼容的、虚拟机的全局唯一标识,如果在定义/创建虚拟机时不指定,则会自动生成一个  -->
    <uuid>4dea22b31d52d8f32516782e98ab3fa0</uuid>
    <!-- 虚拟机的简短描述,可以多行 -->
    <title>A short description</title>
    <!-- 虚拟机的描述-->
    <description>Some human readable description</description>
    <!-- 元数据:由应用程序使用,子树必须使用应用程序自己的名字空间 -->
    <metadata>
        <app1:foo xmlns:app1="http://app1.org/app1/">..</app1:foo>
        <app2:bar xmlns:app2="http://app1.org/app2/">..</app2:bar>
    </metadata>
</domain>

下面介绍如何配置Domain各方面的细节。

Domain启动配置

虚拟机可以不同的方式启动,各有其优缺点。

BIOS bootloader

对于全虚拟化的hypervisor可以选择通过BIOS启动,BIOS定义启动优先级,来确定从软盘、硬盘、光驱还是网络获取启动镜像(boot image)。配置示例:

XML
1
2
3
4
5
6
7
8
9
10
<os>
    <type>hvm</type>
    <loader readonly='yes' secure='no' type='rom'>/usr/lib/xen/boot/hvmloader</loader>
    <nvram template='/usr/share/OVMF/OVMF_VARS.fd'>/var/lib/libvirt/nvram/guest_VARS.fd</nvram>
    <boot dev='hd'/>
    <boot dev='cdrom'/>
    <bootmenu enable='yes' timeout='3000'/>
    <smbios mode='sysinfo'/>
    <bios useserial='yes' rebootTimeout='0'/>
</os>

各子元素的说明如下:

元素 说明
type

指定需要被启动的虚拟机操作系统的类型:
hvm    操作系统设计用来在裸金属(bare metal)上运行,即未修改的操作系统,需要全虚拟化
xen     表示支持Xen3的Guest API的操作系统,别名linux
exe    基于容器的虚拟化
uml   用户模式的Linux

元素属性:
arch    指定被虚拟化的CPU的体系结构
machine    指定机器类型

loader

指定虚拟机固件镜像的(宿主机的)绝对路径。用于Xen全虚拟化、QEMU/KVM的BIOS文件路径设置

元素属性:
readonly    固件是否只读,yes/no
type    rom/pflash,告知hypervisor把固件映射到客户机内存的什么位置,如果loader指定UEFI镜像,则该属性应为pflash
secure    指示固件实现了安全启动(secure boot)特性

boot

此元素可以出现多次,其dev属性可以是fd/hd/cdrom/network,用来确定优先从哪种设备启动虚拟机,写在最前面的那种设备优先级高

如果同一类型的设备配置了多个,它们将依据总线顺序排列,第一个被标记为可启动的。该元素难以细粒度的控制启动顺序,可以使用Per-device的boot元素代替(后者与此元素互斥)

smbios 产生客户机可见的SMBios信息,引用一个sysinfo元素
bootmenu 可以使用一个交互式的启动菜单
bios useserial:可以设置为yes/no,来启用/禁用Serial Graphics Adapter ,SGA允许用户通过串口看到BIOS信息
rebootTimeout   如果启动失败,多久重启,单位毫秒,-1禁止重启
Container boot

当启动基于容器虚拟化的Domain时,需要指定一个init程序:

XML
1
2
3
4
5
6
7
8
<os>
    <type arch='x86_64'>exe</type>
    <!-- init binary -->
    <init>/bin/systemd</init>
    <!-- argumsnts -->
    <initarg>--unit</initarg>
    <initarg>emergency.service</initarg>
</os>

如果你要启用user namespace映射,可以:

XML
1
2
3
4
5
<idmap>
    <!-- start容器内第1个用户的ID,target容器内第1个用户映射到宿主机的用户的ID,count容器最多映射宿主机多少用户-->
    <uid start='0' target='1000' count='10'/>
    <gid start='0' target='1000' count='10'/>
</idmap>
指定SMBIOS信息
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<os>
    <smbios mode='sysinfo'/>
</os>
<!-- type属性必须,它的值决定子元素第布局-->
<sysinfo type='smbios'>
    <bios>
        <entry name='vendor'>LENOVO</entry>
    </bios>
    <system>
        <entry name='manufacturer'>Fedora</entry>
        <entry name='product'>Virt-Manager</entry>
        <entry name='version'>0.9.4</entry>
    </system>
    <baseBoard>
        <entry name='manufacturer'>LENOVO</entry>
        <entry name='product'>20BE0061MC</entry>
        <entry name='version'>0B98401 Pro</entry>
        <entry name='serial'>W1KS427111E</entry>
    </baseBoard>
</sysinfo>
CPU分配

配置示例如下:

XML
1
2
3
4
5
<vcpu placement='static' cpuset="1-4,^3,6" current="1">2</vcpu>
<vcpus>
    <vcpu id='0' enabled='yes' hotpluggable='no' order='1'/>
    <vcpu id='1' enabled='no' hotpluggable='yes'/>
</vcpus>
vcpu元素

此元素定义客户机最大的虚拟CPU的数量,有效值的范围是1-hypervisor支持的最大数量。属性说明如下:

属性 说明
cpuset

逗号分隔的,Domain进程及虚拟CPU默认能够Pin到的物理CPU序号。可以用-指定范围,^进行排除

Domain进程及虚拟CPU的Pin策略可以由cputune指定,如果cputune的emulatorpin属性被设置,则当前属性被忽略
对于指定了vcpupin的虚拟CPU,当前属性被忽略

current

启用比最大数量更少的虚拟CPU

placement

指定Domain进程的CPU placement mode,static/auto

vcpus

此元素控制每个单独虚拟CPU的状态,每个vcpu子元素对应一个虚拟CPU,vcpu子元素的属性说明如下:

属性 说明
id 虚拟CPU的标识符,libvirt在其它地方(例如pinning)引用之。有效值范围0到最大虚拟CPU数-1之间
enabled 控制此虚拟CPU是否启用,yes/no
hotpluggable 此虚拟CPU是否可以热拔插,注意,所有enabled=no的CPU都是可以热拔插的,yes/no
order 此虚拟CPU的顺序号,此值越小,则CPU越先被热插
CPU微调

cputune元素可以对Domain的虚拟CPU进行微调,配置示例如下:

XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<cputune>
    <vcpupin vcpu="0" cpuset="1-4,^2"/>
    <vcpupin vcpu="1" cpuset="0,1"/>
    <vcpupin vcpu="2" cpuset="2,3"/>
    <vcpupin vcpu="3" cpuset="0,4"/>
    <emulatorpin cpuset="1-3"/>
    <iothreadpin iothread="1" cpuset="5,6"/>
    <iothreadpin iothread="2" cpuset="7,8"/>
    <shares>2048</shares>
    <period>1000000</period>
    <quota>-1</quota>
    <emulator_period>1000000</emulator_period>
    <emulator_quota>-1</emulator_quota>
    <iothread_period>1000000</iothread_period>
    <iothread_quota>-1</iothread_quota>
    <vcpusched vcpus='0-4,^3' scheduler='fifo' priority='1'/>
    <iothreadsched iothreads='2' scheduler='batch'/>
</cputune>

各子元素说明如下:

元素 说明
vcpupin 指定虚拟CPU与物理CPU之间的Pin关系。不指定的情况下,虚拟CPU可以Pin到所有物理CPU
emulatorpin 模拟器线程被Pin到哪些物理CPU,模拟器线程是Domain执行序列中除了虚拟CPU、IO线程之外的部分
如果此属性未指定,且vcpu的cpuset属性没有设置,则默认Pin到所有物理CPU
iothreadpin IO线程被Pin到哪些物理CPU
如果此属性未指定,且vcpu的cpuset属性没有设置,则默认Pin到所有物理CPU
shares 此Domain占用CPU时间的权重,这是一个相对值,它对应的具体时长取决于其它虚拟机的设置
2048比1024多获得1倍CPU时间
period

用来指定强制的interval,单位毫秒,可取值 [1000, 1000000]之间。在一个period内,Domain中的每个虚拟CPU消耗的带宽不得超过quota

仅QEMU 0.9.4+、LXC 0.9.10+支持

quota 用来指定最大允许带宽,单位毫秒,可取值 [1000, 18446744073709551],负值表示不限制
emulator_period 与上面类似,但是针对模拟器线程
emulator_quota
iothread_period 与上面类似,但是针对IO线程
iothread_quota
vcpusched 指定特定虚拟CPU的调度类型:
scheduler,调度类型,可选batch, idle, fifo, rr
vcpus,针对的虚拟CPU
priority,对于实时调度器fifo, rr必须,值范围一般1-99之间,取决于宿主机内核
iothreadsched 与上面类似
IO线程分配

IO线程是一种专门的事件循环线程,用于提高磁盘Block I/O的scalability,这些线程会分配给支持的磁盘设备。每个物理CPU只有1-2个IO线程,每个IO线程也可能分配给多个磁盘设备。配置示例:

XML
1
2
3
4
5
6
7
8
9
10
<!-- 分配给Domain使用的IO线程数-->
<iothreads>4</iothreads>
 
<!-- 定义每个IO线程的ID -->
<iothreadids>
    <iothread id="2"/>
    <iothread id="4"/>
    <iothread id="6"/>
    <iothread id="8"/>
</iothreadids>
内存分配

配置示例如下:

XML
1
2
3
<maxMemory slots='16' unit='KiB'>1524288</maxMemory>
<memory unit='KiB'>524288</memory>
<currentMemory unit='KiB'>524288</currentMemory>

各元素说明如下:

元素 说明
memory 在启动时,分配给Domain的内存的数量
unit,单位,Ki按1024,K按1000计,可用B|KB|MB|GB|TB
dumpCore,在Domain崩溃后,是否包含其内存映像到生成的coredump中,仅QEMU
maxMemory 运行期间允许的最大内存,仅QEMU
currentMemory 当前实际分配给Domain的内存,默认与memory相同
内存Backing

memoryBacking元素控制虚拟内存页如何映射到宿主机的内存页,配置示例:

XML
1
2
3
4
5
6
7
8
9
<memoryBacking>
    <hugepages>
        <!-- 为了除4之外的numa节点分配1G的巨页 -->
        <page size="1" unit="G" nodeset="0-3,5"/>
        <page size="2" unit="M" nodeset="4"/>
    </hugepages>
    <nosharepages/>
    <locked/>
</memoryBacking>

子元素说明如下:

元素 说明
hugepages

在Linux 64位系统里面,默认内存是以4K的页面(Page)来管理的,当系统有非常多的内存的时候,管理这些内存的消耗就比较大。HugePage使用2M大小的页面来减小管理开销。HugePage管理的内存并不能被Swap,这就避免了swap引发的性能问题。如果系统经常碰到因为swap引发的性能问题可以考虑启用HugePage

告知hypervisor,客户机的内存基于hugepage而不是宿主机Native页大小来分配。从1.2.5开始,可以为每个numa节点更加细致的设置huagepages:
size/unit    指定huge页的大小
nodeset    给于特定numa节点hugepage

nosharepages nosharepages,用于提示hypervisor禁止此Domain的共享页面(内存合并,KSM)
locked

如果hypervisor支持,设置此元素可以禁止属于Domain的内存页被swap out

对于QEMU/KVM,使用此设置前你需要设置memtune的hard_limit,并且设置maxMemory=Domain所需内存+QEMU进程本身所需内存。注意:启用locked且设置过多的内存可能导致宿主机内核内存溢出

内存微调

memtune提供Domain的内存微调参数,如果不设置这些参数,则使用OS提供的默认值。对于QEMU/KVM,这些参数限制包含QEMU进程本身的内存消耗

XML
1
2
3
4
5
6
<memtune>
    <hard_limit unit='G'>1</hard_limit>
    <soft_limit unit='M'>128</soft_limit>
    <swap_hard_limit unit='G'>2</swap_hard_limit>
    <min_guarantee unit='bytes'>67108864</min_guarantee>
</memtune>

子元素说明如下:

元素 说明
hard_limit 限制客户机能够使用的最大内存,对于QEMU/KVM建议不要设置
soft_limit 出现内存争用时的软限制
swap_hard_limit 最大内存 + 交换文件总大小限制
min_guarantee 确保最小低分配给客户机的内存,仅VMware ESX、OpenVZ支持
NUMA节点微调

numatune元素通过控制针对Domain进程的numa策略来影响宿主机的性能,配置示例如下:

XML
1
2
3
4
5
<numatune>
    <memory mode="strict" nodeset="1-4,^3"/>
    <memnode cellid="0" mode="strict" nodeset="1"/>
    <memnode cellid="2" mode="preferred" nodeset="2"/>
</numatune>

子元素说明如下:

元素 说明
memory 如何在numa主机上为Domain分配内存:
mode,可选值interleave,strict,preferred,默认strict
nodeset,影响的numa节点
memnode 类似,针对单个numa节点设置
块I/O微调

blkiotune元素能够微调Domain的Blkio cgroup可调整参数,如果不指定此元素,则使用OS默认值。配置示例如下:

XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<blkiotune>
    <weight>800</weight>
    <device>
        <path>/dev/sda</path>
        <weight>1000</weight>
    </device>
    <device>
        <path>/dev/sdb</path>
        <weight>500</weight>
        <read_bytes_sec>10000</read_bytes_sec>
        <write_bytes_sec>10000</write_bytes_sec>
        <read_iops_sec>20000</read_iops_sec>
        <write_iops_sec>20000</write_iops_sec>
    </device>
</blkiotune>

子元素说明如下:

元素 说明
weight Domain的整体I/O权重,值范围[100, 1000],自2.6.39内核之后,值范围[10, 1000]
device

此元素可以有多个,用来设置Domain针对宿主机每一个块设备的I/O权重。子元素:
path    宿主机块设备文件的路径
weight    针对此块设备,Domain的权重
read_bytes_sec    读吞吐量限制,bytes/s
write_bytes_sec    写吞吐量限制,bytes/s
read_iops_sec    读次数限制,bytes/s
write_iops_sec    写次数限制,bytes/s

CPU型号与拓扑

对CPU型号、特性的要求,以及它的拓扑结构的要求,可以使用如下方式配置:

XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<cpu match='exact'>
    <model fallback='allow'>core2duo</model>
    <vendor>Intel</vendor>
    <topology sockets='1' cores='2' threads='1'/>
    <feature policy='disable' name='lahf_lm'/>
</cpu>
 
<cpu mode='host-model'>
    <model fallback='forbid'/>
    <topology sockets='1' cores='2' threads='1'/>
</cpu>
 
<cpu mode='host-passthrough'>
    <feature policy='disable' name='lahf_lm'/>
</cpu>

cpu元素是描述客户机CPU需求的容器元素,它的属性如下:

属性 说明
match 宿主机必须满足客户机CPU需求的严格程度,可选值:
minimum    满足CPU型号、feature的要求
exact    完全满足
strict    除非完全满足,否则客户机不被创建
mode

用于简化客户机的配置,让它尽量和宿主机CPU匹配。可选值:

  1. custom(94.73%性能):这种模式下虚拟机 CPU 指令集数最少,故性能相对最差,但是它在热迁移时跨不同型号 CPU 的能力最强。此外,custom 模式下支持用户添加额外的指令集
  2. host-model(95.84%性能):libvirt 根据当前宿主机 CPU 指令集从配置文件 /usr/share/libvirt/cpu_map.xml 选择一种最相配的 CPU 型号。在这种 mode 下,虚拟机的指令集往往比宿主机少,性能相对 host-passthrough 要差一点,但是热迁移时,它允许目的节点 CPU 和源节点的存在一定的差异
  3. host-passthrough(100%性能):libvirt 令 KVM 把宿主机的 CPU 指令集全部透传给虚拟机。因此虚拟机能够最大限度的使用宿主机 CPU 指令集,故性能是最好的。但是在热迁移时,它要求目的节点的 CPU 和源节点的一致

关于热迁移,理论上来说:

  1. host-passthrough: 要求源节点和目的节点的指令集完全一致
  2. host-model: 允许源节点和目的节点的指令集存在轻微差异
  3. custom: 允许源节点和目的节点指令集存在较大差异

从实际情况来看,公司不同时间采购的 CPU 型号可能不相同;不同业务对 CPU 型号的要求也有差异。虽然互联网多采用 intel E5 系列的 CPU,但是该系列的 CPU 也有多种型号,常见的有 Xeon,Haswell,IvyBridge,SandyBridge 等等。即使是 host-model,在这些不同型号的 CPU 之间热迁移虚拟机也可能失败。所以从热迁移的角度,在选择 host-mode 时:

  1. 需要充分考虑既有宿主机类型,以后采购扩容时,也需要考虑相同问题
  2. 除非不存在热迁移的场景,否则不应用选择 host-passthrough
  3. host-model 下不同型号的 CPU 最好能以 aggregate hosts 划分,在迁移时可以使用 aggregate filter 来匹配相同型号的物理机
  4. 如果 CPU 型号过多,且不便用 aggregate hosts 划分,建议使用 custom mode
元素 说明
model

指定客户机要求的CPU型号,可用型号的列表在/usr/share/libvirt/cpu_map.xml
如果hypervisor不能使用精确的CPU,libvirt会自动fallback到特性最接近的CPU

属性列表:
fallback,是否允许fallback,可选值allow,forbid,默认allow
vendor_id,设置客户机看到的vendor_id,必须12字符长,典型值AuthenticAMD、GenuineIntel

vendor 设置客户机要求CPU的厂商,可用厂商列在cpu_map.xml
topology 规定总的CPU插槽数,每个CPU的核心数,每个核心的硬件线程数
feature 可以包含多个这样的元素,用来细粒度的规定CPU的特性:
name    特性名称
policy    策略:force强制要求此特性,即使宿主机不支持;require如果宿主机支持则要求此特性;optional可有可无;disable此特性在客户机上禁用;forbid如果宿主机支持此特性则客户机失败
numa

仅适用于QEMU/KVM,指定客户机的numa拓扑,举例:

XML
1
2
3
4
<numa>
    <cell id='0' cpus='0-3' memory='512000' unit='KiB'/>
    <cell id='1' cpus='4-7' memory='512000' unit='KiB' memAccess='shared'/>
</numa>

 每个cell子元素对应一个numa 节点(cell):
cpus指定节点对应的CPU范围
memory指定节点本地内存大小
memAccess控制内存被映射为shared还是private,此属性仅针对基于hugepage的内存

事件配置

你可能需要覆盖某些事件发生时采取的动作,注意并非所有hypervisors支持所有事件和动作。使用 virsh reboot 或者 virsh shutdown 可以触发事件。配置示例:

XML
1
2
3
4
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>restart</on_crash>
<on_lockfailure>poweroff</on_lockfailure>

 事件类型采用元素表示:

元素 说明
on_poweroff 指定客户机请求断开电源时采取的动作
on_reboot 指定客户机请求重启时采取的动作
on_crash 指定客户机崩溃时采取的动作

这几种事件都支持的动作包括:

  1. destroy,终止Domain并释放一切相关资源
  2. restart,Domain被终止,并以相同的配置再次启动
  3. preserve,Domain被终止但是其资源被保留供分析
  4. rename-restart,以另外一个名字重启Domain

on_crash支持额外的动作:

  1. coredump-destroy,崩溃Domain的core被dump出来,然后destroy
  2. coredump-restart,崩溃Domain的core被dump出来,然后重启
电源管理

仅QEMU支持,强制启用/禁止客户机BIOS的电源管理功能:

XML
1
2
3
4
5
6
<pm>
    <!-- 是否启用ACPI的S4睡眠状态 -->
    <suspend-to-disk enabled='no'/>
    <!-- 是否启用ACPI的S3睡眠状态 -->
    <suspend-to-mem enabled='yes'/>
</pm>
Hypervisor特性

Hypervisor能够启用/禁用一些CPU/机器特性。配置示例:

XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<features>
    <pae/> <!-- 物理地址扩展,允许32位客户机访问超过4GB内存 -->
    <acpi/> <!-- 对于电源管理有用,例如可以优雅的关闭KVM客户机 -->
    <apic/> <!-- 支持可编程的中断请求管理 -->
    <hap/>  <!-- 根据state=on|off,启禁硬件辅助paging -->
    <privnet/> <!-- 总是创建私有的网络命名空间,如果任何网络接口设备被添加,则自动设置。仅和容器虚拟化相关 -->
    <hyperv> <!-- 调整Windows客户机的性能,仅QEMU2.x -->
        <relaxed state='on'/>
        <vapic state='on'/>
        <spinlocks state='on' retries='4096'/>
        <vpindex state='on'/>
        <runtime state='on'/>
        <synic state='on'/>
        <reset state='on'/>
        <vendor_id state='on' value='KVM Hv'/>
    </hyperv>
    <kvm>
        <hidden state='on'/> <!-- 从标准的MSR发现中隐藏KVM的Hypervisor -->
    </kvm>
    <pvspinlock state='on'/>  <!-- 通知客户机,宿主机支持半虚拟化的自旋锁 -->
</features>
时间保持

客户机的时间通常是基于宿主机时间来初始化的,大部分OS期望硬件中存储的是UTC时间,然而Windows期望的则是“本地时间”。

配置示例:

XML
1
2
3
4
5
6
<clock offset='localtime'>
    <timer name='rtc' tickpolicy='catchup' track='guest'>
        <catchup threshold='123' slew='120' limit='10000'/>
    </timer>
    <timer name='pit' tickpolicy='delay'/>
</clock>

clock的offset属性控制客户机的时间如何与宿主机同步:

  1. utc,客户机启动时总是基于UTC时间来同步
  2. localtime,客户机启动时基于宿主机的timezone配置来同步时间
  3. timezone,客户机基于指定的时区来同步
  4. variable,客户机的时钟相对于UTC或者localtime(由basis属性指定,默认utc)具有一定的偏移,偏移量单位秒,由adjustment指定
设备配置

提供给客户机的所有设备,都在 <devices> 元素中配置。本章后续内容讲述各种设备的XML配置信息。

模拟器路径

可以使用下面的元素来指定模拟器全限定的路径:

XML
1
2
3
<devices>
    <emulator>/usr/lib/xen/bin/qemu-dm</emulator>
</devices>

capabilities的XML配置指明了特定Domain类型-体系结构组合对应的最佳模拟器。

软/硬/光盘

任何软盘、硬盘、光盘或者半虚拟化的驱动器,都是通过 disk 元素来指定的。配置示例:

XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
<devices>
    <!-- 后端支持是文件的磁盘,使用外部快照 -->
    <disk type='file' snapshot='external'>
        <!-- xen的驱动微调 -->
        <driver name="tap" type="aio" cache="default"/>
        <!-- 磁盘的源——文件的位置 -->
        <source file='/var/lib/xen/images/fv0' startupPolicy='optional'>
            <seclabel relabel='no'/>
        </source>
        <!-- 磁盘在客户机中的期望设备名,总线类型 -->
        <target dev='hda' bus='ide'/>
        <!-- IO节流阀 -->
        <iotune>
            <total_bytes_sec>10000000</total_bytes_sec>
            <read_iops_sec>400000</read_iops_sec>
            <write_iops_sec>100000</write_iops_sec>
        </iotune>
        <!-- 该磁盘可以作为启动盘,顺序2 -->
        <boot order='2'/>
        <!-- 启用磁盘加密 -->
        <encryption type='...'>
            ...
        </encryption>
        <shareable/><!-- 磁盘可以被多个Domain共享-->
        <serial>...</serial><!-- 指定磁盘序列号 -->
    </disk>
    <!-- 该磁盘的源位于网络上 -->
    <disk type='network'>
        <!-- QEMU驱动微调,指定AIO基于pthread -->
        <driver name="qemu" type="raw" io="threads" ioeventfd="on" event_idx="off"/>
        <!-- 基于sheepdog的网络源 -->
        <source protocol="sheepdog" name="image_name">
            <host name="hostname" port="7000"/><!-- 磁盘源所在的主机 -->
        </source>
        <target dev="hdb" bus="ide"/>
        <boot order='1'/>
        <transient/>
        <address type='drive' controller='0' bus='1' unit='0'/>
    </disk>
    <disk type='network'>
        <driver name="qemu" type="raw"/>
        <source protocol="rbd" name="image_name2">
            <host name="hostname" port="7000"/>
            <snapshot name="snapname"/>
            <config file="/path/to/file"/>
        </source>
        <target dev="hdc" bus="ide"/>
        <!-- 提供网络身份验证信息 -->
        <auth username='myuser'>
            <secret type='ceph' usage='mypassid'/>
        </auth>
    </disk>
    <!-- 定义一个光驱设备 -->
    <disk type='block' device='cdrom'>
        <driver name='qemu' type='raw'/>
        <target dev='hdd' bus='ide' tray='open'/>
    </disk>
    <!-- 定义一个光驱设备,使用ISO镜像文件 -->
    <disk type='file' device='cdrom'>
        <driver name='qemu' type='raw'/>
        <!-- 使用镜像文件 -->
        <source file='/home/alex/Vmware/KVM/coreos.iso'/>
        <target dev='hdd' bus='ide'/>
        <readonly/>
    </disk>
    <!-- 从libvirt管理的存储池中取得源 -->
    <disk type='volume' device='disk'>
        <driver name='qemu' type='qcow2'/>
        <source pool="default" volume="coreos.qcow2" />
        <target dev='vda' bus='virtio'/>
        <boot order='1'/>
    </disk>
    <!-- 来自iscsi池的源 -->
    <disk type='network' device='disk'>
        <driver name='qemu' type='raw'/>
        <source protocol='iscsi' name='iqn.gmem.cc.example:iscsi-nopool/2'>
            <host name='example.com' port='3260'/>
        </source>
        <auth username='myuser'>
            <secret type='iscsi' usage='libvirtiscsi'/>
        </auth>
        <target dev='vda' bus='virtio'/>
    </disk>
    <!-- 来自iscsi池的源,lun与disk不同的是,客户机的请求直接pass through到物理设备 -->
    <disk type='network' device='lun'>
        <driver name='qemu' type='raw'/>
        <source protocol='iscsi' name='iqn.gmem.cc.example:iscsi-nopool/1'>
            <host name='example.com' port='3260'/>
        </source>
        <auth username='myuser'>
            <secret type='iscsi' usage='libvirtiscsi'/>
        </auth>
        <target dev='sdb' bus='scsi'/>
    </disk>
    <disk type='file' device='disk'>
        <driver name='qemu' type='qcow2'/>
        <source file='/var/lib/libvirt/images/domain.qcow'/>
        <!-- 多级嵌套的backing store -->
        <backingStore type='file'>
            <format type='qcow2'/>
            <source file='/var/lib/libvirt/images/snapshot.qcow'/>
            <backingStore type='block'>
                <format type='raw'/>
                <source dev='/dev/mapper/base'/>
                <backingStore/>
            </backingStore>
        </backingStore>
        <target dev='vdd' bus='virtio'/>
    </disk>
</devices>
disk元素属性
属性 说明
type

磁盘的来源(source)类型。有效值:file,block,dir,network,volume

其中volume表示磁盘的来源是存储池中的一个卷

device

客户机看到的磁盘类别。有效值:floppy,disk,cdrom,lun,默认disk

仅当type=block|network且protocol=iscsi时;或者type=volume且使用protocol=iscsi、mode=host的source pool时可设置为lun,lun的行为与disk相同,除了来自客户机的SCSI命令被接收并pass through到物理设备。lun仅被raw设备识别,不能被分区识别

rawio 仅当device=lun时使用,是否启用rawio,有效值:yes/no
snapshot 指定快照行为:
internal    使用内部快照
external    使用外部快照
no    该磁盘不参与快照
 source子元素

 指定磁盘的来源(source),其包含的属性依赖于disk的type属性:

disk.type 属性 说明
file file 指定虚拟磁盘对应的镜像文件的全限定路径
block dev 指定虚拟设备映射到的宿主机块设备的全限定路径
dir dir 指定宿主机目录,此目录作为客户机的磁盘使用
network protocol 访问磁盘映像的协议,有效值:nbd、iscsi、rbd、sheepdog、gluster
name 如果protocol=rbd、sheepdog、gluster,则此属性必须,用于指定什么卷/镜像被使用
对于protocol=iscsi,name可能包含一个逻辑单元号( logical unit number,LUN),例如xx:iscsi-pool/1,不指定LUN则默认0
volume pool 指定由libvirt管理的存储池(storage pool)的名称,磁盘来源位于此池中
volume 指定用作磁盘来源的,由libvirt关联的存储卷( storage volume )的名称
mode 指示如何将LUN暴露为磁盘来源:
direct
host,默认值

source子元素可以包含以下子元素:

子元素 说明
host 当disk.type=network时,可以包含若干各host子元素,用来指定需要连接的主机。host具有以下属性:
name,主机名
port,监听端口
transport,传输协议类型
socket,UNIX套接字路径
backingStore子元素 

紧跟着source元素,用于指定磁盘使用的backing store ,backing store是构成磁盘的逻辑成分(类似于QEMU的backing file)。如果不指定此元素,则意味着source是自包含的。backingStore元素的属性列表如下:

属性 说明
type backing store使用的磁盘类型,类似于disk.type
index  

backingStore可以有下列子元素:

元素 说明
format 其type属性指定backing store内部的镜像格式,例如raw、qcow2
source 类似于disk.source
backingStore 如果此backing store也不是自包含,而依赖于其它backing store时,使用此元素递归的指定
target子元素

此子元素控制虚拟磁盘在什么总线/设备下暴露给客户机。属性如下:

属性 说明
dev 磁盘在客户机下的逻辑名称,此名称并不确保映射到相应的设备名称,你只能将其作为设备的“顺序提示”
bus 设置模拟的磁盘类型,有效值包括ide、scsi、virtio、xen、usb、sata、sd。如果不指定,bus的值根据dev的风格来推断,例如hda可以推断出ide
tray 可移动磁盘(光盘、软盘)的一个状态字段,有效值包括open、closed,默认closed
removable 设置USB磁盘的可移除标记,on/off,默认off
iotune子元素

针对单块磁盘进行IO微调,与 blkiotune 功能类似,但是后者针对Domain全局。

目前可设置的微调项都是针对QEMU的IO throttling微调,这些微调由子元素指定,取值0表示无限制。子元素列表:

元素 说明
total_bytes_sec 每秒钟I/O吞吐量的限制。total_bytes_sec不能与后两者同时出现
read_bytes_sec
write_bytes_sec
total_iops_sec 每秒钟I/O操作次数的限制。total_iops_sec不能与后两者同时出现
read_iops_sec
write_iops_sec
total_bytes_sec_max 与上面类似,但是限制最大值
read_bytes_sec_max
write_bytes_sec_max
total_iops_sec_max
write_iops_sec_max
size_iops_sec
driver子元素

与hypervisor驱动相关的更多细节配置,属性列表:

属性 说明
name    type

如果hypervisor支持多个backend驱动,则name属性指定primary后端驱动的名称,而type则指定一个子类型。例如:

  1. xen支持的name有tap、tap2、phy、file,支持type有aio
  2. QEMU仅支持name为qemu,而支持raw、bochs、qcow2、qed等type
cache

控制IO缓存策略,有效值包括

  1. default 由hypervisor自动选择。qemu-kvm 1.2-默认writethrough。之后的版本,对于客户机驱动ide/scsi/virtio来说,默认值可能是 writethrough
  2. none 相当于直接使用宿主机的物理磁盘缓存,性能不错
  3. writethrough 数据直接写入磁盘(O_DSYNC)里,不使用缓存;在数据更新时,同时写入缓存Cache和后端存储。此模式的优点是操作简单;缺点是因为数据修改需要同时写入存储,数据写入速度较慢
  4. writeback 在数据更新时只写入缓存Cache(不使用O_DSYNC、O_DIRECT)。只在数据被替换出缓存时,被修改的缓存数据才会被写到后端存储。此模式的优点是数据写入速度快,因为不需要写存储;缺点是一旦更新后的数据未被写入存储时出现系统掉电的情况,数据将无法找回
  5. directsync,写入磁盘时,qemu-kvm将使用O_DSYNC + O_DIRECT。速度慢
  6. unsafe 任何时候都不要在生产环境使用,cache flush不会传播到宿主机,因此任何意外的VM关机都会摧毁虚拟机文件系统

最后两种几乎不会使用

writethrough、none、directsync的安全性好,只要客户机操作系统是现代且行为正常的 —— 必要时会执行flush

writeback的安全性次之,它给提示后端写缓存的存在,依赖于客户机发送必须的flush命令来保证客户机磁盘的数据完整性 —— 这是现代文件系统应有的正常行为。但是,在报告(给客户端应用程序)IO操作完成,到数据提交到宿主机磁盘,存在一个时间窗口。如果宿主机宕机,可能导致数据丢失

unsafe安全性差,和writeback的差异在于,客户机的flush命令被忽略

性能上: writeback > none > writethrough

error_policy 当磁盘出现读写错误时hypervisor的处理策略,有效值:stop、report、ignore、enospace
io

控制AIO的策略,QEMU支持:

  1. threads:用户空间异步IO的实现,其实它不是真正的异步IO,是通过启动一定数量的 blocking IO线程来模拟异步IO
  2. native:Kernel native AIO :,Kernel的原生态异步IO实现

native的性能更好

copy_on_read

当读取backing文件时,是否将读取的内容复制到当前的镜像文件中,当backing文件位于慢速网络中时可以设置为on

仅用于QEMU/KVM

iothread 将磁盘分配给Domain的iothreads元素定义的IO线程
boot子元素

用于指定该磁盘是可启动的,order属性指定其启动顺序。

encryption子元素

指定卷如何被加密。

readonly子元素

指定此磁盘不能被客户机修改,对于device=cdrom的设备默认true。

shareable子元素

假设hypervisor和OS支持的话,指示此设备可以被多个Domain共享。指定此元素,应当同时禁用磁盘的缓存。

transient子元素

指示当客户机退出时,对磁盘的所有修改将回退。对于某些hypervisor,把磁盘标记为transient会禁止快照与迁移。

serial子元素

指定磁盘的序列号。

wwn子元素

指定磁盘的世界范围名称(World Wide Name),此值必须唯一,由16位16进制数字组成。

vendor子元素

指定磁盘的生产厂商,不超过8个可打印字符

product子元素

指定磁盘的产品名称,不超过16个可打印字符

address子元素

很多设备可以提供一个address 子元素,来指明设备挂载客户机虚拟总线的什么位置上。如果不指定address,libvirt会生成一个合适的地址。该子元素的属性列表如下:

属性 说明
type 必须,有效值包括:
pci    可配额外属性domain/bus/slot/function/multifunction
drive    可配额外属性controller/bus/target/unit
ccid     用于智能卡,可配额外属性bus/slot
usb    可配额外属性bus/port,其中port位点号分隔的单字节数字,例如1.2或者2.1.3.1
bus 磁盘绑定到的总线,对于PCI范围在0-0xff之间,对于其它为2位的bus号
slot 磁盘绑定到总线上的slot,范围在0x0-0x1f之间,对于其它为2位的slot号
function 磁盘的function号,范围在0-7之间
auth子元素

对于disk.type=network,且protocol为rbd、iscsi的磁盘,可以指定此子元素,提供访问磁盘源时使用的凭据。

blockio子元素

用于QEMU/KVM,覆盖块设备的属性。属性列表:

属性 说明
logical_block_size 报告给客户机的逻辑块大小。对于Linux来说,BLKSSZGET ioctl会返回此值,表示最小单元的磁盘IO大小
physical_block_size 报告给客户机的物理块大小。对于Linux来说,BLKPBSZGET ioctl会返回此值,表示硬件扇区的大小
文件系统配置

使用filesystem元素可以把宿主机上的目录直接暴露给客户机访问,配置示例:

XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<devices>
    <filesystem type='template'>
        <source name='my-vm-template'/><!-- OpenVZ模板名称 -->
        <target dir='/'/>
    </filesystem>
    <filesystem type='mount' accessmode='passthrough'>
        <driver type='path' wrpolicy='immediate'/>
        <source dir='/export/to/guest'/> <!-- 指定宿主机目录 -->
        <target dir='/import/from/host'/>
        <readonly/>
    </filesystem>
    <filesystem type='file' accessmode='passthrough'>
        <driver name='loop' type='raw'/>
        <driver type='path' wrpolicy='immediate'/>
        <source file='/export/to/guest.img'/>
        <target dir='/import/from/host'/>
        <readonly/>
    </filesystem>
</devices>

filesystem元素的属性列表:

属性 说明
type

指定文件系统的来源,有效值包括:
mount    默认值,挂载到客户机的一个宿主机目录,支持LXC、OpenVZ、QEMU/KVM
template    OpenVZ模板
file    一个宿主机文件被作为镜像,挂载到客户机,仅LXC
block    一个宿主机块设备,挂载到客户机,仅LXC
ram    一个内存文件系统
bind    绑定宿主机中的一个目录到客户机的一个目录,仅LXC

 accessmode 访问源的安全模式,有效值包括:
passthrough    默认值,基于客户机用户的权限访问源
mapped    基于hypervisor(QEMU进程)的权限访问源
squash     类似于passthrough,只是忽略chown之类操作的错误

子元素列表:

元素 说明
driver

指定hypervisor驱动的更多细节,属性列表:
type    如果hypervisor支持多种backend驱动,使用该属性指定primary后端驱动的名称
format    指定格式类型

对于LXC,支持type=loop&format=raw,或者type=nbd;QEMU支持type=path|handle,不支持format

source 标注宿主机上的资源,该资源暴露为客户机的文件系统,属性列表:
name    仅用于filesystem.type=template,指定模板的名字
dir    仅用于filesystem.type=mount,指定宿主机目录
usage    仅用于filesystem.type='ram' 以KiB(可以使用units属性指定单位)限制内存用量
target 对于QEMU,指定文件系统在客户机的挂载点
readonly 文件系统对于客户机是否只读,仅QEMU/KVM
space_hard_limit 文件系统对于客户机的容量软硬限制
space_soft_limit
网络接口配置

有几种方式(type)来指定客户机能够看到的网络接口,网络接口的容器元素是 interface 。每个interface元素可以拥有一个address子元素,指定其在PCI上的slot。interface元素的属性列表:

属性 说明
type 网络接口的类型,有效值:
network    虚拟网络
bridge    直接桥接VM到局域网
trustGuestRxFilters

如果设置为true,则宿主机能够检测到并信任来自客户机的关于接口MAC地址变更的报告,并接收filters

对此设置的支持,取决于客户机的网络设备型号,以及宿主机的连接类型。当前只有virtio型号和macvtap宿主机连接类型支持

虚拟网络

对于基于动态地址分配/无线网络的宿主机获得连接性的虚拟机,推荐此方式。

虚拟网络提供一个其详细信息由一个命名网络定义(named network definition)所描述的连接。依据虚拟网络的转发模式(forward mode)设置,它可能是:

  1. 完全隔离的,不配置 <forward> 元素
  2. NAT到一个指定的网络设备或者默认路由,配置 <forward mode='nat'> 
  3. 不基于NAT来路由,配置 <forward mode='route'/> 
  4. 直接连接到宿主机的网络接口(通过macvtap)或桥接设备,配置 <forward mode='bridge|private|vepa|passthrough'/> 

对于转发模式设置为bridge | private | vepa | passthrough的网络,宿主机在libvirt管理范围之外应拥有必要的DNS、DHCP服务。对于转发模式设置为isolated | nat |routed的网络,libvirt提供的虚拟网络中包含了DHCP和DNS。虚拟网络自动分配的IP地址范围可以通过命令

Shell
1
virsh net-dumpxml [networkname]

得到。一个开箱即用的、称为default的虚拟网络NAT到宿主机默认路由,其IP地址范围是192.168.122.0/24,在宿主机中你可以ifconfig看到一个名为virbr0的网络接口,与这个default虚拟网络有关。要自定义虚拟网络,需要修改其它类型(network XML)的配置文件L。

每个客户机会有一个命名为 vnetN 的tun设备,你可以利用target元素覆盖此命名。

类似于direct类型的接口,network类型的接口可以指定一个virtualport子元素,用于将配置信息转发给vepa(802.1Qbg)或 802.1Qbh兼容的交换机,或Open vSwich虚拟交换机。

配置示例:

XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<devices>
    <interface type='network'>
        <!-- 如果接口的source是一个网络,则可以设置soure元素的portgroup属性,一个网络可能定义了多个portgroup,
             每个portgroup包含些许不同的配置信息,用于不同类别的网络连接 -->
        <source network='default'/>
    </interface>
    <interface type='network'>
        <!-- -->
        <source network='default' portgroup='engineering'/>
        <target dev='vnet7'/>  <!-- 设置虚拟网卡的名称 -->
        <mac address="00:11:22:33:44:55"/><!-- 设置虚拟网卡的MAC地址 -->
        <virtualport>
            <parameters instanceid='09b11c53-8b5c-4eeb-8f00-d84eaa0aaa4f'/>
        </virtualport>
    </interface>
</devices>
桥接到LAN

对于基于静态地址的有线网络的宿主机获得连接性的虚拟机,推荐此方式。

该方式将虚拟机直接桥接到宿主机所在的局域网,libvirt假设宿主机上的网桥设备enslaved了1-N个物理网卡。客户机的IP地址范围与宿主机局域网的IP地址范围一样。

在Linux系统中,网桥通常是标准的Linux主机网桥(host bridge)。如果主机支持Open vSwitch,则可以添加 <virtualport type='openvswitch'/> 子元素以连接到Open vSwitch网桥。

每个客户机会有一个命名为 vnetN 的tun设备,你可以利用target元素覆盖此命名。此tun设备会自动enslaved到宿主机网桥。

配置示例:

XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<interface type='bridge'>
    <!-- 桥接到宿主机的br0网桥 -->
    <source bridge='br0'/>
</interface>
<interface type='bridge'>
    <source bridge='br1'/>
    <!-- 设置客户机中虚拟网卡的名称和MAC -->
    <target dev='vnet7'/>
    <mac address="00:11:22:33:44:55"/>
</interface>
<interface type='bridge'>
    <source bridge='ovsbr'/>
    <!-- 连接到Open vSwitch -->
    <virtualport type='openvswitch'>
        <!-- interfaceid是此虚拟网卡的唯一标识,如果不指定自动生成;profileid作为虚拟网卡的port-profile发送给vSwitch -->
        <parameters profileid='menial' interfaceid='09b11c53-8b5c-4eeb-8f00-d84eaa0aaa4f'/>
    </virtualport>
</interface>
用户空间SLIRP栈

提供一个虚拟局域网并NAT到外面的世界,此虚拟网络使用10.0.2.x网段。默认路由10.0.2.2,DNS服务器10.0.2.3,客户机地址从10.0.2.15开始。此网络仅仅用于没有特权的宿主机用户。配置示例:

XML
1
2
3
4
<interface type='user'/>
<interface type='user'>
    <mac address="00:11:22:33:44:55"/>
</interface>
设置虚拟网卡型号

如果hypervisor支持,则可以设置虚拟网卡的型号。示例:

XML
1
2
3
<interface type='network'>
    <model type='ne2k_pci'/>
</interface>

QEMU支持的型号包括 ne2k_isa i82551 i82557b i82559er ne2k_pci pcnet rtl8139 e1000 virtio。

修改虚拟网卡状态

可以设置网卡是启用还是断开的:

XML
1
2
3
<interface type='network'>
    <link state='down'/>
</interface>
IP配置

网络设备、具有网络特性的hostdev设备可以配置一个或者多个IP地址,某些hypervisor会忽略这些配置。配置示例:

XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<devices>
    <interface type='network'>
        <source network='default'/>
        <target dev='vnet0'/>
        <ip address='192.168.122.5' prefix='24'/>
        <!-- peer指定点对点连接中对端的IP地址 -->
        <ip address='192.168.122.5' prefix='24' peer='10.0.0.10'/>
        <!-- route仅用于LXC -->
        <route family='ipv4' address='192.168.122.0' prefix='24' gateway='192.168.122.1'/>
        <route family='ipv4' address='192.168.122.8' gateway='192.168.122.1'/>
    </interface>
    <hostdev mode='capabilities' type='net'>
        <source>
            <interface>eth0</interface>
        </source>
        <ip address='192.168.122.6' prefix='24'/>
        <route family='ipv4' address='192.168.122.0' prefix='24' gateway='192.168.122.1'/>
        <route family='ipv4' address='192.168.122.8' gateway='192.168.122.1'/>
    </hostdev>
</devices> 
图形配置

配置图形(Graphical)设备,可以让你与客户机进行图形化的交互。客户机通常提供一个framebuffer或者text console,作为人机接口。配置示例:

XML
1
2
3
4
5
6
7
8
9
10
11
12
<!-- 基于SDL的图形接口,弹出QEMU的图形化控制台窗口 -->
<graphics type='sdl' display=':0' xauth='/home/alex/.Xauthority'/>
<!-- 基于VNC的图形接口 -->
<graphics type='vnc' port='-1' autoport='yes' listen='0.0.0.0' sharePolicy='allow-exclusive'>
    <listen type='address' address='0.0.0.0'/>
</graphics>
 
<graphics type='rdp' autoport='yes' multiUser='yes'/>
<graphics type='desktop' fullscreen='yes'/>
<graphics type='spice'>
    <listen type='network' network='rednet'/>
</graphics>

根据强制属性type的取值,grpahics的属性、子元素有所差异:

type 说明
sdl 在宿主机的桌面上显示一个窗口,额外属性:
display    使用哪个显示器
xauth    验证标识符
fullscreen    是否全屏,yes/no
vnc 启动一个VNC服务器,额外属性:
port    监听端口,-1表示自动分配
autoport    表示自动分配端口
passwd    VNC密码明文
keymap    使用的keymap
passwdValidTo    密码有效期限(UTC),示例'2010-04-09T15:51:00'
sharePolicy    显示共享策略,allow-exclusive独占并丢弃其它连接,force-shared禁止独占,ignore无条件允许任何连接
socket    对于QEMU,可以指定一个UNIX domain socket而非TCP/IP
spice

启动一个SPICE服务器,额外属性:
port   监听端口,-1表示自动分配
tlsPort    安全协议端口
autoport   表示自动分配端口
passwd   SPICE密码明文
keymap    使用的keymap
passwdValidTo    密码有效期限(UTC),示例'2010-04-09T15:51:00'
connected     如果密码改变,如何控制已连接的客户端。keep保持连接,disconnect断开连接,fail禁止修改密码
defaultMode    设置默认的通道安全策略。有效值secure、insecure、any

如果SPICE同时配置了普通端口、TLS安全端口。则可以利用channel子元素限制某个通道使用的端口。可用的通道名包括main, display, inputs, cursor, playback, record,smartcard,usbredir。配置示例:

XML
1
2
<channel name='main' mode='secure'/>
<channel name='record' mode='insecure'/>

SPICE支持音频、图片、流的压缩。你可以设置以下子元素的compression属性:
image    图片压缩,支持compression取值auto_glz, auto_lz, quic, glz, lz, off
jpeg    基于WAN访问时的JPEG压缩,支持compression取值 auto, never, always
zlib,基于WAN访问时的图片压缩,支持compression取值auto, never, always
playback,音频流压缩,支持compression取值onn,off

配置示例:

XML
1
<image compression='auto_glz'/> 

可以使用streaming子元素设置流模式,其mode属性可以取值filter, all,off,配置示例:

XML
1
<streaming mode='filter'/>

基于Spice agent的复制/粘贴功能可以利用clipboard子元素设置,默认启用,配置示例:

XML
1
<clipboard copypaste='no'/> 

鼠标模式可以利用mouse子元素设置,mode取值client,server,配置示例:

XML
1
<mouse mode='client'/>

文件传输功能可以利用filetransfer设置,默认启用,配置示例:

XML
1
<filetransfer enable='no'/>

SPICE支持服务器端的基于OpenGL的加速渲染(仅QEMU),配置示例: <gl enable='yes'/> 

rdp 启用一个RDP服务器,额外属性:
port 监听端口,-1表示自动分配
autoport 表示自动分配端口
replaceUser    布尔值,是否允许多个用户同时连接
desktop 保留给VirtualBox Domain,配置类似于sdl
listen子元素

用于指明在何处监听客户机连接。

视频设备配置

video是描述视频设备的容器,为了向后兼容,如果配置了graphics却没有配置video,libvirt会根据客户机的类型自动添加一个video。配置示例:

XML
1
2
3
4
5
6
7
8
9
10
<!-- 默认第一个配置video是主视频设备,可以用primary属性覆盖 -->
<video primary='yes'>
    <model type='vga' vram='16384' heads='1'>
        <acceleration accel3d='yes' accel2d='yes'/>
    </model>
</video>
<video>
   <!-- 下面的配置是KVM客户机的默认值 -->
   <model type='cirrus' vram='16384' heads='1' />
</video>

子元素说明如下:

元素 说明
model type     视频设备类型,可选值:vga、cirrus、vmvga、xen、vbox、qxl、virtio,基于hypervisor来选择
vram    现存容量
heads   设置屏幕的数量
acceleration accel2d    启用2D加速,仅vbox
accel3d    启用3D加速,仅vbox、QEMU
 使用libguestfs

libguestfs是一组工具集,用来(在不启动客户机的情况下)访问、修改虚拟机的磁盘文件,通过libguestfs你可以好对磁盘进行以下操作:

  1. 查看或者修改文件
  2. 创建虚拟磁盘
  3. 改变虚拟磁盘大小
  4. 执行磁盘备份、克隆等操作

libguestfs支持多种虚拟磁盘格式,包括Vmware、Hyper-V。日常工作中我们主要使用libguestfs提供的命令行guestfish。libguestfs不依赖于libvirt。

与libguestfs类型工具包括:

  1. kpartx 需要root权限,并且将文件系统挂载到宿主机的内核中。相比之下libguestfs把文件系统隔离在appliance中,安全性高
  2. vdfuse 该工具类似于kpartx,但是仅仅针对VirtualBox虚拟磁盘
  3. qemu-nbd  用QEMU提供的工具,基于QEMU支持的磁盘格式(raw、qcow2)构建网络块服务器。libguestfs可以与之配合使用:
    Shell
    1
    guestfish -a nbd://remote
安装与配置

执行下面的命令安装libguestfs:

Shell
1
sudo apt-get install libguestfs-tools

在Ubuntu下,需要执行:

Shell
1
sudo chmod 0644 /boot/vmlinuz* 

否则在使用过程中你会遇到cp: cannot open '/boot/vmlinuz-4.4.0-38-generic' for reading: Permission denied错误。

使用guestfish

执行下面的命令,以编辑一个虚拟磁盘:

Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# 附加-v参数,可以看到很多调试信息,例如appliance的启动过程日志
guestfish -a vda.qcow2
 
# 出现提示符
><fs>
 
# 添加一个磁盘,只能在run之前执行
# add-drive filename [readonly:true|false] [format:..] [iface:..] [name:..] [label:..] [protocol:..] [server:..]
add-drive vdb,qcow2 format:qcow2
 
# 执行run命令,一个appliance(类似于微型虚拟机)会被启动
><fs> run
 
# 列出设备
><fs> list-devices
# 输出:/dev/sda
 
# 列出分区
><fs> list-partitions
# 输出:
# /dev/sda1
# /dev/sda2
 
# 显示各分区详细信息
><fs> part-list /dev/sda
# [0] = {
#   part_num: 1
#   part_start: 1048576
#   part_end: 525336575
#   part_size: 524288000
# }
# [1] = {
#   part_num: 2
#   part_start: 525336576
#   part_end: 17179869183
#   part_size: 16654532608
# }
# 测试第一个分区是否可启动
><fs> part-get-bootable /dev/sda 1
# 其它分区相关命令: part-add,part-del,part-disk,part-get-bootable,part-get-gpt-type,part-get-mbr-id,
#                 part-get-name,part-get-parttype,part-init,part-list,part-set-bootable,part-set-gpt-type,
#                 part-set-mbr-id,part-set-name,part-to-dev,part-to-partnum
 
# 操作LVM
# 显示物理卷详细信息
pvs-full
# 显示逻辑卷组详细信息
vgs-full
# 其它LVM相关命令:
# lvcreate, lvcreate-free, lvm-canonical-lv-name, lvm-clear-filter, lvm-remove-all, lvm-set-filter,
# lvremove, lvrename, lvresize, lvresize-free, lvs, lvs-full, lvuuid, pvcreate, pvremove, pvresize,
# pvresize-size, pvs, pvs-full, pvuuid, vg-activate, vg-activate-all, vgchange-uuid, vgchange-uuid-all,
# vgcreate, vglvuuids, vgmeta, vgpvuuids, vgremove, vgrename, vgs, vgs-full, vgscan, vguuid
# 列出文件系统
><fs> list-filesystems
# 输出:
# /dev/sda1: ext4
# /dev/fedora_bogon/root: xfs
# /dev/fedora_bogon/swap: swap
 
# 挂载文件系统
><fs> mount /dev/fedora_bogon/root /
 
# 列出目录
><fs> ls /
# 创建新目录
><fs> mkdir /temp
# 其它支持的文件系统命令包括:cp chown chmod cp
 
# 下载文件到当前目录
download /root/.bash_history test
# 上传文件到虚拟磁盘
upload test /temp/test
 
# 查看文件内容
cat /temp/test
 
# 退出
><fs> quit

执行run子命令后,需要等待一会,这时libguestfs在启动一个 appliance。再此appliance中会运行一个Linux内核,LVM、ext2等用户空间工具,以及一个守护程序guestfsd。宿主机进程基于RPC与这个守护进程通信,完成对磁盘镜像的操作。

使用Guestmount

该命令可以把虚拟磁盘挂载到宿主机的目录上:

Shell
1
2
3
4
5
6
7
# 创建挂载点
mkdir vda
# 挂载一个文件系统
guestmount -a vda.qcow2 --rw -m /dev/fedora_bogon/root vda
# 现在你可以在宿主机中修改虚拟磁盘中的文件
# 操作完毕后,取消挂载
guestunmount vda
使用virt-builder

该命令可以用来快速的创建虚拟机磁盘镜像:

Shell
1
2
3
4
5
6
7
8
9
# 列出所有支持的客户机类型
virt-builder --list
# 创建一个Fedora 24的虚拟磁盘镜像,第一次使用某个客户机类型,需要从网络上下载镜像文件
virt-builder fedora-24
    -o fedora-24.qcow2 --format qcow2 --size 20G
    --hostname fedora-24-01   # 设置主机名
    --root-password file:/tmp/pswd  # 设置root密码,密码从文件中读取
    --install "apache2"  # 安装软件
    --firstboot  /tmp/fb.sh  # 第一次启动时执行的脚本
其它命令
命令 说明
virt-ls 列出虚拟磁盘中的文件,示例: virt-ls -a vda.qcow2 / 
virt-cat 查看虚拟磁盘中某个文件的内容,示例: virt-cat -a vda.qcow2 /root/.bashrc 
virt-copy-in 拷贝目录或者文件到虚拟磁盘中,示例: virt-copy-in -a vda.qcow2 hello /root/
virt-copy-out  从虚拟磁盘中拷贝文件到本地,示例: virt-copy-out -a vda.qcow2 /root/hello . 
virt-df 显示虚拟磁盘文件系统的剩余空间情况
virt-diff 显示两个Domain或者虚拟磁盘中同一文件的差异
virt-edit 编辑一个文件
virt-format 执行格式化操作
virt-inspector 显示虚拟磁盘中操作系统的版本、以及其它信息
virt-make-fs 从一系列文件,或者tar来创建一个文件系统
virt-resize 改变虚拟磁盘的尺寸
virt-sparsify 稀疏化,虚拟磁盘中的空闲空间将归还给宿主机
virt-tar-in 打包并上传
virt-tar-out 打包并下载
virt-win-reg 导入导出Windows的注册表键值
管理虚拟局域网

通过配置libvirt的虚拟局域网,可以简化Domain的网络接口配置,比QEMU的脚本方式好很多。此外虚拟局域网还提供DHCP服务。

虚拟网络交换机

libvirt引入了virtual network switch的概念,这是运行在宿主机上的软件。客户机可以“插入”到这个交换机上并传递流量。在Linux宿主机上,这个交换机表现为网络接口——默认情况下名字为virbr0,这个接口实质上是一个网桥。

NAT

默认情况下虚拟网络交换机工作在NAT模式下,实质上是基于宿主机的iptables设置IP遮掩(不使用SNAT/DNAT),客户机对外通信时,使用宿主机的IP地址。

路由模式

与NAT不同,这种模式直接转发客户机的IP封包,不进行NAT转换。这需要物理网络的路由器配置适当的路由,让客户机子网的流量流向所在的宿主机。

隔离模式

这种模式下,虚拟网络交换机不把客户机的IP封包转发到真实网络上去。只有宿主机、各客户机之间可以进行通信。

DNS &DHCP

每个虚拟网络交换机可以设置一个用于动态分配的IP地址范围,供连接到此交换机的客户机使用,客户机可以通过DHCP服务自动获得IP地址。

libvirt基于dnsmasq实现DHCP和DNS,对于每个需要DHCP的虚拟网络交换机,libvirt在宿主机上启动一个dnsmasq实例。

除了简单的DNS请求转发,dnsmasq可以做更多的事情:

  1. 它可以读取宿主机的 /etc/hosts 中条目,来应答DNS查询请求
网络架构示意

一个可能的虚拟网络架构的逻辑图如下:

libvirt-net-logical

对应的物理拓扑如下:

libvirt-net-physical

其中:

  1. VLAN 1,这个虚拟局域网通过网桥virbr0与eth1进行桥接,并基于NAT连接到真实局域网lan2
  2. VLAN 2,这个虚拟局域网桥接到virbr0,但是与真实局域网完全隔离
  3. Guest A,该虚拟机的:
    1. eth0桥接到宿主机的网桥eth0,后者直接连接到真实网卡pth0,从而与lan1互联
    2. eth1桥接到virbr0,可以基于NAT受限访问lan2
  4. Guest B,基于NAT、隔离网络
  5. Guest C,基于隔离网络
相关virsh子命令
Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 列出所有活动的虚拟局域网,libvrit默认创建一个桥接到virbr0的vlan,名为default
virsh net-list
#  Name                 State      Autostart     Persistent
# ----------------------------------------------------------
#  default              active     yes           yes
 
# 列出所有虚拟局域网
virsh net-list --all
 
# 显示一个虚拟局域网的详细信息
net-info default
# Name:           default
# UUID:           e9e308a3-dea9-46dc-bc92-fad27f2a970a
# Active:         yes
# Persistent:     yes
# Autostart:      yes
# Bridge:         virbr0
 
# 导出一个虚拟局域网的XML
virsh net-dumpxml default
 
# 定义一个新的虚拟局域网
virsh net-define /home/alex/Vmware/KVM/vlans/default.xml
# 解除虚拟局域网定义
virsh net-undefine default
 
# 关闭一个虚拟局域网
virsh net-destroy default
 
# 将一个虚拟局域网标记为自动启动
net-autostart default
 
# 启动虚拟局域网,启动后,宿主机上可能出现一个网桥设备
net-start default
虚拟局域网配置

类似于Domain,虚拟局域网的配置也是存放在XML文件中的。每个虚拟局域网对应一个XML配置文件,根元素为network。包括属性:

属性 说明
ipv6 yes/no,设置为yes时定义一个虚拟网络,该网络不指定网关的IPv6地址,但是允许客户机-客户机之间的通讯
trustGuestRxFilters yese/no,设置连接到此VLAN的Domain的interface元素的同名属性

包含以下基本子元素:

子元素 说明
name 此VLAN的短名,此名字被用作构成持久化配置文件的名字
uuid 全局唯一的VLAN标识符,例如3e3fce45-4f53-4fa7-bb32-11f34168b82a
连通性配置

以下子元素用于配置VLAN的连通性:

子元素 说明
bridge 说明如何创建让VLAN连接到物理网络的、宿主机上的网桥设备(虚拟网络交换机):
name  
网桥设备的名称,这样所有使用此VLAN的客户机可以相互通信,网桥本身可以连接到真实局域网(LAN),取决于宿主机的配置
当使用mode=nat|route指定forward子元素,或者不指定forward子元素(隔离的VLAN)时,如果你不指定name属性,则libvirt会自动创建为网桥生成唯一名称并记住。libvirt推荐以virbr前缀指定name
stp
是否启用 Spanning Tree Protocol,默认on
delay
网桥转发的延迟秒数,默认0
macTableManager
告知libvirt,如何管理MAC地址表(用于判断数据包的出口),默认值kernel,可设置为libvirt。设置为libvirt可以提高性能,但是导致vlan tagging, multicast等功能失效。需要内核版本3.17+
domain DHCP相关配置:
name
定义DHCP服务器的DNS Domain(域名)
localOnly
如果设置为yes,则name对应子域名的解析均由VLAN自己的DNS服务器负责,与宿主机的DNS无关;如果设置为no,则无法解析的DNS请求转发给宿主机DNS处理
forward

通过配置此元素,可以让VLAN连接到物理网络。如果不指定此子元素,则VLAN与其它网络隔离(isolated mode)
mode
定义包转发方式:

nat  所有连接到此VLAN的客户机、物理网络之间的流量,均forward到宿主机的IP路由栈。从宿主机外部看来,所有客户机均使用宿主机的IP地址。这种mode适合多个客户机需要访问物理网络,而宿主机仅仅允许用于一个公共IP地址的应用场景。如果网络分配的IPv6地址,那么IPv6流量通过plain路由转发,因为IPv6没有NAT的概念。同一VLAN上地址相互通信,不进行NAT

route  来自客户机的流量forward到宿主机的IP路由栈,但是不进行NAT。要使用这种方式,LAN路由器必须包含适当的路由表项,将流量返回给宿主机(进而转发给对应客户机)。使用此方式,则客户机的入站/出站会话不受限制

open  类似于route,但是libvirt不会在宿主机上应用任何Firewall规则,也不支持设置dev属性

bridge 桥接到既有网桥,该模式下libvirt不去创建网桥设备:

当配置了 <bridge name='br'/> 时:桥接到一个宿主机既有(非libvirt管理)的网桥
当配置了 <virtualport type='openvswitch'/>  时:桥接到一个既有的Open vSwitch网桥
当配置了 <interface dev='eth0' /> 时:通过macvtap的桥接模式,直连到物理网络

dev
如果设置此属性,当mode=nat|route时,Firewall规则将限定仅仅forward到dev设备上,否则应用到所有设备。当设置为wlan0时生成的iptables规则:

Shell
1
2
3
4
5
6
7
# sudo iptables -t nat -L -nv
 
Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target     prot opt in  out     source         destination        
    0     0 MASQUERADE  tcp  --  *   wlan0   10.0.0.0/16   !10.0.0.0/16   masq ports: 1024-65535
    0     0 MASQUERADE  udp  --  *   wlan0   10.0.0.0/16   !10.0.0.0/16   masq ports: 1024-65535
    0     0 MASQUERADE  all  --  *   wlan0   10.0.0.0/16   !10.0.0.0/16  

如果不设置dev,默认值为*,这就意味着:从虚拟机中访问任何非10.0.0.0/16网段时,不管路由出口是哪个网卡,封包到达宿主机后,会强制进行SNAT —— 哪怕目标地址在另外一台虚拟机上。这会让Flannel的host-gw模式失效,因为,Flannel的容器网络CIDR肯定不是10.0.0.0/16,这意味着跨节点(虚拟机)的CNI通讯会发生NAT,这显然是不期望发生的。 

示例:

XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!-- 桥接到物理网络,基于macvtap -->
<network connections='1'>
  <name>default</name>
  <uuid>519cbf63-8ec0-4893-ba9c-0747430bdecd</uuid>
  <forward dev='eth0' mode='bridge'>
    <interface dev='eth0' connections='1'/>
  </forward>
</network>
 
<!-- NAT转发,创建VLAN -->
<network connections='1'>
  <name>default</name>
  <uuid>9bae4de8-ca58-48c5-ba58-109aebf8b954</uuid>
  <forward mode='nat' dev="wlan0">
    <nat>
      <port start='1024' end='65535'/>
    </nat>
  </forward>
  <bridge name='virbr0' stp='off' delay='0'/>
  <mac address='de:ad:be:ef:00:00'/>
  <ip address='10.0.0.1' netmask='255.0.0.0'>
    <dhcp>
      <range start='10.0.0.100' end='10.0.0.200'/>
      <host mac='DE:AD:BE:EF:00:02' ip='10.0.0.2'/>
    </dhcp>
  </ip>
</network>
bandwidth

配置虚拟网络的QoS,仅支持fowward mode=route|nat或者隔离网络。配置示例:

XML
1
2
3
4
<bandwidth>
    <inbound average='1000' peak='5000' burst='5120'/>
    <outbound average='128' peak='256' burst='256'/>
</bandwidth>
ip

设置虚拟局域网的子网、网桥(DHCP服务器)的IP地址、DHCP和DNS配置

ip子元素

该元素设置子网、网桥地址:

XML
1
2
<!-- 子网10.0.0.0/8,网桥地址10.0.0.1 -->
<ip address='10.0.0.1' netmask='255.0.0.0'>

ip/dhcp子元素

设置DHCP自动分配的地址范围、静态映射MAC地址到IP: 

XML
1
2
3
4
5
6
<dhcp>
    <!-- 自动分配的地址范围 -->
    <range start='10.0.0.100' end='10.0.0.200'/>
    <!-- 对于给定的MAC地址,总是绑定到某个IP、机器网络名 -->
    <host mac="DE:AD:BE:EF:F1:10" name="fedora-10" ip="10.0.0.10" />
</dhcp>
dns

配置虚拟网络的DNS服务器,示例:

XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- 如果enable为no,则libvirt不会启动DNS服务 -->
<dns enable="yes">
    <!--
        每个forwarder元素定义一个备选DNS服务器:
        如果指定domain属性,则仅针对该domain下的子域名的查询请求转发给addr对应的DNS服务器
    -->
    <forwarder addr="8.8.8.8"/>
    <forwarder domain='example.com' addr="8.8.4.4"/>
    <forwarder domain='www.example.com'/>
    <!-- 静态DNS映射 -->
    <host ip='10.0.0.10'>
        <hostname>fedora-10</hostname>
        <hostname>fedora-10.local</hostname>
    </host>
</dns>
mac

 设置网桥的MAC地址,示例:

XML
1
<mac address='DE:AD:BE:EF:F1:00'/>

当你创建一个新的虚拟网络后,libvrit会在 /etc/libvirt/qemu/networks 目录中生成一个XML文件,该文件基于virsh net-define指定的那个配置文件。

配置示例

基于NAT转发,启用DHCP,静态映射MAC到IP地址的例子:

XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<network>
  <name>default</name>
  <forward mode='nat'>
  </forward>
  <bridge name='virbr0' stp='off' delay='0'/>
  <mac address='DE:AD:BE:EF:F1:00'/>
  <ip address='10.0.0.1' netmask='255.0.0.0'>
    <dhcp>
      <range start='10.0.0.100' end='10.0.0.200'/>
      <host mac="DE:AD:BE:EF:F1:10" ip="10.0.0.10" />
      <host mac="DE:AD:BE:EF:F1:11" ip="10.0.0.11" />
    </dhcp>
  </ip>
</network>

使用宿主机既有网桥的例子: 

XML
1
2
3
4
5
6
<network>
    <name>default</name>
    <forward mode="bridge"/>
    <!-- br1是宿主机上既有的网桥,它可能直接连接到物理网络 -->
    <bridge name="br1"/>
</network>

macvtap直连,需要 2.6.34+内核、仅支持QEMU/KVM。通过macvtap可以通过一组物理网络接口(不需要网桥)之一,直接连接到物理网络。客户机将直接具有物理网络的子网IP地址,并且与物理网络中其它机器互联互通。示例:

XML
1
2
3
4
5
6
7
<network>
    <name>direct-macvtap</name>
    <forward mode="bridge">
        <interface dev="eth0"/>
        <interface dev="eth1"/>
    </forward>
</network>
存储池管理

通过存储池,libvirt集中管理物理机器上的存储资源,供客户机使用。存储池被划分为卷(volume),卷则可以作为块设备分配给客户机使用。

池类型

libvirt支持以下类型的存储池后端:

后端类型 说明
目录 将宿主机的一个目录作为池看待,该目录中的文件可以包含各种客户机磁盘文件、镜像文件
本地文件系统 将宿主机上一个格式化好的文件系统作为池看待,文件系统类型可以是ext2,ext3,vfat 
网络文件系统 使用远端网络文件系统服务器的导出目录作为存储池。默认为 NFS 网络文件系统
逻辑卷 使用已经创建好的 LVM 卷组,或者提供一系列生成卷组的源设备,libvirt 会在其上创建卷组,生成存储池
磁盘 使用磁盘作为存储池
iSCSI 使用 iSCSI 设备作为存储池
其它 SCSI、Multipath、RBD、Sheepdog、Gluster 、ZFS
NFS池

通常存储管理员负责维护存储设备,例如NFS服务器。而宿主机的管理员定义存储池,存储池包含了NFS shares到挂载目录的映射。当存储池启动时,libvirt会自动执行挂载操作。一旦存储池启动完毕,NFS share中的文件被当作卷看待,你可以在客户机的Domain配置文件中引用卷的路径,作为块设备的source。

使用NFS时,基于libvirt的API可以在池中创建/删除卷(即NFS上的文件),但是不能超过池的尺寸限制(NFS share有容量控制)。注意不是所有类型的池支持卷的创建/删除操作。 

池XML配置

你可以通过XML文件来定义存储池,使用virsh命令可以定义、启动、停止、解除定义池,就像操作Domain、虚拟网络一样。你的配置文件被libvrit修改后,存放在 /etc/libvirt/storage/ 目录中。配置示例:

XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<!-- 基于本地目录的存储池 -->
<pool type="dir">
    <name>virtimages</name>
    <target>
        <path>/var/lib/virt/images</path>
    </target>
</pool>
 
<!-- 基于iscsi的存储池 -->
<pool type="iscsi">
    <name>virtimages</name>
    <source>
        <host name="iscsi.example.com"/>
        <device path="iqn.2013-06.com.example:iscsi-pool"/>
        <auth type='chap' username='myuser'>
            <secret usage='libvirtiscsi'/>
        </auth>
    </source>
    <target>
        <path>/dev/disk/by-path</path>
    </target>
</pool>
 
<!-- 基于本地文件系统的池 -->
<pool type="fs">
    <name>sda</name>
    <source>
        <!-- 使用的文件系统 -->
        <device path="/dev/sda3"/>
      </source>
    <target>
        <!-- 指定挂载点 -->
        <path>/home/alex/Vmware/libvirt/images/sda</path>
        <permissions>
            <mode>0777</mode>
        </permissions>
    </target>
</pool>
基本配置

一个存储池配置的根元素为pool,最基础的配置项如下:

XML
1
2
3
4
5
6
7
<pool type="iscsi">
    <name>virtimages</name>
    <uuid>3e3fce45-4f53-4fa7-bb32-11f34168b82b</uuid>
    <allocation>10000000</allocation>
    <capacity>50000000</capacity>
    <available>40000000</available>
</pool>

属性和子元素说明如下:

属性/子元素 说明
@type 存储池类型,支持取值dir, fs, netfs, disk, iscsi, logical, scsi,mpath,rbd,sheepdog,gluster,zfs
name 对于宿主机来说,唯一的名称
uuid 全局唯一的UUID,如果忽略libvirt会自动生成
allocation 当前分配给池的容量,单位字节。创建池时不能使用此元素
capacity 池的总容量,单位字节。创建池时不能使用此元素
available 设置可以分配给新的卷使用的空闲容量。创建池时不能使用此元素
source子元素

存储池具有最多一个(某些类型的池后端没有)source子元素,用于描述池的来源。source包含的内容取决于池的type。 配置示例:

XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- disk池配置 -->
<source>
    <device path='/dev/mapper/mpatha' part_separator='no'/>
    <format type='gpt'/>
</source>
 
<!-- SCSI池配置 -->
<source>
    <adapter type='scsi_host' name='scsi_host1'/>
</source>
<source>
    <adapter type='scsi_host'>
        <parentaddr unique_id='1'>
            <address domain='0x0000' bus='0x00' slot='0x1f' addr='0x2'/>
        </parentaddr>
    </adapter>
</source>

各子元素说明如下:

source的子元素 说明
device 对于fs, logical, disk, iscsi, zfs类型的池后端,指定存储池的后端物理设备。对于某些池后端,该元素可能出现多次。支持以下属性:
path,指向物理设备的全限定路径,或者iSCSI全限定名称(IQN)
part_separator,yes/no,用于disk后端
dir 对于 dir, netfs, gluster类型的池后端,指定存储池对应的文件目录。仅能出现一次。支持以下属性:
path,目录的全限定名称。对于Samba share,路径不包含起始的斜杠
adapter 对于scsi类型的池后端,指定存储池使用的SCSI适配器
host 对于 netfs, iscsi, rbd, sheepdog, gluster类型的池后端,如果存储池位于远程机器上,指定远程机器的信息,必须和device或者dir元素联用。支持以下属性:
name  远程机器的IP或者主机名
port  监听端口,可选,默认值依据远程协议确定
auth 提供访问池后端所需的身份验证信息。支持以下属性:
type  可以是chap或者ceph,分别用于iscsi、rbd
username  用户名
secret  密码信息(libvirt secret object)
name 对于logical, rbd, sheepdog, gluster类型的池后端,依据已命名元素提供池的源
format

指定源的格式

对于fs池后端,指定的是文件系统的类型:auto  ext2  ext3  ext4  ufs  iso9660  udf  gfs  gfs2  vfat  hfs+  xfs  ocfs2

对于netfs池后端,指定的是网络协议的类型:auto  nfs  glusterfs  cifs

对于logical池后端,只能是lvm2

对于disk池后端,指定的是分区表类型:dos  dvh  gpt  mac  bsd  pc98  sun  lvm2

vendor 指定存储设备的供应商信息
product 指定存储设备的产品信息
target子元素

对于dir, fs, netfs, logical, disk, iscsi, scsi, mpath类型池后端,可以具有单个target子元素。该元素描述如何映射存储池的source到宿主机的文件系统命名空间。配置示例:

XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<target>
    <path>/dev/disk/by-path</path>
    <permissions>
        <owner>107</owner>
        <group>107</group>
        <mode>0744</mode>
        <label>virt_image_t</label>
    </permissions>
    <timestamps>
        <atime>1341933637.273190990</atime>
        <mtime>1341930622.047245868</mtime>
        <ctime>1341930622.047245868</ctime>
    </timestamps>
    <encryption type='...'></encryption>
</target>

各子元素说明如下:

target的子元素 说明
path 存储池映射到宿主机目录树的什么位置。对于:
  1. fs,dir池后端,该路径是在其中创建卷的绝对路径
  2. 基于设备的后端,该路径是设备节点所在路径,最好使用稳定(重启后不变化)的路径:/dev/disk/by-{path|id|uuid|label},避免重启后路径变化
  3. mpath后端,设置该子元素无效,总是使用默认值/dev/mapper
permissions 仅用于fs,dir池后端,指定池对应目录的文件模式
timestamps 提供卷的时间戳信息
encryption 指定卷的加密方式
卷XML配置

存储卷,通常对应一个文件或者设备节点。注意:当创建了存储池后,池中的镜像文件会被libvirt自动识别,并创建相应的卷定义。

配置示例:

XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!-- 基于文件的卷 -->
<volume type='file'>
    <name>fedora-10.qcow2</name>
    <capacity unit='GiB'>16</capacity>
    <target>
        <path>/home/alex/Vmware/libvirt/images/fedora-10.qcow2</path>
        <format type='qcow2'/>
        <permissions>
            <mode>0666</mode>
        </permissions>
    </target>
    <backingStore>
        <path>/home/alex/Vmware/libvirt/images/fedora-base.qcow2</path>
        <format type='qcow2'/>
        <permissions>
            <mode>0666</mode>
        </permissions>
    </backingStore>
</volume>
<!-- 使用LUKS加密的卷-->
<volume>
    <name>MyLuks.img</name>
    <capacity unit="G">5</capacity>
    <target>
        <path>/var/lib/virt/images/MyLuks.img</path>
        <format type='raw'/>
        <encryption format='luks'>
            <secret type='passphrase' uuid='f52a81b2-424e-490c-823d-6bd4235bc572'/>
        </encryption>
    </target>
</volume>
基本配置

一个卷配置的根元素是volume,最基础的配置项如下:

XML
1
2
3
4
5
6
<volume type='file'>
    <name>sparse.img</name>
    <key>/var/lib/xen/images/sparse.img</key>
    <allocation>0</allocation>
    <capacity unit="T">1</capacity>
</volume>

 属性和子元素说明如下:

属性/子元素 说明
@type 该属性仅能读取,显示卷的实际类型
name 存储池范围内唯一的卷名称
key 用于唯一性的识别卷,该子元素不能在创建卷的时候指定,它总是自动生成的
allocation 指定分配给卷的存储空间,如果卷是稀疏分配的,该值可以小与卷的逻辑大小。可用属性:
unit  计量单位
capacity 指定卷的逻辑大小。可用属性:
unit 计量单位
target子元素

volume包含一个target子元素,指定卷如何映射到宿主机的文件系统中:

target的子元素 说明
path 卷在本地文件系统的绝对路径。该元素为只读,在创建时不能指定,libvirt会根据卷的name元素自动生成此元素
format

指定卷的格式:

对于dir,fs,nfs池后端,支持的卷格式包括:
raw  原始文件
bochs  Bochs磁盘镜像格式
cloop   压缩的loopback磁盘镜像格式
cow   用户模式Linux磁盘镜像格式
dmg   MAC的磁盘镜像格式
iso    光盘镜像格式
qcow  QEMU v1磁盘镜像格式
qcow2  QEMU v2磁盘镜像格式
qed  QEMU增强磁盘镜像格式
vmdk  Vmware磁盘镜像格式
vpc    VirtualPC磁盘镜像格式

对于disk池后端,支持的卷格式包括:
none  linux  fat16  fat32 linux-swap  linux-lvm  linux-raid  extended

对于logical,iscsi,scsi,mpath,rbd,sheepdog,gluster,zfs池后端,不支持该元素

permissions 指定卷的文件模式
compat 兼容性级别,仅仅用于qcow2格式的卷
nocow 关闭COW,仅仅支持btrfs文件系统中的、文件形式的卷
features 特定于format的配置项
backingStore子元素

volume可能包含一个backingStore子元素,用来描述卷的copy-on-write后端存储,配置示例:

XML
1
2
3
4
5
6
7
8
9
10
<backingStore>
    <path>/var/lib/virt/images/master.img</path>
    <format type='raw'/>
    <permissions>
        <owner>107</owner>
        <group>107</group>
        <mode>0744</mode>
        <label>virt_image_t</label>
    </permissions>
</backingStore> 
迁移

在不同宿主机之间迁移客户机是比较复杂的问题,为了应对不同的需求,libvirt实现了多种解决方案。

网络数据传输

从数据传输的角度来看,libvirt支持两种类型的迁移:

传输类型 说明
native

使用Hypervisor本身的数据传输机制,直接在源、目标宿主机之前进行迁移。该方式可能不支持加密,但是需要最少的数据传输量

可能需要针对Hypervisor进行手工的网络相关的配置,某些Hypervisor需要打开大量的端口才能支持并发的迁移操作

tunnelled

利用libvirt内置此RPC通信协议进行数据传输,总是支持数据加密。数据先从源客户机拷贝到源宿主机上的libvirt,再发送到目标主机的libvrit,最终发送给目标客户机,因此需要额外的数据传输量

不需要针对Hypervisor进行网络配置,仅仅需要开放一个端口

如果客户机内存很大,因而脏页变化频繁,额外的数据传输可能导致性能问题

通信控制路径

从通信控制路径角度来看,libvirt支持三种类型的迁移:

通信控制路径 说明
受管直接迁移

由libvirt客户端进程控制迁移的各个阶段。libvirt客户端必须能够连接到源、目的客户机的libvirt守护进程,并通过身份验证。源、目的客户机上的libvirt守护进程不需要相互通信

如果libvirt客户端崩溃,或者丢失到libvirt守护进程的连接,则源宿主机上的迁移会取消,并在源宿主机上重启客户机的CPU

受管点对点迁移

libvirt客户端仅仅与源宿主机上的libvirt守护进程通信,由后者控制迁移的整个过程。源宿主机的libvirt守护进程会连接到目的宿主机的libvirt守护进程执行迁移操作,libvirt客户端崩溃或者断开不会影响迁移过程的推进

注意,源宿主机上的libvirt守护进程使用自己的身份(通常是root)而不是客户端的身份连接到目标宿主机

非受管直接迁移

libvirt客户端、守护进程都不控制迁移过程,迁移由底层的Hypervisor负责。libvirt仅仅在Hypervisor的管理层次上触发迁移请求

即使libvirt客户端、守护进程都崩溃,迁移过程还会继续推进

数据安全的考虑

迁移时,客户机的内存需要完整的在网络上传输,这可能导致客户机中的敏感信息被嗅探。

如果宿主机有多个网络接口,或者交换机支持Tagged虚拟局域网,最好将客户机的网络流量和迁移/管理网络流量隔离开来。

某些场景下,仅仅依靠网络隔离不能满足安全性需要,这时,需要使用libvirt的tunnelled传输,启用数据加密。

离线迁移

所谓离线迁移,就是把源主机的Domain状态设置为inactive,并且在迁移完毕后,Domain在源主机上继续运行,在目标主机上保持inactive状态。目前离线迁移不支持拷贝非共享存储。

PCI直通
简介

开发虚拟机固件(Open Virtual Machine Firmware)是一个为虚拟机提供UEFI支持的项目。从Linux 3.9开始,配合最近版本的QEMU,能够支持PCI设备直通,例如将显卡直接分配给虚拟机,这样虚拟机就获得接近原生显卡的性能。

对于Intel CPU,除了CPU需要支持VT-x之外,还需要支持VT-d(Intel Virtualization Technology for Directed I/O )。此技术改善系统的安全性、可靠性,同时支持提升虚拟化环境下的IO性能。

Intel的VT-d和AMD的AMD-Vi,通称IOMMU。IOMMU特性不但要求CPU支持,主板、BIOS也需要配合。开启IOMMU能够实现PCI直通、内存保护(免于被恶意、缺陷设备错误修改)。

启用IOMMU
内核参数
Shell
1
intel_iommu=on iommu=pt

设置iommu=pt能够防止内核触碰不能直通的设备。

重启后,使用下面的命令确认IOMMU已经正确启用: 

Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
dmesg | grep -i -e DMAR -e IOMMU
[    0.000000] Command line: BOOT_IMAGE=/boot/vmlinuz-5.8.0-45-generic root=UUID=66acedc4-69d6-48b2-888c-9516089e004a ro quiet splash net.ifnames=0 biosdevname=0 intel_iommu=on iommu=pt vt.handoff=7
[    0.010062] ACPI: DMAR 0x0000000073AE3000 0000C8 (v01 INTEL  CML      00000002 INTL 01000013)
[    0.223797] Kernel command line: BOOT_IMAGE=/boot/vmlinuz-5.8.0-45-generic root=UUID=66acedc4-69d6-48b2-888c-9516089e004a ro quiet splash net.ifnames=0 biosdevname=0 intel_iommu=on iommu=pt vt.handoff=7
# IOMMU被启用
[    0.223929] DMAR: IOMMU enabled
[    0.546449] DMAR: Host address width 39
[    0.546450] DMAR: DRHD base: 0x000000fed90000 flags: 0x0
[    0.546455] DMAR: dmar0: reg_base_addr fed90000 ver 1:0 cap 1c0000c40660462 ecap 19e2ff0505e
[    0.546456] DMAR: DRHD base: 0x000000fed91000 flags: 0x1
[    0.546459] DMAR: dmar1: reg_base_addr fed91000 ver 1:0 cap d2008c40660462 ecap f050da
[    0.546460] DMAR: RMRR base: 0x0000007372c000 end: 0x0000007374bfff
[    0.546461] DMAR: RMRR base: 0x00000078000000 end: 0x0000007c7fffff
[    0.546462] DMAR: RMRR base: 0x00000073792000 end: 0x00000073811fff
[    0.546464] DMAR-IR: IOAPIC id 2 under DRHD base  0xfed91000 IOMMU 1
[    0.546465] DMAR-IR: HPET id 0 under DRHD base 0xfed91000
[    0.546465] DMAR-IR: Queued invalidation will be enabled to support x2apic and Intr-remapping.
[    0.549640] DMAR-IR: Enabled IRQ remapping in x2apic mode
[    4.330593] iommu: Default domain type: Passthrough (set via kernel command line)
[    4.510270] DMAR: No ATSR found
[    4.510351] DMAR: dmar0: Using Queued invalidation
[    4.510354] DMAR: dmar1: Using Queued invalidation
# PCI设备被加入到的分组信息
[    4.510868] pci 0000:00:00.0: Adding to iommu group 0
[    4.510888] pci 0000:00:01.0: Adding to iommu group 1
[    4.510901] pci 0000:00:02.0: Adding to iommu group 2
[    4.510911] pci 0000:00:04.0: Adding to iommu group 3
[    4.510928] pci 0000:00:12.0: Adding to iommu group 4
[    4.510950] pci 0000:00:14.0: Adding to iommu group 5
[    4.510963] pci 0000:00:14.2: Adding to iommu group 5
[    4.510975] pci 0000:00:14.3: Adding to iommu group 6
[    4.510997] pci 0000:00:15.0: Adding to iommu group 7
[    4.511008] pci 0000:00:15.1: Adding to iommu group 7
[    4.511031] pci 0000:00:16.0: Adding to iommu group 8
[    4.511042] pci 0000:00:16.3: Adding to iommu group 8
[    4.511087] pci 0000:00:1b.0: Adding to iommu group 9
[    4.511121] pci 0000:00:1b.4: Adding to iommu group 9
[    4.511161] pci 0000:00:1c.0: Adding to iommu group 10
[    4.511197] pci 0000:00:1c.5: Adding to iommu group 10
[    4.511226] pci 0000:00:1d.0: Adding to iommu group 11
[    4.511265] pci 0000:00:1f.0: Adding to iommu group 12
[    4.511277] pci 0000:00:1f.3: Adding to iommu group 12
[    4.511290] pci 0000:00:1f.4: Adding to iommu group 12
[    4.511301] pci 0000:00:1f.5: Adding to iommu group 12
[    4.511316] pci 0000:00:1f.6: Adding to iommu group 12
[    4.511321] pci 0000:01:00.0: Adding to iommu group 1
[    4.511325] pci 0000:01:00.1: Adding to iommu group 1
[    4.511330] pci 0000:01:00.2: Adding to iommu group 1
[    4.511335] pci 0000:01:00.3: Adding to iommu group 1
[    4.511350] pci 0000:02:00.0: Adding to iommu group 9
[    4.511363] pci 0000:03:00.0: Adding to iommu group 9
[    4.511373] pci 0000:04:00.0: Adding to iommu group 10
[    4.511383] pci 0000:05:00.0: Adding to iommu group 10
[    4.511392] pci 0000:05:01.0: Adding to iommu group 10
[    4.511399] pci 0000:05:02.0: Adding to iommu group 10
[    4.511409] pci 0000:05:04.0: Adding to iommu group 10
[    4.511435] pci 0000:06:00.0: Adding to iommu group 10
[    4.511458] pci 0000:3a:00.0: Adding to iommu group 10
[    4.511471] pci 0000:6e:00.0: Adding to iommu group 10
[    4.511484] pci 0000:6f:00.0: Adding to iommu group 11
# VT-d被启用
[    4.511850] DMAR: Intel(R) Virtualization Technology for Directed I/O
[    4.711749]     intel_iommu=on
PCI设备分组

使用下面的脚本可以更加清楚的看到PCI设备是如何并映射到IOMMU分组的:

Shell
1
2
3
4
5
6
7
8
#!/bin/bash
shopt -s nullglob
for g in `find /sys/kernel/iommu_groups/* -maxdepth 0 -type d | sort -V`; do
    echo "IOMMU Group ${g##*/}:"
    for d in $g/devices/*; do
        echo -e "\t$(lspci -nns ${d##*/})"
    done;
done;

输出如下: 

Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
IOMMU Group 0:
    00:00.0 Host bridge [0600]: Intel Corporation Device [8086:9b44] (rev 02)
# Nvidia显卡被分配在组1
IOMMU Group 1:
    00:01.0 PCI bridge [0604]: Intel Corporation Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor PCIe Controller (x16) [8086:1901] (rev 02)
    01:00.0 VGA compatible controller [0300]: NVIDIA Corporation TU104GLM [Quadro RTX 5000 Mobile / Max-Q] [10de:1eb5] (rev a1)
    01:00.1 Audio device [0403]: NVIDIA Corporation TU104 HD Audio Controller [10de:10f8] (rev a1)
    01:00.2 USB controller [0c03]: NVIDIA Corporation TU104 USB 3.1 Host Controller [10de:1ad8] (rev a1)
    01:00.3 Serial bus controller [0c80]: NVIDIA Corporation TU104 USB Type-C UCSI Controller [10de:1ad9] (rev a1)
# Intel集成显卡被分配在组2
IOMMU Group 2:
    #                                                                  设备ID
    00:02.0 VGA compatible controller [0300]: Intel Corporation Device [8086:9bf6] (rev 05)
IOMMU Group 3:
    00:04.0 Signal processing controller [1180]: Intel Corporation Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Thermal Subsystem [8086:1903] (rev 02)
IOMMU Group 4:
    00:12.0 Signal processing controller [1180]: Intel Corporation Comet Lake PCH Thermal Controller [8086:06f9]
IOMMU Group 5:
    00:14.0 USB controller [0c03]: Intel Corporation Comet Lake USB 3.1 xHCI Host Controller [8086:06ed]
    00:14.2 RAM memory [0500]: Intel Corporation Comet Lake PCH Shared SRAM [8086:06ef]
# 无线网卡被分到组6
IOMMU Group 6:
    00:14.3 Network controller [0280]: Intel Corporation Wi-Fi 6 AX201 [8086:06f0]
IOMMU Group 7:
    00:15.0 Serial bus controller [0c80]: Intel Corporation Comet Lake PCH Serial IO I2C Controller #0 [8086:06e8]
    00:15.1 Serial bus controller [0c80]: Intel Corporation Comet Lake PCH Serial IO I2C Controller #1 [8086:06e9]
IOMMU Group 8:
    00:16.0 Communication controller [0780]: Intel Corporation Comet Lake HECI Controller [8086:06e0]
    00:16.3 Serial controller [0700]: Intel Corporation Device [8086:06e3]
# 指纹识别器、三星硬盘被分到组9
IOMMU Group 9:
    00:1b.0 PCI bridge [0604]: Intel Corporation Comet Lake PCI Express Root Port #17 [8086:06c0] (rev f0)
    00:1b.4 PCI bridge [0604]: Intel Corporation Comet Lake PCI Express Root Port #21 [8086:06ac] (rev f0)
    02:00.0 Non-Volatile memory controller [0108]: Samsung Electronics Co Ltd NVMe SSD Controller SM981/PM981/PM983 [144d:a808]
    03:00.0 Non-Volatile memory controller [0108]: Silicon Motion, Inc. Device [126f:2262] (rev 03)
# 雷电、读卡器被分到组10
IOMMU Group 10:
    00:1c.0 PCI bridge [0604]: Intel Corporation Device [8086:06b8] (rev f0)
    00:1c.5 PCI bridge [0604]: Intel Corporation Device [8086:06bd] (rev f0)
    04:00.0 PCI bridge [0604]: Intel Corporation JHL7540 Thunderbolt 3 Bridge [Titan Ridge 4C 2018] [8086:15ea] (rev 06)
    05:00.0 PCI bridge [0604]: Intel Corporation JHL7540 Thunderbolt 3 Bridge [Titan Ridge 4C 2018] [8086:15ea] (rev 06)
    05:01.0 PCI bridge [0604]: Intel Corporation JHL7540 Thunderbolt 3 Bridge [Titan Ridge 4C 2018] [8086:15ea] (rev 06)
    05:02.0 PCI bridge [0604]: Intel Corporation JHL7540 Thunderbolt 3 Bridge [Titan Ridge 4C 2018] [8086:15ea] (rev 06)
    05:04.0 PCI bridge [0604]: Intel Corporation JHL7540 Thunderbolt 3 Bridge [Titan Ridge 4C 2018] [8086:15ea] (rev 06)
    06:00.0 System peripheral [0880]: Intel Corporation JHL7540 Thunderbolt 3 NHI [Titan Ridge 4C 2018] [8086:15eb] (rev 06)
    3a:00.0 USB controller [0c03]: Intel Corporation JHL7540 Thunderbolt 3 USB Controller [Titan Ridge 4C 2018] [8086:15ec] (rev 06)
    6e:00.0 Unassigned class [ff00]: Realtek Semiconductor Co., Ltd. RTS525A PCI Express Card Reader [10ec:525a] (rev 01)
IOMMU Group 11:
    00:1d.0 PCI bridge [0604]: Intel Corporation Comet Lake PCI Express Root Port #9 [8086:06b0] (rev f0)
    6f:00.0 Non-Volatile memory controller [0108]: Silicon Motion, Inc. Device [126f:2262] (rev 03)
# 有线网卡被分到组12
IOMMU Group 12:
    00:1f.0 ISA bridge [0601]: Intel Corporation Device [8086:068e]
    00:1f.3 Multimedia audio controller [0401]: Intel Corporation Comet Lake PCH cAVS [8086:06c8]
    00:1f.4 SMBus [0c05]: Intel Corporation Comet Lake PCH SMBus Controller [8086:06a3]
    00:1f.5 Serial bus controller [0c80]: Intel Corporation Comet Lake PCH SPI Controller [8086:06a4]
    00:1f.6 Ethernet controller [0200]: Intel Corporation Ethernet Connection (10) I219-LM [8086:0d4e] 

需要注意,IOMMU组是直通的最小调度单元。也就是说,你可能被迫将多个设备一起直通给虚拟机,或者将设备放到其他PCI插槽。

隔离PCI设备

为了将设备分配给虚拟机,它和它所在分组的所有其他设备的驱动,必须更换为一个占位符驱动(stub 或 VFIO驱动)。这样,宿主机就不会和该设备进行交互。大部分设备,可以在启动虚拟机之前,即席的完成驱动更换操作。

对于显卡,没有这么方便。GPU驱动通常对动态rebinding支持的不好,不能将宿主机上正在使用的GPU透明的直通给虚拟机。因此,我们需要在宿主机启动时,尽早将占位符驱动bind给GPU。这样,除非驱动被换回,或者虚拟机claim该GPU,它会保持inactive状态。

之所以要求尽早,是因为Linux现有的驱动机制是每个驱动在初始化时,自行去寻找没有初始化的,自己关注的硬件。因此驱动的初始化顺序非常关键。如果慢了,物理驱动就会抢先绑定设备。

从内核4.1开始,VFIO驱动vfio-pci被包含其中。可以用来代替pci-stub。

基于pci-stub

修改内核参数:

Shell
1
pci-stub.ids=8086:9bf6

pci-stub根据ID来识别设备,也就是说,我们需要提供用于直通的PCI设备的ID。对于上面的输出中,Intel显卡的ID是8086:9bf6。

如果要绑定多个设备,ID需要用逗号分隔。 

随后我们需要执行下面的命令并重启:

Shell
1
sudo update-grub
基于vfio-pci

内核总是先初始化所有内建的驱动,然后才逐个初始化外部模块。 在外部模块中,我们可以通过softdep来增加模块间的依赖关键: 例如我们可以把vfio-pci列为nvidia的依赖,那么vfio-pci总会被在nvidia之前初始化,而确保能抢占到硬件。

我们首先需要弄清楚,需要直通的硬件,当前是被什么驱动所绑定:

Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
lspci -nnv
 
00:02.0 VGA compatible controller [0300]: Intel Corporation Device [8086:9bf6] (rev 05) (prog-if 00 [VGA controller])
    DeviceName: Onboard IGD
    Subsystem: Hewlett-Packard Company Device [103c:8780]
    Flags: bus master, fast devsel, latency 0, IRQ 221
    Memory at df000000 (64-bit, non-prefetchable) [size=16M]
    Memory at a0000000 (64-bit, prefetchable) [size=256M]
    I/O ports at 4000 [size=64]
    Expansion ROM at 000c0000 [virtual] [disabled] [size=128K]
    Capabilities: <access denied>
    # 当前使用的驱动
    Kernel driver in use: i915
    Kernel modules: i915

i915是内核builtin的驱动,无法通过修改softdep,改变驱动加载顺序。解决此问题,有两种方案:

  1. 将i915从内核剥离出来,编译为模块
  2. 将vfio-pci编译进内核

如果选择方案2,则使用内核参数: vfio-pci.ids=8086:9bf6 即可完成设备隔离。对于Ubuntu 20.04,vfio-pci已经默认编译到内核中。

Hooks

libvirt提供钩子(Hooks)功能,你可以添加自定义的脚本,存放在/etc/libvirt/hooks目录下,以便在:

  1. libvirt守护程序启动、停止、reload时(对应daemon子目录)
  2. QEMU客户机启动、停止时(对应qemu子目录)
  3. 一个虚拟网络启动、停止时,或者一个网络接口接入、断开网络时(对应network子目录)

执行特定的逻辑。Hooks可以基于Bash或者Python编写。

常见问题
Vmware中运行的libvirt
无法使用macvtap直连的虚拟网络

报错:The virtual machine's operating system has attempted to enable promiscuous mode on adapter 'Ethernet0'.

要解决此问题,需要在宿主机上执行:

Shell
1
2
# vmnet0改为实际使用的Vmware虚拟网络
sudo chmod a+rw /dev/vmnet0
无法启动基于KVM的嵌套虚拟机

报错信息:error: unsupported configuration: Domain requires KVM, but it is not available. Check that virtualization is enabled in the host BIOS, and host configuration is setup to load the kvm modules.

解决办法:VM ⇨ Settings,弹出的对话框中,选择Processors,在面板右侧勾选Virtualize Intel VT-X/EPT or AMD-V/RVI

零散问题
AppArmor导致无法启动QEMU虚拟机

报错信息:error: internal error: cannot load AppArmor profile 'libvirt-cf9a1d56-306d-4a6f-8b0e-79ac070aa8fe'

这个错误与安全模块AppArmor有关,你可以简单的禁用libvirt的QEMU驱动的安全驱动来规避:

/etc/libvirt/qemu.conf
Shell
1
2
# 添加
security_driver = "none"

并执行:

Shell
1
sudo touch /etc/apparmor.d/disable/usr.sbin.libvirtd

然后重启libvirtd: sudo service libvirt-bin restart  

无法启动桥接的QEMU虚拟机

报错信息:
error: internal error: process exited while connecting to monitor: failed to open /dev/net/tun: Operation not permitted
failed to launch bridge helper
qemu-system-x86_64: -netdev bridge,id=tap0,br=br0: Device 'bridge' could not be initialized

解决办法:

/etc/libvirt/qemu.conf
Shell
1
2
3
4
5
6
7
8
9
10
11
# 添加如下几行
 
user = "root"
group = "root"
cgroup_device_acl = [
        "/dev/null", "/dev/full", "/dev/zero",
        "/dev/random", "/dev/urandom",
        "/dev/ptmx", "/dev/kvm", "/dev/kqemu",
        "/dev/rtc", "/dev/hpet", "/dev/net/tun"
]
clear_emulator_capabilities = 0
-daemonize选项导致启动QEMU虚拟机报错

报错信息:error: internal error: process exited while connecting to monitor

解决办法:从Domain配置文件中去掉 <qemu:arg value='-daemonize'/> 即可。

Ubuntu 16.04 Server无法启动

症状:无法启动成功,配置SDL则可以启动,但是需要Headless的服务器。

解决办法:配置一个graphics,类型为VNC。

显示相关报错:SDL无法初始化

这导致虚拟机无法启动,QEMU的SDL窗口不出现。

报错信息:error: internal error: process exited while connecting to monitor: Could not initialize SDL(No available video device) - exiting

解决办法:

  1. 首先在宿主机执行 env | egrep "DISPLAY|XAUTH" ,获取两个环境变量的值
  2. 然后修改Domain配置文件,把type=sdl的graphics元素的display和xauth属性修改为上面命令输出的环境变量值:
    XML
    1
    <graphics type='sdl' display=':0' xauth='/home/alex/.Xauthority'/>
找不到可启动硬盘

报错信息:

Booting from Hard Disk...
Boot failed: not abootable disk
No bootable device.

报错原因:可能是Domain配置文件搞错了镜像文件的格式。

解决办法:修改为正确的镜像格式,例如: <driver name='qemu' type='qcow2'/> 

无法连接到客户机Console

报错信息:error: internal error: cannot find character device <null>

解决办法:可能是你的Domain配置中缺少Console和串口的配置,添加:

XML
1
2
3
4
5
6
<serial type='pty'>
    <target port='0'/>
</serial>
<console type='pty'>
    <target type='serial' port='0'/>
</console>
如何退出客户机Console

要退出从virsh console进入的控制台,可以按组合键 Ctrl+] 。

能连接到客户机Console但卡死

问题现象:光标在Escape character is ^]下面一行闪烁,但是按任何键都没有反应。

问题原因:客户机没有进行适当的配置。

解决办法: 通过其它方式登录到客户机,修改配置。

以Fedora为例:

  1. 修改/etc/sysconfig/grub(该文件是指向/etc/default/grub的符号链接):
    1
    2
    # 修改这一行,在命令行尾部添加console=...这个内核参数
    GRUB_CMDLINE_LINUX="... console=ttyS0,115200"
  2. 更新GRUB: grub2-mkconfig > /boot/grub2/grub.cfg  
  3. 执行命令 echo ttyS0 >> /etc/securetty 并重启客户机

现在你可以使用virsh console连接到客户机,并执行Shell命令了。

Console窗口太小

当通过串口(上面的方式)连接到Linux时,缺乏协商机制(Negotiate About Window Size ,NAWS),Linux也不知道你的(通过串口连接的)控制台屏幕屏幕大小。

在这种情况下,通常会使用 stty -a命令显示的结果,来设置窗口尺寸,往往不靠谱,偏小。

你可以在.bashrc中增加 /usr/bin/resize > /dev/null,resize是来自xterm的工具,它可以使用VT100-style的光标位置来判断屏幕尺寸。resize会改变stty输出结果。

无法删除外部快照

报错信息:unsupported configuration: deletion of 1 external disk snapshots not supported yet

解决办法:目前libvirt无法删除外部快照的磁盘文件,因此我们仅仅需要删除它的元数据,调用snapshot-delete时添加--metadata参数。磁盘文件可以手工删除。

虚拟局域网(nat方式)无法连通

问题现象:无法ping通网桥,无法ping通外网,Vmware无法桥接到libvirt创建的网桥

原因分析:

在我的机器上,外网是通过Wifi连接的。如果设置虚拟局域网为net-autostart,则虚拟局域网启动时Wifi可能尚未连接,这会导致客户机无法连通外网的情况,原因未知。

将Vmware workstation中的虚拟机也桥接到virbr0时,提示无法连接。出现这种现象的原因也未知,但是先启动一个libvirt管理的客户机“激活”一下VLAN,然后启动Vmware就没事了。

解决办法:在Wifi连接后,启动VLAN、一台libvirt管理的虚拟机,然后再启动Vmware。

使用QEMU驱动访问NFS报错:Permission denied

设置NFS导出时,要启用no_root_squash选项,让NFS客户端(QEMU宿主机)以root权限访问NFS:

1
/home/alex/Vmware/libvirt/images/default  10.0.0.0/8(rw,no_root_squash) 

然后,参考本文针对问题《AppArmor导致无法启动QEMU虚拟机》、 《无法启动桥接的QEMU虚拟机》的解决方案。

启动或迁移时报错:Unable to get index for interface  ***: No such device

可能是因为虚拟局域网配置错误:

XML
1
2
3
4
5
6
<network>
  <name>default</name>
  <forward mode="bridge">
    <interface dev="eth0"/> <!-- 必须和宿主机的以太网设备名称一致 -->
  </forward>
</network> 
迁移时报错:Unsupported machine type

在Domain配置文件中指定宿主机集群中通用的机器类型,避免此问题:

XML
1
2
3
<os>
    <type arch='x86_64' machine='pc-i440fx-2.0'>hvm</type>
</os>
迁移时报错,无法连接到49152端口

报错信息:

unable to connect to server at 'Zircon:49152': Connection refused 
unable to connect to server at 'centos.local:49152': No route to host

报错原因:默认情况下,迁移的源使用目的主机的主机名来建立迁移连接,出现此错误说明源无法正确连接到目标主机——Zircon,可以ping验证一下

解决方案:

  1. 修改目的主机libvirt的QEMU配置:
    /etc/libvirt/qemu.conf
    1
    2
    # 指向目的主机可被迁移源访问的IP地址或者DNS名称
    migration_host = "zircon.local"
  2.  老版本的libvirt可能不支持上述配置,这时可以配置DNS服务器或者迁移源的/etc/hosts文件

  3. No route to host错误可能是因为目标主机的防火墙导致,注意检查设置
迁移时报错,Unable to read from monitor: Connection reset by peer

问题现象:迁移进度到达100%报如上错误。这个报错很笼统,详细的错误信息可以在目的宿主机的客户机运行日志( /var/log/libvirt/qemu/$VM.log  )中看到:

/var/log/libvirt/qemu/debian-20.log
1
2
3
qemu: warning: error while loading state for instance 0x0 of device 'cpu'
load of migration failed
2016-10-19 01:47:45.913+0000: shutting down
启动后很快进入pause状态

可能是因为卷所在的物理磁盘空间不足。

← 2015年7月架构师培训笔记
Spring对JMS的支持 →

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

Related Posts

  • KVM和QEMU学习笔记
  • CoreOS知识集锦
  • Kata Containers学习笔记

Recent Posts

  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
  • A Comprehensive Study of Kotlin for Java Developers
  • 背诵营笔记
  • 利用LangChain和语言模型交互
  • 享学营笔记
ABOUT ME

汪震 | Alex Wong

江苏淮安人,现居北京。目前供职于腾讯云,专注容器方向。

GitHub:gmemcc

Git:git.gmem.cc

Email:gmemjunk@gmem.cc@me.com

ABOUT GMEM

绿色记忆是我的个人网站,域名gmem.cc中G是Green的简写,MEM是Memory的简写,CC则是我的小天使彩彩名字的简写。

我在这里记录自己的工作与生活,同时和大家分享一些编程方面的知识。

GMEM HISTORY
v2.00:微风
v1.03:单车旅行
v1.02:夏日版
v1.01:未完成
v0.10:彩虹天堂
v0.01:阳光海岸
MIRROR INFO
Meta
  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org
Recent Posts
  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
    In this blog post, I will walk ...
  • A Comprehensive Study of Kotlin for Java Developers
    Introduction Purpose of the Study Understanding the Mo ...
  • 背诵营笔记
    Day 1 Find Your Greatness 原文 Greatness. It’s just ...
  • 利用LangChain和语言模型交互
    LangChain是什么 从名字上可以看出来,LangChain可以用来构建自然语言处理能力的链条。它是一个库 ...
  • 享学营笔记
    Unit 1 At home Lesson 1 In the ...
  • K8S集群跨云迁移
    要将K8S集群从一个云服务商迁移到另外一个,需要解决以下问题: 各种K8S资源的迁移 工作负载所挂载的数 ...
  • Terraform快速参考
    简介 Terraform用于实现基础设施即代码(infrastructure as code)—— 通过代码( ...
  • 草缸2021
    经过四个多月的努力,我的小小荷兰景到达极致了状态。

  • 编写Kubernetes风格的APIServer
    背景 前段时间接到一个需求做一个工具,工具将在K8S中运行。需求很适合用控制器模式实现,很自然的就基于kube ...
  • 记录一次KeyDB缓慢的定位过程
    环境说明 运行环境 这个问题出现在一套搭建在虚拟机上的Kubernetes 1.18集群上。集群有三个节点: ...
  • eBPF学习笔记
    简介 BPF,即Berkeley Packet Filter,是一个古老的网络封包过滤机制。它允许从用户空间注 ...
  • IPVS模式下ClusterIP泄露宿主机端口的问题
    问题 在一个启用了IPVS模式kube-proxy的K8S集群中,运行着一个Docker Registry服务 ...
  • 念爷爷
      今天是爷爷的头七,十二月七日、阴历十月廿三中午,老人家与世长辞。   九月初,回家看望刚动完手术的爸爸,发

  • 6 杨梅坑

  • liuhuashan
    深圳人才公园的网红景点 —— 流花山

  • 1 2020年10月拈花湾

  • 内核缺陷触发的NodePort服务63秒延迟问题
    现象 我们有一个新创建的TKE 1.3.0集群,使用基于Galaxy + Flannel(VXLAN模式)的容 ...
  • Galaxy学习笔记
    简介 Galaxy是TKEStack的一个网络组件,支持为TKE集群提供Overlay/Underlay容器网 ...
TOPLINKS
  • Zitahli's blue 91 people like this
  • 梦中的婚礼 64 people like this
  • 汪静好 61 people like this
  • 那年我一岁 36 people like this
  • 为了爱 28 people like this
  • 小绿彩 26 people like this
  • 彩虹姐姐的笑脸 24 people like this
  • 杨梅坑 6 people like this
  • 亚龙湾之旅 1 people like this
  • 汪昌博 people like this
  • 2013年11月香山 10 people like this
  • 2013年7月秦皇岛 6 people like this
  • 2013年6月蓟县盘山 5 people like this
  • 2013年2月梅花山 2 people like this
  • 2013年淮阴自贡迎春灯会 3 people like this
  • 2012年镇江金山游 1 people like this
  • 2012年徽杭古道 9 people like this
  • 2011年清明节后扬州行 1 people like this
  • 2008年十一云龙公园 5 people like this
  • 2008年之秋忆 7 people like this
  • 老照片 13 people like this
  • 火一样的六月 16 people like this
  • 发黄的相片 3 people like this
  • Cesium学习笔记 90 people like this
  • IntelliJ IDEA知识集锦 59 people like this
  • 基于Kurento搭建WebRTC服务器 38 people like this
  • Bazel学习笔记 37 people like this
  • PhoneGap学习笔记 32 people like this
  • NaCl学习笔记 32 people like this
  • 使用Oracle Java Mission Control监控JVM运行状态 29 people like this
  • Ceph学习笔记 27 people like this
  • 基于Calico的CNI 27 people like this
Tag Cloud
ActiveMQ AspectJ CDT Ceph Chrome CNI Command Cordova Coroutine CXF Cygwin DNS Docker eBPF Eclipse ExtJS F7 FAQ Groovy Hibernate HTTP IntelliJ IO编程 IPVS JacksonJSON JMS JSON JVM K8S kernel LB libvirt Linux知识 Linux编程 LOG Maven MinGW Mock Monitoring Multimedia MVC MySQL netfs Netty Nginx NIO Node.js NoSQL Oracle PDT PHP Redis RPC Scheduler ServiceMesh SNMP Spring SSL svn Tomcat TSDB Ubuntu WebGL WebRTC WebService WebSocket wxWidgets XDebug XML XPath XRM ZooKeeper 亚龙湾 单元测试 学习笔记 实时处理 并发编程 彩姐 性能剖析 性能调优 文本处理 新特性 架构模式 系统编程 网络编程 视频监控 设计模式 远程调试 配置文件 齐塔莉
Recent Comments
  • qg on Istio中的透明代理问题
  • heao on 基于本地gRPC的Go插件系统
  • 黄豆豆 on Ginkgo学习笔记
  • cloud on OpenStack学习笔记
  • 5dragoncon on Cilium学习笔记
  • Archeb on 重温iptables
  • C/C++编程:WebSocketpp(Linux + Clion + boostAsio) – 源码巴士 on 基于C/C++的WebSocket库
  • jerbin on eBPF学习笔记
  • point on Istio中的透明代理问题
  • G on Istio中的透明代理问题
  • 绿色记忆:Go语言单元测试和仿冒 on Ginkgo学习笔记
  • point on Istio中的透明代理问题
  • 【Maven】maven插件开发实战 – IT汇 on Maven插件开发
  • chenlx on eBPF学习笔记
  • Alex on eBPF学习笔记
  • CFC4N on eBPF学习笔记
  • 李运田 on 念爷爷
  • yongman on 记录一次KeyDB缓慢的定位过程
  • Alex on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • haolipeng on 基于本地gRPC的Go插件系统
  • 吴杰 on 基于C/C++的WebSocket库
©2005-2025 Gmem.cc | Powered by WordPress | 京ICP备18007345号-2