<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>绿色记忆 &#187; libvirt</title>
	<atom:link href="https://blog.gmem.cc/tag/libvirt/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.gmem.cc</link>
	<description></description>
	<lastBuildDate>Fri, 03 Apr 2026 04:13:36 +0000</lastBuildDate>
	<language>en-US</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.9.14</generator>
	<item>
		<title>CoreOS知识集锦</title>
		<link>https://blog.gmem.cc/coreos-faq</link>
		<comments>https://blog.gmem.cc/coreos-faq#comments</comments>
		<pubDate>Wed, 12 Oct 2016 10:28:13 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Virtualization]]></category>
		<category><![CDATA[libvirt]]></category>

		<guid isPermaLink="false">https://blog.gmem.cc/?p=13526</guid>
		<description><![CDATA[<p>简介 CoreOS是一个轻量级的Linux操作系统，CoreOS的自动化、安全性、可扩容性特征，让其非常适用于集群化的部署场景。 与其它的发行版不同，CoreOS没有包管理器，它倾向于在容器（例如Docker）中运行应用程序。CoreOS对流行的容器系统提供了开箱即用的支持。 CoreOS可以在云服务（例如EC2、GCE）、虚拟化平台（VMware、OpenStack、KVM）、裸金属值上运行。 Ignition 这是CoreOS提供的新的VM初始化机制，用于代替cloud-config。 Ignition能够执行磁盘分区、分区格式化、写入文件、配置用户、配置网络、创建RAID阵列等初始化操作。Ignition的运行时机非常的早，它在systemd启动之前、任何永久存储挂载之前即被调用。 在VM第一次（也仅仅是第一次）启动时，Ignition会从文件系统、URL、Hypervisor bridge读取JSON格式的配置文件，并将配置应用到VM。 运行CoreOS 即使是学习阶段，也最好创建3台CoreOS的集群，那样更容易认识CoreOS的特性。为了让CoreOS集群正常启动，你需要提供“点火配置”（ Ignition config ），或者通过user_data提供一个cloud-config。 libvirt/cloud-config 本节记录基于libvirt创建CoreOS客户机的详细过程。 获取镜像 我们可以下载CoreOS官网提供的qcow2格式的镜像，此镜像适用于QEMU： [crayon-69d26d56e37bd209689279/] 下载完毕后，解压镜像到[crayon-69d26d56e37c4889942636-i/] 备用，默认扩展名是img，手工改成qcow2。 Config drive <a class="read-more" href="https://blog.gmem.cc/coreos-faq">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/coreos-faq">CoreOS知识集锦</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h1"><span class="graybg">简介</span></div>
<p>CoreOS是一个轻量级的Linux操作系统，CoreOS的自动化、安全性、可扩容性特征，让其非常适用于集群化的部署场景。</p>
<p>与其它的发行版不同，CoreOS没有包管理器，它倾向于在容器（例如Docker）中运行应用程序。CoreOS对流行的容器系统提供了开箱即用的支持。</p>
<p>CoreOS可以在云服务（例如EC2、GCE）、虚拟化平台（VMware、OpenStack、KVM）、裸金属值上运行。</p>
<div class="blog_h1"><span class="graybg">Ignition</span></div>
<p>这是CoreOS提供的新的VM初始化机制，用于代替cloud-config。</p>
<p>Ignition能够执行磁盘分区、分区格式化、写入文件、配置用户、配置网络、创建RAID阵列等初始化操作。Ignition的运行时机非常的早，它在systemd启动之前、任何永久存储挂载之前即被调用。</p>
<p>在VM第一次（也仅仅是第一次）启动时，Ignition会从文件系统、URL、Hypervisor bridge读取JSON格式的配置文件，并将配置应用到VM。</p>
<div class="blog_h1"><span class="graybg">运行CoreOS</span></div>
<p>即使是学习阶段，也最好创建3台CoreOS的集群，那样更容易认识CoreOS的特性。为了让CoreOS集群正常启动，你需要提供<a href="https://coreos.com/blog/introducing-ignition.html">“点火配置”（ Ignition config ）</a>，或者通过user_data提供一个cloud-config。</p>
<div class="blog_h2"><span class="graybg">libvirt/cloud-config</span></div>
<p>本节记录基于libvirt创建CoreOS客户机的详细过程。</p>
<div class="blog_h3"><span class="graybg">获取镜像</span></div>
<p>我们可以下载CoreOS官网提供的qcow2格式的镜像，此镜像适用于QEMU：</p>
<pre class="crayon-plain-tag">wget https://stable.release.core-os.net/amd64-usr/current/coreos_production_qemu_image.img.bz2</pre>
<p>下载完毕后，解压镜像到<pre class="crayon-plain-tag">/home/alex/Vmware/KVM/coreos-20/vda.qcow2</pre> 备用，默认扩展名是img，手工改成qcow2。</p>
<div class="blog_h3"><span class="graybg">Config drive</span></div>
<p>为了配置CoreOS实例，我们需要在宿主机上创建一个目录，此目录中包含一些配置信息。此目录最终映射将为客户机上的一个文件系统。执行以下命令：</p>
<pre class="crayon-plain-tag">mkdir -p /home/alex/Vmware/KVM/coreos-20/cloud-config/openstack/latest/
touch /home/alex/Vmware/KVM/coreos-20/cloud-config/openstack/latest/user_data</pre>
<p>user_data是<a href="https://coreos.com/os/docs/latest/cloud-config.html">cloud config</a>格式的配置文件，它提供了客户机运行时所需要的定制化信息。我们至少需要在其中配置SSH Key以便（初次）登录到CoreOS中：</p>
<pre class="crayon-plain-tag">#cloud-config

ssh_authorized_keys:
 - ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIBlsU4YrAi ... EIokU+jOd0MrsnOwQn9wJbov8Xhyw==</pre>
<div class="blog_h3"><span class="graybg">定义Domain</span></div>
<p>我们可以使用类似下面的命令来定义CoreOS的Domain：</p>
<pre class="crayon-plain-tag">virt-install --import --name coreos-20 --ram 1024 --vcpus 1
             --disk path=/home/alex/Vmware/KVM/coreos-20/vda.qcow2,format=qcow2,bus=virtio 
             --filesystem /home/alex/Vmware/KVM/coreos-20/cloud-config/,config-2,type=mount,mode=squash</pre>
<p>或者，在常规的Domain XML配置文件中，添加devices的子元素：</p>
<pre class="crayon-plain-tag">&lt;filesystem type='mount' accessmode='squash'&gt;
    &lt;source dir='/home/alex/Vmware/KVM/coreos-20/cloud-config/'/&gt;
    &lt;target dir='config-2'/&gt;
&lt;/filesystem&gt;</pre>
<div class="blog_h3"><span class="graybg">登录到CoreOS</span></div>
<p>默认情况下，CoreOS会尝试通过DHCP来获得自身的网络配置，我们可以在启动Domain后立即查看控制台，CoreOS的IP地址会打印在上面。执行下面的命令登录到CoreOS：</p>
<pre class="crayon-plain-tag">ssh -i path-to-key core@ip-of-coreos</pre>
<p>登录成功后，你可以为core用户设置密码，或者执行其它操作。 </p>
<div class="blog_h3"><span class="graybg">完整libvirt配置</span></div>
<pre class="crayon-plain-tag">&lt;domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'&gt;
    &lt;name&gt;coreos-20&lt;/name&gt;
    &lt;memory unit='MiB'&gt;1024&lt;/memory&gt;
    &lt;vcpu placement='static'&gt;1&lt;/vcpu&gt;
    &lt;os&gt;
        &lt;type arch='x86_64' machine='pc'&gt;hvm&lt;/type&gt;
    &lt;/os&gt;
    &lt;features&gt;
        &lt;acpi/&gt;
    &lt;/features&gt;
    &lt;cpu mode='custom' match='exact'&gt;
        &lt;model fallback='allow'&gt;SandyBridge&lt;/model&gt;
    &lt;/cpu&gt;
    &lt;clock offset='utc'/&gt;
    &lt;on_poweroff&gt;destroy&lt;/on_poweroff&gt;
    &lt;on_reboot&gt;restart&lt;/on_reboot&gt;
    &lt;on_crash&gt;destroy&lt;/on_crash&gt;
    &lt;devices&gt;
        &lt;emulator&gt;/usr/bin/qemu-system-x86_64&lt;/emulator&gt;
        &lt;disk type='volume' device='disk'&gt;
            &lt;driver name='qemu' type='qcow2'/&gt;
            &lt;source pool="default" volume="coreos-20.qcow2" /&gt;
            &lt;target dev='vda' bus='virtio'/&gt;
            &lt;boot order='1'/&gt;
        &lt;/disk&gt;
        &lt;filesystem type='mount' accessmode='squash'&gt;
            &lt;source dir='/home/alex/Vmware/libvirt/mount/coreos-base/cloud-config/'/&gt;
            &lt;target dir='config-2'/&gt;
        &lt;/filesystem&gt;
        &lt;controller type='usb' index='0'/&gt;
        &lt;controller type='pci' index='0' model='pci-root'/&gt;
        &lt;interface type='network'&gt;
            &lt;mac address='DE:AD:BE:EF:00:20'/&gt;
            &lt;source network='default'/&gt;
            &lt;model type='virtio'/&gt;
        &lt;/interface&gt;
        &lt;serial type='pty'&gt;
            &lt;target port='0'/&gt;
        &lt;/serial&gt;
        &lt;console type='pty'&gt;
            &lt;target type='serial' port='0'/&gt;
        &lt;/console&gt;
        &lt;input type='mouse' bus='ps2'/&gt;
        &lt;input type='keyboard' bus='ps2'/&gt;
        &lt;memballoon model='virtio'/&gt;
    &lt;/devices&gt;
&lt;/domain&gt;</pre>
<div class="blog_h2"><span class="graybg">libvirt/Ignition-config</span></div>
<p>当前推荐的配置CoreOS的方式是Ignition，但是目前libvirt没有对Ignition的直接支持，需要引入QEMU特有的配置片断。</p>
<p>Ignition配置为JSON格式，示例：</p>
<pre class="crayon-plain-tag">{
  "ignition": {
    "config": {},
    "timeouts": {},
    "version": "2.1.0"
  },
  "networkd": {},
  "passwd": {
    "users": [
      {
        "name": "core",
        "sshAuthorizedKeys": [
          "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIBlsU4YrAif8Oh4Qdcq1SuF+CbPdr5T3DE3zzeYGG8nkcDMt/9dEjT8eHTMW+4BzCoIfYrIWIprJoykMnhZONBXnoXc/541tqU6MqF0ZRF0QlzSq6VLLLebG3zz+avdJSNLMAvolCLczP536EIokU+jOd0MrsnOwQn9wJbov8Xhyw=="
        ]
      }
    ]
  },
  "storage": {
    "files": [
      {
        "filesystem": "root",
        "group": {},
        "path": "/etc/hostname",
        "user": {},
        "contents": {
          "source": "data:,coreos-21",
          "verification": {}
        }
      }
    ]
  },
  "systemd": {}
}</pre>
<p>libvrit Domain配置示例：</p>
<pre class="crayon-plain-tag">&lt;domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'&gt;
    ...
    &lt;qemu:commandline&gt;
    	&lt;qemu:arg value="-fw_cfg"/&gt;
    	&lt;qemu:arg value="name=opt/com.coreos/config,file=/home/alex/Vmware/libvirt/mount/coreos-base/provision.ign"/&gt;
  &lt;/qemu:commandline&gt;
&lt;/domain&gt;</pre>
<p>注意：QEMU 2.0版本不支持 -fw_cfg，手工构建最新版本可以支持。 </p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/coreos-faq">CoreOS知识集锦</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/coreos-faq/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>KVM和QEMU学习笔记</title>
		<link>https://blog.gmem.cc/kvm-qemu-study-note</link>
		<comments>https://blog.gmem.cc/kvm-qemu-study-note#comments</comments>
		<pubDate>Mon, 17 Aug 2015 06:22:32 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Virtualization]]></category>
		<category><![CDATA[libvirt]]></category>

		<guid isPermaLink="false">https://blog.gmem.cc/?p=12816</guid>
		<description><![CDATA[<p>简介 虚拟化基础知识 关于Hypervisor Hypervisor，即虚拟机监管程序（virtual machine monitor ，VMM）。它可以是电脑上的软件、固件或者硬件，用于建立和执行虚拟机。拥有Hypervisor后，你可以执行一个或者多个虚拟机。这些虚拟机称为客户机（guest machine），相应的Hypervisor所在机器称为宿主机（host machine）。 传统的虚拟化技术都是基于Hypervisor的，它们被分为两类： bare-metal Hypervisor：裸机监管程序，直接运行在硬件上 Hosted Hypervisor：被宿主监管程序，Hypervisor运行在操作系统之上，就像一个应用程序一样 特权级别和虚拟化类型 X86处理器定义了定义了0-3个特权级，数字越小，权限越高。 对于Linux来说，在没有虚拟化的情况下，内核态对应了0级，用户态对应3级。 传统的虚拟化技术都是在宿主机、客户机之间加一个Hypervisor。因此，当在Linux上运行Linux虚拟机时，两个内核都需要运行在0级。根据解决此冲突（对于Host来说整个Client是用户程序）的方式的不同，虚拟化被分为3种类型： 虚拟化类型 说明 半虚拟化 Paravirtualization。此类型的特点是： <a class="read-more" href="https://blog.gmem.cc/kvm-qemu-study-note">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/kvm-qemu-study-note">KVM和QEMU学习笔记</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h1"><span class="graybg">简介</span></div>
<div class="blog_h2"><span class="graybg">虚拟化基础知识</span></div>
<div class="blog_h3"><span class="graybg">关于Hypervisor</span></div>
<p>Hypervisor，即虚拟机监管程序（virtual machine monitor ，VMM）。它可以是电脑上的软件、固件或者硬件，用于建立和执行虚拟机。拥有Hypervisor后，你可以执行一个或者多个虚拟机。这些虚拟机称为客户机（guest machine），相应的Hypervisor所在机器称为宿主机（host machine）。</p>
<p>传统的虚拟化技术都是基于Hypervisor的，它们被分为两类：</p>
<ol>
<li>bare-metal Hypervisor：裸机监管程序，直接运行在硬件上</li>
<li>Hosted Hypervisor：被宿主监管程序，Hypervisor运行在操作系统之上，就像一个应用程序一样</li>
</ol>
<div class="blog_h3"><span class="graybg">特权级别和虚拟化类型</span></div>
<p>X86处理器定义了定义了0-3个特权级，数字越小，权限越高。</p>
<p>对于Linux来说，在没有虚拟化的情况下，内核态对应了0级，用户态对应3级。</p>
<p>传统的虚拟化技术都是在宿主机、客户机之间加一个Hypervisor。因此，当在Linux上运行Linux虚拟机时，两个内核都需要运行在0级。根据解决此冲突（对于Host来说整个Client是用户程序）的方式的不同，虚拟化被分为3种类型：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 25%; text-align: center;">虚拟化类型</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>半虚拟化</td>
<td>
<p>Paravirtualization。此类型的特点是：</p>
<ol>
<li>Hypervisor运行在内核态，对应0级特权</li>
<li>客户机的内核不运行在内核态，内核被修改，需要在0级执行的特权指令转调Hypervisor</li>
<li>客户机上的用户程序运行在3级</li>
</ol>
</td>
</tr>
<tr>
<td>非硬件辅助全虚拟化</td>
<td>
<p>Full Virtualization without Hardware Assist。此类型的特点是：</p>
<ol>
<li>Hypervisor运行在内核态</li>
<li>Hypervisor位客户机提供一个模拟的CPU</li>
<li>客户机内核不需要修改，运行在模拟CPU上的0级</li>
<li>Hypervisor对客户机的CPU指令进行转译，变成正式CPU的指令</li>
</ol>
</td>
</tr>
<tr>
<td>硬件辅助全虚拟化</td>
<td>
<p>Full Virtualization with Hardware Assist。由Intel VT或AMD-V实现，此类型的特点是：</p>
<ol>
<li>Hypervisor运行在新的-1级别</li>
<li>客户机内核直接运行在真实CPU的0级</li>
</ol>
</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">KVM</span></div>
<p>KVM，即基于内核的虚拟机（Kernel-based Virtual Machine），是构建于支持虚拟化扩展（Intel VT 或者 AMD-V）的x<span style="background-color: #c0c0c0;">86平台</span>、<span style="background-color: #c0c0c0;">Linux</span>操作系统之上的，完整虚拟化解决方案。使用KVM，你可以运行多个Linux或者Windows系统镜像，这些虚拟机拥有私有的虚拟化设备，包括网卡、磁盘、显卡等。</p>
<p>KVM主要包含两个内核组件：</p>
<ol>
<li>可加载的内核模块 kvm.ko，负责核心的虚拟化基础功能</li>
<li>针对处理器的模块：kvm-intel.ko或者kvm-amd.ko</li>
</ol>
<p>从2.6.20版本的Linux内核开始，KVM的内核组件就被包含在其中。从<a href="http://wiki.qemu.org/Main_Page">QEMU</a> 1.3开始，KVM的用户空间组件被包含在其中。要查看你的机器是否支持KVM，可以执行：<pre class="crayon-plain-tag">lsmod | grep kvm</pre> </p>
<div class="blog_h2"><span class="graybg">QEMU</span></div>
<p>QEMU是一个基于<span style="background-color: #c0c0c0;">通用目的</span>开源仿真器/虚拟器软件。它可以模拟：</p>
<ol>
<li>CPU</li>
<li>Intel e1000 PCI等网卡</li>
<li>基于PCI IDE接口的硬盘、光驱</li>
<li>软驱</li>
<li>串口</li>
<li>AC97兼容声卡以及其它声卡</li>
<li>PS/2 键盘鼠标</li>
<li>VGA显卡</li>
</ol>
<p>等多种外围设备。QEMU最多支持255 CPU的SMP。</p>
<p>当作为<span style="background-color: #c0c0c0;">仿真器（Emulator，模拟器）</span>使用时，它可以在真实机器（例如你的x86_64台式机）上模拟一台机器 + 操作系统 + 程序，而这台模拟的机器的体系结构（例如ARM板）与宿主机器不同。</p>
<p>而作为<span style="background-color: #c0c0c0;">虚拟器（Virtualizer）</span>使用时，它可以在宿主机器上直接执行客户机（虚拟机）的代码，因而虚拟机的性能接近于宿主机。QEMU通过下列方式之一来支持虚拟化：</p>
<ol>
<li>在XEN监管程序（Hypervisor）之上执行</li>
<li>在支持KVM的Linux操作系统下运行。使用KVM时QEMU可以虚拟化x86、PowerPC、S390客户机</li>
</ol>
<p>注意Emulator和Virtualizer的区别，最重要的一点是客户机的代码是直接执行（意味着宿主和客户机体系结构兼容），还是模拟执行，后者的效率要低得多。很多同学喜欢在PC上玩街机游戏，这也是通过模拟器（例如Winkawaks）实现的。</p>
<p>和Vmware、VirtualBox 之类的虚拟机管理软件不同，QEMU不提供图形化的管理界面。你可以使用第三方的图形前端，例如qtemu，但是命令行的丰富性让QEMU更适合在服务器上使用。</p>
<div class="blog_h3"><span class="graybg">QEMU与KVM</span></div>
<p>单纯靠QEMU来模拟一系列硬件，因为存在指令转译，性能一般很差。而KVM可以基于Intel-VT、AMD-V实现硬件辅助的CPU虚拟化，客户机指令直接在真实CPU上运行。因此结合KVM可以很好的提高QEMU的<span style="background-color: #c0c0c0;">CPU性能</span>。另一方面KVM仅仅提供CPU的虚拟化，它无法构建一台完整的虚拟机。因此QEMU和KVM整合的需求就很明显了。</p>
<p>qemu-kvm项目就是来整合QEMU和KVM的，此项目在1.3.0版本开始正式合并到QEMU项目的master上。qemu-kvm（qemu-system-***）利用ioctl调用/dev/kvm，将有关CPU的部分交由KVM去做。</p>
<p>如果KVM内核模块存在、且CPU支持，你可以通过下面的选项启用KVM支持：</p>
<pre class="crayon-plain-tag"># 启用基于KVM的虚拟化加速
-enable-kvm</pre>
<div class="blog_h3"><span class="graybg">周围硬件的性能</span></div>
<p>CPU的性能问题解决了，但是QEMU模拟的其它硬件也存在同样的低效问题，于是Virtio被引入了。</p>
<p>Virtio是libvirt的一部分，它是一个关于网络、磁盘等设备的虚拟化标准，在此标准中客户机的设备驱动知道自己运行在虚拟化环境中，因而这些驱动可以和Hypervisor进行直接交互，获得<span style="background-color: #c0c0c0;">接近于Native驱动的性能</span>。</p>
<p>较新版本的Linux发行版都已经把Virtio编译进内核，因而客户机可以直接使用Virtio驱动。</p>
<div class="blog_h2"><span class="graybg">与其它虚拟化技术的比较</span></div>
<div class="blog_h3"><span class="graybg">Xen</span></div>
<p>KVM和XEN都是基于Hypervisor的虚拟化技术。它们的区别包括：</p>
<ol>
<li>Xen<span style="background-color: #c0c0c0;">是裸机监管程序</span>，而KVM某种程度上把Linux内核变成了Hosted Hypervisor</li>
<li>Xen的整体性能高于KVM，但是I/O略差</li>
<li>KVM要求CPU必须支持虚拟化技术，但Xen则没有此限制。这个限制在当前的硬件条件下，基本不是问题</li>
<li>KVM的优势是它对Linux内核的整合程度，KVM本质上就是一个内核模块，因此你可以很容易的升级内核</li>
</ol>
<div class="blog_h3"><span class="graybg">VirtualBox</span></div>
<p>VirtualBox是标准的2类Hypervisor，KVM与它的区别包括：</p>
<ol>
<li>VirtualBox它与商用软件Vmware Workstation一样，都以图形界面为主，适合个人用户。但是在商用环境下，大部分虚拟机都是Headless的（不需要图形界面），此时VirtualBox的GUI则是劣势，GUI浪费了资源</li>
<li>VirtualBox支持大量的宿主操作系统，例如Windows、Linux、Mac OS X。而KVM显然仅支持Linux</li>
<li>一般情况下，轻量级的KVM的性能要比VirtualBox好的多</li>
</ol>
<div class="blog_h3"><span class="graybg">关于LXC</span></div>
<p>LXC即Linux容器（Containers），这是一种<span style="background-color: #c0c0c0;">操作系统层（传统虚拟机是硬件层）</span>虚拟化技术，要由liblxc库及其多语言绑定、一系列控制容器的工具组成。LXC将整个应用，包括：软件本身代码、所需库、支撑软件，打包为一个“容器”。通过Linux内核的特性，可以实现容器与系统之间的隔离，这些特性包括：</p>
<ol>
<li>内核命名空间（IPC、uts、mount、pid、network、user）</li>
<li>Apparmor（限制每个应用程序访问的资源）和SELinux配置</li>
<li>Seccomp（提供应用程序沙盒机制）策略</li>
<li>Chroots（通过pivot_root调用）</li>
<li>cgroups，即控制组（control groups），用来限制、控制、分离进程组的资源（CPU、内存、磁盘等），此特性最初的名字就叫“进程容器”</li>
</ol>
<p>通过LXC，你可以创建尽可能接近标准Linux的环境，同时<span style="background-color: #c0c0c0;">不需要独立的内核</span>。</p>
<p>基于LXC的虚拟化技术和KVM相比，区别如下：</p>
<ol>
<li>LXC的优势在于轻量化和高性能，但是隔离性不高</li>
<li>LXC支持任何体系结构，例如x86、ARM、PowerPC等</li>
</ol>
<div class="blog_h3"><span class="graybg">LXD</span></div>
<p>LXD基于LXC，可以认为是一个Container的Hypervisor，LXD一般创建<span style="background-color: #c0c0c0;">自包含的操作系统用户空间</span>。也就是说，LXD容器内运行的是一个操作系统，虽然存在用户空间和内核空间隔离，但是这个操作系统和宿主系统共享一个内核。</p>
<p>有测试数据表明：LXD相比KVM可以减少50+%的延迟；LXD启动实例的速度比KVM块90+%</p>
<div class="blog_h3"><span class="graybg">Docker</span></div>
<p>近年来非常流行的容器软件，与LXD最大的不同是，Docker打包<span style="background-color: #c0c0c0;">应用程序+自包含的文件系统</span>，而不是操作系统用户空间。每个Docker容器，仅仅包含一个应用程序。曾经Docker也是基于lxc技术的，但是现在它使用自己的库ibcontainer。</p>
<p>Docker中的文件系统、网络都是抽象的，而LXD直接使用宿主机的文件系统、网络，LXD可以方便的设置IP地址。</p>
<p>Docker比起LXD更加轻量，可以实现更高的部署密度。</p>
<div class="blog_h1"><span class="graybg">安装和配置</span></div>
<div class="blog_h2"><span class="graybg">使用随系统自带的KVM</span></div>
<p>大部分的Linux发行版已经内置了KVM内核模块以及用户空间工具，使用这些内置组件是最容易、推荐的方式：</p>
<ol>
<li>KVM内核模块现在是Linux内核的一部分，除非你使用的是精简过的内核</li>
<li>用户空间组件，软件包名称一般是qemu-kvm或者kvm，例如：
<ol>
<li>Ubuntu下可以执行<pre class="crayon-plain-tag">apt-get install qemu-kvm</pre> 安装</li>
<li>CentOS下可以执行<pre class="crayon-plain-tag">yum install kvm</pre>安装</li>
</ol>
</li>
<li>客户机驱动：Linux客户机的驱动包含在内核中；<a href="http://www.linux-kvm.org/page/WindowsGuestDrivers/Download_Drivers">Windows客户机的驱动</a>需要下载</li>
</ol>
<div class="blog_h2"><span class="graybg">手工构建KVM</span></div>
<p>安装QEMU的依赖包：</p>
<pre class="crayon-plain-tag">sudo apt-get install gcc libsdl1.2-dev zlib1g-dev libasound2-dev linux-kernel-headers pkg-config libgnutls-dev libpci-dev</pre>
<p>下载用户空间组件： </p>
<ol>
<li>QEMU 1.3或者更老版本的，在<a href="https://sourceforge.net/projects/kvm/files/">Sourceforge</a>下载</li>
<li>新版本，在<a href="http://wiki.qemu.org/Download">QEMU官网</a>下载</li>
</ol>
<p>注意：2.6.29以上版本的内核，可以和任何版本的qemu-kvm搭配使用。</p>
<div class="blog_h3"><span class="graybg">构建和安装用户空间组件</span></div>
<pre class="crayon-plain-tag">tar xzf qemu-kvm-release.tar.gz
cd qemu-kvm-release
./configure --prefix=/usr/local/kvm
make
sudo make install</pre>
<div class="blog_h3"><span class="graybg">构建和安装KVM内核模块</span></div>
<p>如果你使用旧版本内核，或者内核精简了KVM，则需要此步骤：</p>
<pre class="crayon-plain-tag">tar xjf kvm-kmod-release.tar.bz2
cd kvm-kmod-release 
./configure
make 
sudo make install</pre>
<div class="blog_h3"><span class="graybg">启用内核模块</span></div>
<pre class="crayon-plain-tag"># 对于Intel CPU
sudo /sbin/modprobe kvm-intel
# 对于AMD CPU
sudo /sbin/modprobe kvm-amd</pre>
<div class="blog_h2"><span class="graybg">构建QEMU</span></div>
<p>从3.0.0开始QEMU的版本大跃进，每年major版本增加1，目前已经是5.x版本。</p>
<div class="blog_h3"><span class="graybg">下载和构建</span></div>
<pre class="crayon-plain-tag">wget https://download.qemu.org/qemu-5.1.0.tar.xz
tar xvJf qemu-5.1.0.tar.xz 
cd qemu-5.1.0/
./configure
make</pre>
<div class="blog_h3"><span class="graybg">配置项说明</span></div>
<pre class="crayon-plain-tag">./configure --help

# 方括号中是默认值

Standard options:
  --prefix=PREFIX          install in PREFIX [/usr/local]
  --interp-prefix=PREFIX   where to find shared libraries, etc.
                           use %M for cpu name [/usr/gnemul/qemu-%M]
  # 目标列表，默认所有
  # xxx-softmmu 生成qemu-system-xxx，用于运行xxx架构下的虚拟机
  # xxx-linux-user 生成qemu-xxx，用于模拟运行xxx架构下的应用程序，可以配合binfmt_misc和Docker联用
  --target-list=LIST       set target list (default: build everything)
                           Available targets: aarch64-softmmu alpha-softmmu 
                           arm-softmmu avr-softmmu cris-softmmu hppa-softmmu 
                           i386-softmmu lm32-softmmu m68k-softmmu 
                           microblazeel-softmmu microblaze-softmmu 
                           mips64el-softmmu mips64-softmmu mipsel-softmmu 
                           mips-softmmu moxie-softmmu nios2-softmmu 
                           or1k-softmmu ppc64-softmmu ppc-softmmu 
                           riscv32-softmmu riscv64-softmmu rx-softmmu 
                           s390x-softmmu sh4eb-softmmu sh4-softmmu 
                           sparc64-softmmu sparc-softmmu tricore-softmmu 
                           unicore32-softmmu x86_64-softmmu xtensaeb-softmmu 
                           xtensa-softmmu aarch64_be-linux-user 
                           aarch64-linux-user alpha-linux-user armeb-linux-user 
                           arm-linux-user cris-linux-user hppa-linux-user 
                           i386-linux-user m68k-linux-user 
                           microblazeel-linux-user microblaze-linux-user 
                           mips64el-linux-user mips64-linux-user 
                           mipsel-linux-user mips-linux-user 
                           mipsn32el-linux-user mipsn32-linux-user 
                           nios2-linux-user or1k-linux-user 
                           ppc64abi32-linux-user ppc64le-linux-user 
                           ppc64-linux-user ppc-linux-user riscv32-linux-user 
                           riscv64-linux-user s390x-linux-user sh4eb-linux-user 
                           sh4-linux-user sparc32plus-linux-user 
                           sparc64-linux-user sparc-linux-user 
                           tilegx-linux-user x86_64-linux-user 
                           xtensaeb-linux-user xtensa-linux-user
  --target-list-exclude=LIST exclude a set of targets from the default target-list

Advanced options (experts only):
  --cross-prefix=PREFIX    use PREFIX for compile tools []
  --cc=CC                  use C compiler CC [cc]
  --iasl=IASL              use ACPI compiler IASL [iasl]
  --host-cc=CC             use C compiler CC [cc] for code run at
                           build time
  --cxx=CXX                use C++ compiler CXX [c++]
  --objcc=OBJCC            use Objective-C compiler OBJCC [cc]
  --extra-cflags=CFLAGS    append extra C compiler flags QEMU_CFLAGS
  --extra-cxxflags=CXXFLAGS append extra C++ compiler flags QEMU_CXXFLAGS
  --extra-ldflags=LDFLAGS  append extra linker flags LDFLAGS
  --cross-cc-ARCH=CC       use compiler when building ARCH guest test cases
  --cross-cc-flags-ARCH=   use compiler flags when building ARCH guest tests
  --make=MAKE              use specified make [make]
  --install=INSTALL        use specified install [install]
  --python=PYTHON          use specified python [/usr/bin/python3]
  --sphinx-build=SPHINX    use specified sphinx-build []
  --smbd=SMBD              use specified smbd [/usr/sbin/smbd]
  --with-git=GIT           use specified git [git]
  --static                 enable static build [no]
  --mandir=PATH            install man pages in PATH
  --datadir=PATH           install firmware in PATH/qemu
  --docdir=PATH            install documentation in PATH/qemu
  --bindir=PATH            install binaries in PATH
  --libdir=PATH            install libraries in PATH
  --libexecdir=PATH        install helper binaries in PATH
  --sysconfdir=PATH        install config in PATH/qemu
  --localstatedir=PATH     install local state in PATH (set at runtime on win32)
  --firmwarepath=PATH      search PATH for firmware files
  --efi-aarch64=PATH       PATH of efi file to use for aarch64 VMs.
  --with-confsuffix=SUFFIX suffix for QEMU data inside datadir/libdir/sysconfdir [/qemu]
  --with-pkgversion=VERS   use specified string as sub-version of the package
  --enable-debug           enable common debug build options
  --enable-sanitizers      enable default sanitizers
  --enable-tsan            enable thread sanitizer
  --disable-strip          disable stripping binaries
  --disable-werror         disable compilation abort on warning
  --disable-stack-protector disable compiler-provided stack protection
  --audio-drv-list=LIST    set audio drivers list:
                           Available drivers: oss alsa sdl pa
  --block-drv-whitelist=L  Same as --block-drv-rw-whitelist=L
  --block-drv-rw-whitelist=L
                           set block driver read-write whitelist
                           (affects only QEMU, not qemu-img)
  --block-drv-ro-whitelist=L
                           set block driver read-only whitelist
                           (affects only QEMU, not qemu-img)
  --enable-trace-backends=B Set trace backend
                           Available backends: dtrace ftrace log simple syslog ust
  --with-trace-file=NAME   Full PATH,NAME of file to store traces
                           Default:trace-&lt;pid&gt;
  --disable-slirp          disable SLIRP userspace network connectivity
  --enable-tcg-interpreter enable TCG with bytecode interpreter (TCI)
  --enable-malloc-trim     enable libc malloc_trim() for memory optimization
  --oss-lib                path to OSS library
  # 为指定CPU构建
  --cpu=CPU                Build for host CPU [x86_64]
  --with-coroutine=BACKEND coroutine backend. Supported options:
                           ucontext, sigaltstack, windows
  --enable-gcov            enable test coverage analysis with gcov
  --gcov=GCOV              use specified gcov [gcov]
  --disable-blobs          disable installing provided firmware blobs
  --with-vss-sdk=SDK-path  enable Windows VSS support in QEMU Guest Agent
  --with-win-sdk=SDK-path  path to Windows Platform SDK (to build VSS .tlb)
  --tls-priority           default TLS protocol/cipher priority string
  --enable-gprof           QEMU profiling with gprof
  --enable-profiler        profiler support
  --enable-debug-stack-usage
                           track the maximum stack usage of stacks created by qemu_alloc_stack
  --enable-plugins
                           enable plugins via shared library loading
  --disable-containers     don't use containers for cross-building
  --gdb=GDB-path           gdb to use for gdbstub tests [/usr/bin/gdb]

Optional features, enabled with --enable-FEATURE and
disabled with --disable-FEATURE, default is enabled if available:

  system          all system emulation targets
  user            supported user emulation targets
  linux-user      all linux usermode emulation targets
  bsd-user        all BSD usermode emulation targets
  docs            build documentation
  guest-agent     build the QEMU Guest Agent
  guest-agent-msi build guest agent Windows MSI installation package
  pie             Position Independent Executables
  modules         modules support (non-Windows)
  module-upgrades try to load modules from alternate paths for upgrades
  debug-tcg       TCG debugging (default is disabled)
  debug-info      debugging information
  sparse          sparse checker
  safe-stack      SafeStack Stack Smash Protection. Depends on
                  clang/llvm &gt;= 3.7 and requires coroutine backend ucontext.

  gnutls          GNUTLS cryptography support
  nettle          nettle cryptography support
  gcrypt          libgcrypt cryptography support
  auth-pam        PAM access control
  sdl             SDL UI
  sdl-image       SDL Image support for icons
  gtk             gtk UI
  vte             vte support for the gtk UI
  curses          curses UI
  iconv           font glyph conversion support
  vnc             VNC UI support
  vnc-sasl        SASL encryption for VNC server
  vnc-jpeg        JPEG lossy compression for VNC server
  vnc-png         PNG compression for VNC server
  cocoa           Cocoa UI (Mac OS X only)
  virtfs          VirtFS
  mpath           Multipath persistent reservation passthrough
  xen             xen backend driver support
  xen-pci-passthrough    PCI passthrough support for Xen
  brlapi          BrlAPI (Braile)
  curl            curl connectivity
  membarrier      membarrier system call (for Linux 4.14+ or Windows)
  fdt             fdt device tree
  kvm             KVM acceleration support
  hax             HAX acceleration support
  hvf             Hypervisor.framework acceleration support
  whpx            Windows Hypervisor Platform acceleration support
  rdma            Enable RDMA-based migration
  pvrdma          Enable PVRDMA support
  vde             support for vde network
  netmap          support for netmap network
  linux-aio       Linux AIO support
  linux-io-uring  Linux io_uring support
  cap-ng          libcap-ng support
  attr            attr and xattr support
  vhost-net       vhost-net kernel acceleration support
  vhost-vsock     virtio sockets device support
  vhost-scsi      vhost-scsi kernel target support
  vhost-crypto    vhost-user-crypto backend support
  vhost-kernel    vhost kernel backend support
  vhost-user      vhost-user backend support
  vhost-vdpa      vhost-vdpa kernel backend support
  spice           spice
  rbd             rados block device (rbd)
  libiscsi        iscsi support
  libnfs          nfs support
  smartcard       smartcard support (libcacard)
  libusb          libusb (for usb passthrough)
  live-block-migration   Block migration in the main migration stream
  usb-redir       usb network redirection support
  lzo             support of lzo compression library
  snappy          support of snappy compression library
  bzip2           support of bzip2 compression library
                  (for reading bzip2-compressed dmg images)
  lzfse           support of lzfse compression library
                  (for reading lzfse-compressed dmg images)
  zstd            support for zstd compression library
                  (for migration compression and qcow2 cluster compression)
  seccomp         seccomp support
  coroutine-pool  coroutine freelist (better performance)
  glusterfs       GlusterFS backend
  tpm             TPM support
  libssh          ssh block device support
  numa            libnuma support
  libxml2         for Parallels image format
  tcmalloc        tcmalloc support
  jemalloc        jemalloc support
  avx2            AVX2 optimization support
  avx512f         AVX512F optimization support
  replication     replication support
  opengl          opengl support
  virglrenderer   virgl rendering support
  xfsctl          xfsctl support
  qom-cast-debug  cast debugging support
  tools           build qemu-io, qemu-nbd and qemu-img tools
  bochs           bochs image format support
  cloop           cloop image format support
  dmg             dmg image format support
  qcow1           qcow v1 image format support
  vdi             vdi image format support
  vvfat           vvfat image format support
  qed             qed image format support
  parallels       parallels image format support
  sheepdog        sheepdog block driver support
  crypto-afalg    Linux AF_ALG crypto backend driver
  capstone        capstone disassembler support
  debug-mutex     mutex debugging support
  libpmem         libpmem support
  xkbcommon       xkbcommon support
  rng-none        dummy RNG, avoid using /dev/(u)random and getrandom()
  libdaxctl       libdaxctl support</pre>
<div class="blog_h2"><span class="graybg">第一台虚拟机</span></div>
<p>要创建虚拟机，首先要创建一个虚拟磁盘，然后从光驱启动此虚拟机：</p>
<pre class="crayon-plain-tag">mkdir -p ~/Vmware/KVM

# 以qcow2格式创建一个16G的虚拟磁盘，注意，默认不会预先分配空间
qemu-img create -f qcow2 ~/Vmware/KVM/centos7-base.img 16G

# 指定光盘镜像，从光驱启动虚拟机
# -hda 第一块硬盘的镜像
# -cdrom 光驱的镜像，你可以把宿主的/dev/cdrom传入，这样可以使用物理光驱
# -boot 指定启动顺序，d表示第一个光驱，c表示第一块硬盘
# -m 为虚拟机分配多少内存，默认单位M，默认128M
qemu-system-x86_64 -enable-kvm -hda ~/Vmware/KVM/centos7-base.img  -boot d -m 512
                   -cdrom ~/Software/OS/CentOS-7-x86_64-Minimal-1503-01.iso</pre>
<p>上述命令执行完毕之后，会弹出一个窗口，该窗口相当于虚拟机的显示器。你可以在其中完成操作系统的安装。安装完毕后，执行下面的命令，即可启动虚拟机：</p>
<pre class="crayon-plain-tag">qemu-system-x86_64 -enable-kvm -hda ~/Vmware/KVM/centos7-base.img -m 512</pre>
<p>后续几个章节，我们深入学习客户机硬件的定制，以满足不同应用场景的需要、提高客户机的性能。</p>
<div class="blog_h1"><span class="graybg">配置CPU</span></div>
<p>使用选项<pre class="crayon-plain-tag">-cpu</pre> 选项可以选择客户机使用的CPU，执行<pre class="crayon-plain-tag">qemu-system-x86_64 -cpu help</pre> 可以列出QEMU支持的CPU名称、可用的CPUID标记。</p>
<p>你可以这样配置一个CPU：</p>
<pre class="crayon-plain-tag">-cpu SandyBridge,+erms,+smep,+fsgsbase,+pdpe1gb,+rdrand,+f16c,+osxsave,+dca,+pcid,+pdcm,\
     +xtpr,+tm2,+est,+smx,+vmx,+ds_cpl,+monitor,+dtes64,+pbe,+tm,+ht,+ss,+acpi,+ds,+vme</pre>
<p>+表示启用CPU特性，如果要禁用CPU特性，可以使用<pre class="crayon-plain-tag">-</pre> 。</p>
<div class="blog_h2"><span class="graybg">SMP配置</span></div>
<p>所谓对称多处理（Symmetrical Multi-Processing） ，是指在一个计算机上汇集了一组处理器，各处理器<span style="background-color: #c0c0c0;">共享内存子系统以及总线结构</span>。在PC机上QEMU最多可以模拟255个CPU。</p>
<p>你可以这样配置SMP：<pre class="crayon-plain-tag">-smp 1,sockets=1,cores=1,threads=1</pre> 。这个配置表示主板上有一个CPU插槽、1个CPU、每个CPU具有1核心、每个核心具有1个硬件线程（超线程）。</p>
<div class="blog_h1"><span class="graybg">配置磁盘</span></div>
<p>你可以在宿主机上创建一个磁盘镜像文件，然后供客户机使用。客户机磁盘I/O都将针对此文件。镜像文件可以有几种格式。</p>
<div class="blog_h2"><span class="graybg">Raw镜像</span></div>
<p>这种镜像的特点是<span style="background-color: #c0c0c0;">格式简单，性能较好</span>。</p>
<p>你的文件系统（例如Ext3）必须支持稀疏文件（sparse file），才能避免不必要的磁盘空间占用。稀疏文件是一种高效使用磁盘空间的技术，当文件大小很大，而其绝大部分块都是空白（未使用）的时，可以基于文件元数据来表示那些空白的块（而不是真实的硬盘空间）。</p>
<p>创建Raw镜像：</p>
<pre class="crayon-plain-tag">qemu-img create -f raw hda.img 1G

# 查看镜像信息
qemu-img info hda.img 
# image: hda.img
# file format: raw
# virtual size: 1.0G (1073741824 bytes)
# disk size: 0</pre>
<p>你也可以使用dd命令产生Raw镜像，例如：</p>
<pre class="crayon-plain-tag"># 产生非稀疏文件：块大小1MB，写入1024个块，虚拟大小1G，实际大小1G
dd if=/dev/zero of=hda.img bs=1024k count=1024
# 产生稀疏文件：块大小1MB，写入0个块，虚拟大小1G，实际大小0
dd if=/dev/zero of=hda.img bs=1024k count=0 seek=1024</pre>
<div class="blog_h2"><span class="graybg">qcow2镜像</span></div>
<p>qcow2镜像的动态增长的，即使文件系统不支持稀疏文件，它也会尽可能的小。qcow2支持Copy-on-write、镜像、压缩、加密。 </p>
<p>正是由于qcow2支持Copy-on-write，我们才可以使用<span style="background-color: #c0c0c0;">backing file</span>——用一个镜像保存针对另外一个镜像的改变，而后面那个镜像不需要被改动。这是<span style="background-color: #c0c0c0;">多虚拟机公用一个Base镜像，以及Snapshot的基础</span>。</p>
<div class="blog_h3"><span class="graybg">qcow2原理</span></div>
<p>qcow2镜像文件的结构如下图所示：</p>
<p><img class="aligncenter size-full wp-image-13261" src="https://blog.gmem.cc/wp-content/uploads/2015/08/qcow2fmt.png" alt="qcow2fmt" width="640" height="178" /></p>
<p>qcow2镜像文件由一个头、几张表、数据簇组成。所有数据都存放在数据簇（Data Clusters）中，每个数据簇是512字节的扇区。为了方便管理这些数据簇，qcow2建立了两级表：L1、L2。其中L1表的条目指向L2表，而L2表的条目指向数据簇。</p>
<p>要定位数据，需要3个偏移量构成的数组：</p>
<ol>
<li>通过位于Header中的L1表指针  +offset[0]，得到L2表的指针</li>
<li>L2表指针 + offset[1]，得到数据簇指针</li>
<li>数据簇指针 + offset[2]，得到目标数据的指针</li>
</ol>
<div class="blog_h3"><span class="graybg">创建qcow2镜像</span></div>
<p>你可以这样创建一个qcow2镜像：</p>
<pre class="crayon-plain-tag">qemu-img create -f qcow2 hda-back.img 16G</pre>
<div class="blog_h3"><span class="graybg">转为backing file</span></div>
<p>然后，在未来某个时刻把它作为backing file使用：</p>
<pre class="crayon-plain-tag">qemu-img create -f qcow2 -o backing_file=hda-back.img hda.img</pre>
<div class="blog_h3"><span class="graybg">压缩、加密和扩展</span></div>
<p>镜像hda.img在一开始是空白的，所有数据都是从hda-back.img中获取，一旦发生写入操作，hda.img就开始有数据而hda-base.img保持不变。</p>
<p>使用下面的命令可以压缩一个qcow2镜像：</p>
<pre class="crayon-plain-tag">qemu-img convert -c -f qcow2 -O qcow2 hda.img hda.compressed.img</pre>
<p>使用下面的命令可以为一个qcow2镜像设加密：</p>
<pre class="crayon-plain-tag">qemu-img convert -o encryption -f qcow2 -O qcow2  hda.img hda.encrypted.img
# 提示输入密码</pre>
<p>使用压缩镜像启动虚拟机时，必须在Monitor中输入密码才可以。</p>
<p>使用下面的命令，可以扩展一个qcow2镜像的大小：</p>
<pre class="crayon-plain-tag">qemu-img resize hda.img +10G</pre>
<p>注意：扩大得到的空间，不会被分区或者格式化。 </p>
<div class="blog_h3"><span class="graybg">清理空白</span></div>
<p>要移除镜像中的spare space，直接qcow2-to-qcow2转换即可，压缩（-c）可选：</p>
<pre class="crayon-plain-tag">qemu-img convert -O qcow2 source.qcow2 shrunk.qcow2</pre>
<div class="blog_h3"><span class="graybg">变基rebase</span></div>
<p>rebase操作用于改变一个镜像的backing镜像：</p>
<pre class="crayon-plain-tag"># -u 表示unsafe模式，在此模式下，仅仅改变backing文件的路径，不对文件内容进行检查
#    用于backing文件移动的情况
# -p 表示safe模式，在此模式下，执行真正的rebase操作。backing文件的内容可能和之前
#    不同，qemu-img会小心处理，确保VM可见的内容不变。为达成这一点，新旧backing
#    文件的差异，会合并到被改变镜像中
                    # 格式
qemu-img rebase -u  -f qcow2   
  # 新的backing文件位置                                      新backing文件格式
  -b /home/alex/Vmware/libvirt/images/sdd/xenial-base.qcow2 -F qcow2 
  # 被处理镜像文件
  /home/alex/Vmware/libvirt/images/sdd/xenial-100.qcow2</pre>
<div class="blog_h2"><span class="graybg">格式转换</span></div>
<p>你可以把一个镜像的格式在Raw和qcow2之间进行转换：</p>
<pre class="crayon-plain-tag"># 把Raw格式的hda.img转换为qcow2格式的hda.qcow2
qemu-img convert -f raw -O qcow2 hda.img hda.qcow2</pre>
<div class="blog_h2"><span class="graybg">使用快照</span></div>
<p>快照（Snapshot）是Copy-on-write的一种应用。QEMU支持两种快照：</p>
<ol>
<li>内部快照（internal snapshot）：在qcow2镜像的snapshot table中维护的快照，所有快照都存放在一个镜像文件中</li>
<li>外部快照（external snapshot）：与Backing file很类似，在外部文件中创建新的镜像，原先的镜像只读</li>
</ol>
<div class="blog_h3"><span class="graybg">内部快照</span></div>
<p>内部快照的原理是：</p>
<ol>
<li>创建一个Snapshot后，在Snapshot Table中新增一项，复制L1 Table</li>
<li>当L2 Table或者Data Cluster发生改变，则把改变前的数据复制一份（Copy-on-write），由新创建的Snapshot的L1 Table来管理</li>
<li>L2 Table或者Data Cluster的变化，直接写到原始位置</li>
<li>要删除快照，很简单，直接把Snapshot Table对应项、以及复制的L1-L2-DS删除即可</li>
<li>要加载快照，则需要依据L1-L2-DS信息，将其合并到镜像的L1-L2-DS信息中</li>
</ol>
<p>可以使用Monitor来创建、加载、删除内部快照：</p>
<pre class="crayon-plain-tag"># 保存一个内部快照
(qemu) savevm snapshot-1

qemu-img info hda.img
# 输出如下：
#Snapshot list:
#ID        TAG                 VM SIZE                DATE       VM CLOCK
#1         snapshot-1             112M 2016-09-07 18:05:48   00:00:21.536
#Format specific information:
#    compat: 1.1
#    lazy refcounts: false

# 加载内部快照
(qemu) loadvm snapshot-1

# 删除内部快照
(qemu) delvm snapshot-1</pre>
<div class="blog_h3"><span class="graybg">外部快照</span></div>
<p>外部快照与内部快照相反：内部快照是原数据变化，外部快照则是新文件变化。</p>
<p>可以使用Monitor来管理外部快照：</p>
<pre class="crayon-plain-tag">snapshot_blkdev ide0-hd0 snapshot.img qcow2</pre>
<div class="blog_h2"><span class="graybg">配置客户机磁盘</span></div>
<p>有了磁盘镜像文件后，你需要为qemu-system-*指定参数，给客户机增加磁盘。有几种不同的配置方式：</p>
<pre class="crayon-plain-tag"># 最简单的方式
-hda hda.img 

# 使用-drive配置块设备，可以指定if为virtio来提升性能
-drive file=hda.img,index=0,media=disk,if=virtio

# 使用-device配置通用设备
-drive file=hda.img,if=none,id=virtio-disk0,format=qcow2,cache=none 
# 可以指定virtio-blk-pci来提升性能
-device virtio-blk-pci,scsi=off,bus=pci.0,addr=0x4,drive=virtio-disk0,bootindex=1</pre>
<div class="blog_h1"><span class="graybg">配置网络</span></div>
<p>QEMU中的网络，包含两部分的内容：</p>
<ol>
<li>客户机使用的虚拟网络设备</li>
<li>和上述虚拟设备通信的<span style="background-color: #c0c0c0;">网络后端</span>，这些后端负责把虚拟设备的数据包发到宿主机的网络中</li>
</ol>
<p>要创建一个网络后端，可以指定如下选项：</p>
<pre class="crayon-plain-tag"># TYPE为后端类型：user、tap、bridge、socket、vde等
# id为一个标识符，将虚拟网络设备和网络后端关联在一起
# 如果客户机有多个虚拟网络设备，则每一个都需要自己的网络后端
-netdev TYPE,id=NAME,...</pre>
<p>QEME支持多种网络后端。</p>
<div class="blog_h2"><span class="graybg">USER后端</span></div>
<p>如果没有指定网络选项，QEMU默认会模拟单张Intel e1000 PCI网卡，该网卡基于user后端（SLIRP）连接到宿主机：</p>
<pre class="crayon-plain-tag"># 不指定网络
qemu 
# 等价配置。自0.12开始废弃的配置方式 -net nic相当于-device DEVNAME；-net TYPE相当于-netdev TYPE
qemu -hda disk.img -net nic -net user
# 等价配置。-netdev指定网络后端，-device指定虚拟网络设备，后者通过netdev字段引用后端的ID
qemu -netdev user,id=network0 -device e1000,netdev=network0</pre>
<p>在客户机看来：</p>
<ol>
<li>本身的IP地址被分配为 10.0.2.15+</li>
<li>分配IP的虚拟DHCP为 10.0.2.2</li>
<li>虚拟DNS服务器为 10.0.2.3</li>
<li>虚拟Samba服务器为 10.0.2.4，客户机可以通过此服务器访问宿主机的文件系统</li>
</ol>
<p>用户模式网络可以很方便的访问网络资源。但是它有很多限制：</p>
<ol>
<li>默认的，它运作方式类似于防火墙，且不允许任何入站流量。这个限制可以通过端口重定向解决</li>
<li>仅仅支持TCP、UDP协议，对于ICMP则不支持</li>
<li>性能比较差</li>
</ol>
<p>为了支持入站请求，你可以使用端口重定向（Redirecting ports）——把针对宿主机某个端口的请求转发给客户机的某个端口。映射后，客户机可以对外提供SSH、HTTP等服务：</p>
<pre class="crayon-plain-tag"># 把宿主机的7080端口重定向到客户机的80端口；把宿主机的7022端口重定向到客户机的22端口
qemu-system-x86_64 -redir tcp:7080::80 -redir tcp:7022::22 -hda ~/Vmware/KVM/centos7-base.img -m 512 

# 从宿主机SSH到客户机
ssh root@127.0.0.1 -p 7022</pre>
<p>你可以不使用默认的10.0.2网段：</p>
<pre class="crayon-plain-tag">-netdev user,id=network0,net=192.168.5.0/24,dhcpstart=192.168.5.9 </pre>
<div class="blog_h3"><span class="graybg">客户机OS配置</span></div>
<p>依据客户机安装的操作系统，可能需要进行一些配置，才能正常使用网络。以CentOS 7 Minimal + 用户模式网络为例，需要修改以下配置文件：</p>
<pre class="crayon-plain-tag">NETWORKING=yes
# 如果不使用IPV6
NETWORKING_IPV6=no</pre><br />
<pre class="crayon-plain-tag"># 如果不使用IPV6
IPV6INIT=no
# 开机启动此网卡，默认不启动
ONBOOT=yes</pre>
<p>网关、DNS不需要设置。修改完这些配置文件后，重启客户机网络：<pre class="crayon-plain-tag">/etc/init.d/network restart</pre>  。然后执行<pre class="crayon-plain-tag">yum update</pre> 测试一下能否正常联网（不要使用ping测试）</p>
<div class="blog_h2"><span class="graybg">TAP后端</span></div>
<p>QEMU的TAP后端利用宿主机的TAP设备，为客户机提供完整的桥接网络支持，如果外部需要使用标准端口连接到客户机， 或者多个客户机需要相互通信，可以使用该方式。 TAP后端还具有以下优势：</p>
<ol>
<li>非常好的性能</li>
<li>可以配置以支持各种网络拓扑</li>
</ol>
<p>但是，你需要在宿主机上进行网络拓扑的配置，而且各种系统的配置不同。</p>
<p>使用TAP后端前，你需要确认你的宿主机的内核支持TAP网络接口：<pre class="crayon-plain-tag">/dev/net/tun</pre> 文件存在则说明支持。如果没有这样的文件，可以尝试手工创建：</p>
<pre class="crayon-plain-tag">sudo mkdir /dev/net
sudo mknod /dev/net/tun c 10 200
sudo /sbin/modprobe tun</pre>
<div class="blog_h3"><span class="graybg">基于TAP的私有桥接网络</span></div>
<p>如果你想创建几个客户机之间的私有网络，可以使用该方式。未参与进来的客户机、真实网络无法看到此网络。</p>
<p>如果你不是root，则你需要<pre class="crayon-plain-tag">/dev/kvm</pre> 的读写权限。</p>
<p>首先，添加一个以太网桥设备：</p>
<pre class="crayon-plain-tag">sudo ip link add br0 type bridge
# 也可以使用：sudo brctl addbr br0添加网桥
# 要删除网桥，执行： ip link delete br0
# 注意：网桥会在重启后消失

# 启用此网桥
sudo ip link set br0 up

# 为网桥分配IP地址
sudo ip addr add 10.0.0.1 dev br0

# 在宿主机添加一条直接路由，便于它能和客户机通信
sudo ip route add 10.0.0.0/8 dev br0</pre>
<p>创建一个创建TAP设备并桥接到网桥的脚本：</p>
<pre class="crayon-plain-tag">#!/bin/sh

switch=br0

if [ -n "$1" ];then
        # tunctl -u `whoami` -t $1
        # 添加一个tap设备，在我的机器上不需要，原因见下面
        # ip tuntap add $1 mode tap user `whoami`
        # 不知道从什么时候开始，QEMU会在执行此脚本之前就创建好tap设备，因此会报下面的错误
        # ioctl(TUNSETIFF): Device or resource busy
        # 启动tap设备
        ip link set $1 up
        # brctl addif $switch $1
        # 将网桥和tap设备进行桥接
        ip link set $1 master $switch
        exit 0
else
        echo "Error: no interface specified"
        exit 1
fi</pre>
<p>创建一个生成随机MAC地址的脚本：</p>
<pre class="crayon-plain-tag">#!/bin/bash
# generate a random mac address for the qemu nic
printf 'DE:AD:BE:EF:%02X:%02X\n' $((RANDOM%256)) $((RANDOM%256))</pre>
<p>启动客户机的脚本：</p>
<pre class="crayon-plain-tag">#!/bin/bash
# $1 base name of virtual disk
# $2 memory size
# $3 tap device id

mac=`/usr/bin/qemu-genmac`
src=/usr/bin/qemu-ifup
sudo qemu-system-x86_64 -enable-kvm -device e1000,netdev=$3,mac=$mac -netdev tap,id=$3,script=$src,downscript=no \
                        -hda ~/Vmware/KVM/$1.img -m $2</pre>
<p>为上面的脚本文件添加可执行权限：</p>
<pre class="crayon-plain-tag">sudo chmod +x /usr/bin/qemu-ifup-br0
sudo chmod +x /usr/bin/qemu-genmac
sudo chmod +x /usr/bin/qemu-start-br0</pre>
<p>执行下面的命令，启动一台客户机（或者更多虚拟机，但是命令中的tap0要更换为不同的名字）：</p>
<pre class="crayon-plain-tag">/usr/bin/qemu-start centos7-base 512 tap0</pre>
<p>修改客户机的IP地址，使用10.0.0.0/8网段：</p>
<pre class="crayon-plain-tag">TYPE=Ethernet
BOOTPROTO=static
DEFROUTE=yes
PEERDNS=yes
PEERROUTES=yes
IPV4_FAILURE_FATAL=no
IPV6INIT=no
NAME=ens3
UUID=d9f47102-b177-4a27-ae98-86f6939d6680
DEVICE=ens3
ONBOOT=yes
IPADDR=10.0.0.10
PREFIX=8
GATEWAY=10.0.0.1</pre>
<p>好了，你现在可以互相ping客户机和宿主机，应该可以正常连通了。 </p>
<div class="blog_h3"><span class="graybg">私有桥接下访问互联网</span></div>
<p>上一节介绍的这种基于TAP的私有桥接网络，可以让客户机、宿主机相互连通，但是客户机无法访问互联网。</p>
<p>要解决此问题，你可以选择以下方法之一：</p>
<ol>
<li>让客户机通过宿主机暴露的HTTP/SOCKS代理上网</li>
<li>配置宿主机的路由规则，设置好源地址转换即可：<br />
<pre class="crayon-plain-tag"># 宿主机需要启用IP转发功能，这样它就可以像路由器那样中转IP封包了
sudo sysctl -w net.ipv4.ip_forward=1 
# 对客户机网段进行源地址转换
sudo iptables -t nat -A POSTROUTING  -s 10.0.0.0/255.0.0.0 ! -d 10.0.0.0/255.0.0.0 -j MASQUERADE </pre>
</li>
</ol>
<div class="blog_h3"><span class="graybg">公共桥接网络</span></div>
<p>此方式和私有桥接网络类似，主要区别是，除了TAP设备桥接到网桥之外，以太网卡（例如eth0）也桥接到网桥（例如br1）。</p>
<p>你可以通过发行版的配置文件来配置网桥：</p>
<pre class="crayon-plain-tag"># 注意网络管理器组件的影响
# 去掉 auto eth0，改为：
auto br1

# 配置br1
iface br1 inet dhcp
    bridge_ports    eth0
    bridge_stp      off
    bridge_maxwait  0
    bridge_fd       0
    # 这里附加上原来属于eth0的配置</pre>
<p>或者基于脚本来配置： </p>
<pre class="crayon-plain-tag">sudo ip link add br1 type bridge
sudo ip link set br1 up
sudo ip link set eth0 master br1 

# DHCP
sudo killall dhclient &amp;&amp; sudo ip addr flush dev eth0
sudo dhclient br1</pre>
<p>无论用哪种方式，都应该注意到eth0的IP地址需要<span style="background-color: #c0c0c0;">转移给br1</span>，这样才能确保网络正常运作——br1必须在链路层接收到相关ARP请求，并决定是否需要转发给客户机，eth0没有这种转发能力。</p>
<p>如果eth0所在网络是基于DHCP的，那么客户机配置为DHCP后，会自动获取公共IP地址。否则，需要手工设置客户机的IP地址。 </p>
<div class="blog_h3"><span class="graybg">基于TAP的桥接的简化配置</span></div>
<p>现在QEMU支持自动桥接TAP设备到宿主机的一个网桥，因此你不再需要编写脚本，修改网络后端为bridge即可：</p>
<pre class="crayon-plain-tag">-netdev bridge,id=tap0,br=br0</pre>
<p>注意，使用上述选项时，QEMU需要读取配置文件/etc/qemu/bridge.conf，你只需在此文件中添加一行代码：<pre class="crayon-plain-tag">allow br0</pre> </p>
<p>你可以编写如下脚本自动创建网桥、配置iptables规则。示例：</p>
<pre class="crayon-plain-tag"># Create private bridge link for QEMU
/sbin/ip link add br0 type bridge
/sbin/ip link set br0 up
/sbin/ip addr add 10.0.0.1 dev br0
/sbin/ip route add 10.0.0.0/8 dev br0
# NAT for 10.0.0.0/8
/sbin/iptables -t nat -A POSTROUTING  -s 10.0.0.0/255.0.0.0 ! -d 10.0.0.0/255.0.0.0  -j MASQUERADE


# Create public bridge link for QEMU
/sbin/ip link add br1 type bridge
/sbin/ip link set br1 up
/sbin/ip link set eth0 master br1 
/usr/bin/killall dhclient &amp;&amp; /sbin/ip addr flush dev eth0
/sbin/dhclient br1</pre>
<div class="blog_h3"><span class="graybg">macvtap直连</span></div>
<p>建议和libvirt一起使用macvtap。 </p>
<div class="blog_h3"><span class="graybg">基于libvirt的桥接</span></div>
<p>在使用libvirt时，客户机（Domain）的网络接口配置可以简化为：</p>
<pre class="crayon-plain-tag">&lt;interface type='bridge'&gt;
    &lt;mac address='DE:AD:BE:EF:F1:00'/&gt;
    &lt;source bridge='br0'/&gt;
    &lt;target dev='tap0'/&gt;
    &lt;model type='virtio'/&gt;
&lt;/interface&gt;</pre>
<div class="blog_h3"><span class="graybg">基于libvirt虚拟局域网的桥接</span></div>
<p>可以使用libvrit的虚拟局域网，这样宿主机上<span style="background-color: #c0c0c0;">不会为客户机创建专门的tap设备，那些手工编写的脚本也全都不需要了</span>。虚拟网络配置示例：</p>
<pre class="crayon-plain-tag">&lt;network&gt;
  &lt;name&gt;default&lt;/name&gt;
  &lt;uuid&gt;9bae4de8-ca58-48c5-ba58-109aebf8b954&lt;/uuid&gt;
  &lt;forward mode='nat'&gt;
  &lt;/forward&gt;
  &lt;bridge name='virbr0' stp='on' delay='0'/&gt;
  &lt;ip address='10.0.0.1' netmask='255.0.0.0'&gt;
    &lt;dhcp&gt;
      &lt;range start='10.0.0.100' end='10.0.0.200'/&gt;
    &lt;/dhcp&gt;
  &lt;/ip&gt;
&lt;/network&gt;</pre>
<p>客户机（Domain）的网络接口配置示例：</p>
<pre class="crayon-plain-tag">&lt;interface type='network'&gt;
    &lt;mac address='DE:AD:BE:EF:F1:00'/&gt;
    &lt;source network='default'/&gt;
    &lt;model type='virtio'/&gt;
&lt;/interface&gt;</pre>
<p>另外，libvirt的虚拟网络提供了DHCP功能，因此客户机的IP地址不需要静态设置。</p>
<div class="blog_h1"><span class="graybg">其它配置</span></div>
<div class="blog_h2"><span class="graybg">SMBIOS</span></div>
<p>SMBIOS即DMI表，存放了X86系统硬件信息，这个表依据DMI type分为数十个段，type0是BIOS、type1是系统信息、type2是主板信息……</p>
<p>QEMU支持模拟这些信息，例如：</p>
<pre class="crayon-plain-tag"># 设置客户机的type1信息
-smbios type=1,manufacturer=OpenStack Foundation,product=OpenStack Nova,version=2011,serial=8059dfb4,uuid=1f8ee7f308</pre>
<div class="blog_h2"><span class="graybg">内存</span></div>
<p>要设置客户机的内存容量，可以使用<pre class="crayon-plain-tag">-m</pre> ，默认单位MB。</p>
<p>客户机没必要占据着空闲的内存不用，因此我们一般启用内存实际大小的动态调整功能，例如：</p>
<pre class="crayon-plain-tag">-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x5</pre>
<div class="blog_h1"><span class="graybg">半虚拟化和virtio</span></div>
<p>在本文的第一章，谈到周围硬件性能的时候，我们提及了virtio——它是规定了<span style="background-color: #c0c0c0;">虚拟设备的前端驱动与宿主机硬件的后端驱动之间通信接口的标准</span>，并且知道目前的很多Linux发行版已经把virtio驱动编译进内核了。前面的章节我们也使用了很多virtio驱动，包括磁盘、网络、内存相关的。</p>
<p><span style="background-color: #c0c0c0;">基于virtio驱动的虚拟设备，我们成为“半虚拟化设备”</span>，因为这些设备驱动知道自己工作在虚拟化模式下。为客户机配置半虚拟化设备，可以提高内存、硬盘、网络方面的性能，由其对于网络，性能提升很明显。</p>
<p>除了virtio，Vmware Tools也属于半虚拟化驱动，QEMU客户机也可以利用Vmware Tools（例如-vga指定vmware）。virtio驱动的具体实现包括：virtio-blk、virtio-net、virtio-pci、virtio-balloon、virtio-console等。</p>
<p>半/全虚拟化的区别如下：</p>
<ol>
<li>在全虚拟化状态下，Guest OS不知道自己是虚拟机，于是像发送普通的IO一样发送数据，<span style="background-color: #c0c0c0;">被Hypervisor拦截</span>，转发给真正的硬件。</li>
<li>在半虚拟化状态下，Guest OS知道自己是虚拟机（需安装半虚拟化驱动），所以数据直接发送给半虚拟化设备，经过特殊处理，发送给真正的硬件</li>
</ol>
<div class="blog_h2"><span class="graybg">virtio-balloon</span></div>
<p>这是一个特殊的半虚拟化设备，它能够动态（不需要重启客户机）的调整客户机的内存大小。如果你指定了-m参数，则不能调整的比-m更大。使用选项<pre class="crayon-plain-tag">-balloon virtio</pre> 可以添加Ballooning设备。</p>
<div class="blog_h2"><span class="graybg">virtio-blk</span></div>
<p>要基于半虚拟化来访问磁盘，可以使用选项：<pre class="crayon-plain-tag">-drive file=vda.qcow2,if=virtio</pre> ，使用virto_blk驱动的硬盘，在客户机里对应的设备文件是/dev/vda（而IDE硬盘是/dev/hda、基于SATA的硬盘则显示为/dev/sda） 。</p>
<p>可以使用驱动<span style="background-color: #c0c0c0;">virtio-blk-data-plane进一步提高性能</span>（I/O性能较virtio-blk能提高10-20%），此驱动自QEMU 1.4开始引入。与传统的virtio-blk不同的是，virtio-blk-data-plane为每个块设备独立分配一个线程用于I/O处理，此线程不需要和QEMU执行线程同步、竞争锁。此驱动基于宿主机的原生AIO响应客户机的请求。启用此驱动的选项示例：</p>
<pre class="crayon-plain-tag">-drive if=none,id=drive0,cache=none,aio=native,format=raw,file=vda.img
-device virtio-blk-pci,drive=drive0,scsi=off,x-data-plane=on</pre>
<p>但是，启用virtio-blk-data-plane后，存储迁移（storage migration）、热拔插、I/O限流（throttling） 等功能无法使用。而且该驱动仅支持Raw格式的磁盘。</p>
<div class="blog_h2"><span class="graybg">virtio-net</span></div>
<p>要基于半虚拟化来访问网络，可以使用选项：<pre class="crayon-plain-tag">-device virtio-net-pci,netdev=network0</pre> 。你应当总是考虑启用<span style="background-color: #c0c0c0;">半虚拟化网卡，因为性能会有很大的提升</span>。</p>
<p>宿主机网卡的某些特性可能会影响virtio的性能，例如：</p>
<ol>
<li>TSO（TCP Segmentation Offload）：通过网络设备进行TCP段的分割，从而来提高网络性能</li>
<li>GSO（Generic Segmentation Offload）：类似，用TCPv6、UDP等传输层协议</li>
</ol>
<p>你可以开关这些特性来测试对客户机网络性能的影响。要检查宿主机网卡是否支持、开启这些特性，可以执行命令：<pre class="crayon-plain-tag">ethtool -k eth0</pre> 。</p>
<div class="blog_h1"><span class="graybg">配置NBD</span></div>
<p>网络块设备（Network Block Device）是一种把虚拟块设备通过TCP/IP暴露出去，供远程共享访问的技术。</p>
<div class="blog_h2"><span class="graybg">暴露NBD</span></div>
<div class="blog_h3"><span class="graybg">通过套接字暴露</span></div>
<p>你可以通过UNIX套接字来暴露：</p>
<pre class="crayon-plain-tag">qemu-nbd -t -k /home/alex/Vmware/KVM/.images/fedora-108 fedora-108/hda.img</pre>
<p> 也可以通过普通套接字来暴露：</p>
<pre class="crayon-plain-tag">qemu-nbd  -p 1025 fedora-108/hda.img </pre>
<div class="blog_h3"><span class="graybg">挂载到NBD设备</span></div>
<p>甚至是把镜像直接挂载到宿主机的NBD设备中：</p>
<pre class="crayon-plain-tag"># 在宿主机上启用NBD内核模块，最多16个分区：
sudo modprobe nbd max_part=16
# 查看NBD设备文件
ls /dev/nbd*
# 输出/dev/nbd0 ... /dev/nbd15

# 挂载
sudo qemu-nbd -c /dev/nbd0 fedora-108/hda.img

# 查看nbd0的分区情况
sudo fdisk -l /dev/nbd0
#  Device Boot Start End Blocks Id System
#  /dev/nbd0p1 * 2048 1026047 512000 83 Linux
#  /dev/nbd0p2 1026048 33554431 16264192 8e Linux LVM</pre>
<div class="blog_h2"><span class="graybg">使用NBD</span></div>
<p>客户机可以直接使用NBD作为磁盘：</p>
<pre class="crayon-plain-tag"># 使用UNIX套接字：
qemu-system-x86_64 -hda nbd:unix:/home/alex/Vmware/KVM/.images/fedora-108
# 使用普通套接字
qemu-system-x86_64 -hda nbd:10.0.0.1:1025</pre>
<div class="blog_h1"><span class="graybg">迁移</span></div>
<p>QEMU支持<span style="background-color: #c0c0c0;">离线或者在线</span>的迁移，你可以在Monitor中使用迁移命令。当迁移完毕后，虚拟机会在目标主机上继续运行。</p>
<p>AMD和Intel宿主机之间可以随意的迁移虚拟机，64位虚拟机只能迁移到64位宿主机上，32位则没有限制。某些老旧的Intel CPU不支持NX（禁止执行比特位），这种CPU处于启用NX的宿主机群中，会导致问题，你需要禁止客户机的NX：<pre class="crayon-plain-tag">-cpu qemu64,-nx</pre> 。</p>
<p>QEMU的迁移功能具有以下特性：</p>
<ol>
<li>极短暂的客户机停机时间</li>
<li>如果迁移成功，则客户机在目标主机上运行；如果迁移失败，则客户机继续在源主机上运行</li>
<li>几乎对硬件没有依赖</li>
</ol>
<div class="blog_h2"><span class="graybg">使用共享存储</span></div>
<p>使用共享存储时，QEMU迁移会很便利，因为不牵涉到磁盘映像的移动。共享存储包括：NFS、NBD、SAN等。 我们以NBD为例说明：</p>
<ol>
<li>启动供源、目的虚拟机共享的NBD服务：<pre class="crayon-plain-tag">qemu-nbd -p 1025 --share=2 fedora-108/hda.img</pre> </li>
<li>确保源、目的虚拟机的配置，它们要具有相同的网络环境</li>
<li>启动源虚拟机：<br />
<pre class="crayon-plain-tag">sudo qemu-system-x86_64 -netdev bridge,id=tap0,br=br0 -device virtio-net-pci,netdev=tap0,mac=DE:AD:BE:EF:F1:08 
                        -hda nbd:10.0.0.1:1025 -monitor stdio -enable-kvm  </pre>
</li>
<li>源虚拟机运作一段时间后，其宿主机的硬件需要维护，因此准备迁移。在另外一台宿主机上启动目的虚拟机，并监听migration端口：<br />
<pre class="crayon-plain-tag"># qemu选项同源虚拟机，附加：
-incoming tcp:0:4444</pre></p>
<p> 注意，这个监<span style="background-color: #c0c0c0;">听端口是开在宿主机上的</span>。实际上，以-incoming启动目的虚拟机后，虚拟机是<span style="background-color: #c0c0c0;">处于Stopped状态</span>的</p>
</li>
<li>登录到源虚拟机，确认它与目的虚拟机的宿主机之间的网络是畅通的</li>
<li>在源虚拟机的Monitor中，发起迁移命令：<pre class="crayon-plain-tag">(qemu) migrate -d tcp:10.0.0.1:4444</pre> </li>
<li>在迁移过程中，可以通过<pre class="crayon-plain-tag">info migrate</pre> 查看迁移状态，完毕后会显示Migration status: completed，并列出迁移消耗的时间、停机时间</li>
<li>迁移完成后，源虚拟机变为Stopped，而目的虚拟机开始运行，获得<span style="background-color: #c0c0c0;">源虚拟机的全部瞬时状态</span></li>
</ol>
<div class="blog_h2"><span class="graybg">不使用共享存储</span></div>
<p>这种情况下，源虚拟机的磁盘镜像需要拷贝到目标宿主机中。因而需要更长的时间、更多的网络带宽消耗。步骤如下：</p>
<ol>
<li>查看源虚拟机的磁盘镜像信息：<pre class="crayon-plain-tag">qemu-img info fedora-108/hda.img</pre> </li>
<li>在目的机器上创建与之大小（Virtual size）一致的空磁盘镜像：<pre class="crayon-plain-tag">qemu-img create -f qcow2 fedora-108-m/hda.img 16G</pre> </li>
<li>使用与共享存储一样的步骤进行迁移，只是源、目的虚拟机使用各自的磁盘镜像</li>
</ol>
<div class="blog_h1"><span class="graybg">监控与调试</span></div>
<p>前面的章节我们已经多次使用QEMU的监控功能，通过使用QEMU的HMI（Monitor）可以在(qemu)提示符下进行各种监控操作，包括查看虚拟机信息、动态添加设备、执行迁移等等。在《QEMU命令与快捷键》一章我们会详细的讲解HMI命令，本章主要介绍监控相关的QEMU配置</p>
<div class="blog_h2"><span class="graybg">基于HMI（Monitor）监控</span></div>
<p>你可以通过多种方式使用Monitor：</p>
<ol>
<li>默认的，可以在QEMU的虚拟机窗口中，按Ctrl + Alt + 2切换到Monitor</li>
<li>可以使用<pre class="crayon-plain-tag">-monitor stdio</pre> ，让Monitor重定向到启动虚拟机的Terminal</li>
<li>可以启动一个TCP监听<pre class="crayon-plain-tag">-monitor tcp::4444,server,nowait</pre> ，这样你可以<pre class="crayon-plain-tag">telnet hostip:4444</pre> 访问Monitor</li>
<li>可以通过字符设备：<pre class="crayon-plain-tag">-chardev stdio,id=x -monitor chardev=x</pre> 访问Monitor</li>
</ol>
<div class="blog_h2"><span class="graybg">基于QMP监控</span></div>
<p>非交互式监控时，QEMU监控协议（QEMU Monitor Protocol）是更好的选择，这是一个基于JSON格式的协议。要启用QMP，你可以：</p>
<ol>
<li>基于stdio：<pre class="crayon-plain-tag">-qmp stdio</pre> </li>
<li>基于TCP：<pre class="crayon-plain-tag">-qmp tcp:localhost:4444,server</pre> </li>
<li>基于UNIX Socket：<pre class="crayon-plain-tag">-qmp unix:./qmp-sock,server</pre> </li>
</ol>
<div class="blog_h1"><span class="graybg">最佳实践</span></div>
<p>以下列出一些应用基于QEMU/KVM的虚拟化方案时的最佳实践：</p>
<ol>
<li>使用半虚拟化驱动virtio
<ol>
<li>性能好：延迟低、吞吐量高</li>
<li>纯虚拟设备的劣势：需要高吞吐能力的设备在硬件方面会有特殊的实现，这些纯虚拟设备是没法利用的</li>
<li>网络、块设备、内存，都可以使用virtio</li>
<li>兼容性较差</li>
</ol>
</li>
<li>虚拟机最好直接使用块设备做存储
<ol>
<li>性能好、无需管理宿主机的文件系统、无需管理稀疏文件</li>
<li>I/O 缓存以4K为边界</li>
<li>如果没有条件使用块设备，只能使用镜像文件</li>
<li>宿主机最好使用ext3文件系统，ext4的barrier会影响性能</li>
<li>Raw格式镜像的性能优于qcow2</li>
<li>选择正确的缓存策略，缓存模式推荐none，I/O调度器推荐Deadline I/O scheduler</li>
</ol>
</li>
<li>CPU配置
<ol>
<li>每个客户机相当于一个进程，而每个客户机的虚拟CPU相当于一个线程。因此超配CPU是可行的</li>
<li>CPU超配可能带来额外的上下文切换，影响性能</li>
<li>要保证客户机获得足够的时间片，可以利用cgroup的cpu.cfs_period_us、cpu.cfs_quota_us来干预CFS调度器的行为</li>
<li>Pin CPU：可以将虚拟CPU Pin到一个物理CPU，或者一组共享缓存的物理CPU，便于缓存共享。缺点是Pin导致其它空闲CPU可能得不到利用</li>
</ol>
</li>
<li>内存配置
<ol>
<li>使用内核特性KSM（Kernel Same Page Merging），KSM通过扫描将相同的内存区域设置为共享，并且Copy-on-write。共享内存节约可以内存空间，但是内存扫描同时影响性能</li>
<li>尽量避免使用swap，可以设置/proc/sys/vm/swappiness=0</li>
</ol>
</li>
<li>网络配置
<ol>
<li>使用tap类型的网络后端</li>
<li>启用PCI passthough可以提高性能，但是影响迁移</li>
</ol>
</li>
</ol>
<div class="blog_h1"><span class="graybg">QEMU命令与快捷键</span></div>
<div class="blog_h2"><span class="graybg">HMI</span></div>
<p>HMI即 Human Monitor Interface，是QEMU在运行客户机时提供的一个console（下面我们称此console为Monitor），它让你可以和运行中的虚拟机进行交互，你可以获得内存Dump、列出虚拟设备树、获取屏幕截图等操作。</p>
<div class="blog_h3"><span class="graybg">访问HMI</span></div>
<p>默认情况下QEMU使用SDL来显示客户机的视频输出，此所谓图形模式。如果启用-nographic选项则会禁用图形模式。</p>
<p>在图形模式下，你可以使用以下方式之一访问HMI：</p>
<ol>
<li>在客户机的虚拟控制台（客户机弹窗）访问HMI，按Ctrl + Alt + 2可以切换到Monitor，在其中你可以调用HMI命令</li>
<li>指定-monitor stdio，则启动虚拟机的Terminal变为Monitor</li>
</ol>
<p>在基于-nographic的非图形模式下，Monitor、虚拟串口都被重定向到stdio，你可以Ctrl + a c来切换。你可以同时把虚拟串口配置为<span style="background-color: #c0c0c0;">系统控制台</span>，这样你可以通过单个窗口完成客户机登录、HMI操作</p>
<div class="blog_h3"><span class="graybg">HMI命令</span></div>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 25%; text-align: center;">命令</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>info</td>
<td>
<p>显示客户机的相关信息，示例：</p>
<p><pre class="crayon-plain-tag">info kvm           # 显示KVM支持情况
info pci           # 显示PCI信息
info qtree         # 显示QEMU系统总线树
info network       # 显示网络设备信息
info block         # 显示块设备信息
info blockstatus   # 显示块设备读写统计信息
info snapshots     # 显示快照信息
info migrate       # 显示迁移状态</pre>
</td>
</tr>
<tr>
<td>memsave</td>
<td>Dump客户机内存到宿主机的文件</td>
</tr>
<tr>
<td>screendump</td>
<td>屏幕截图</td>
</tr>
<tr>
<td>sendkey</td>
<td>键盘控制，示例：<pre class="crayon-plain-tag">sendkey ctrl-alt-f1</pre> </td>
</tr>
<tr>
<td>quit</td>
<td>退出客户机addr=0xM.0xN</td>
</tr>
<tr>
<td>system_powerdown</td>
<td>关闭虚拟机电源</td>
</tr>
<tr>
<td>system_reset</td>
<td>重启虚拟机</td>
</tr>
<tr>
<td>system_wakeup</td>
<td>唤醒休眠中的虚拟机</td>
</tr>
<tr>
<td>savevm</td>
<td>
<p>保存一个虚拟机快照，示例：<pre class="crayon-plain-tag">savevm blankos</pre> </p>
</td>
</tr>
<tr>
<td>loadvm</td>
<td>从快照加载虚拟机，示例：<pre class="crayon-plain-tag">loadvm blankos</pre> </td>
</tr>
<tr>
<td>delvm</td>
<td>删除一个虚拟机快照</td>
</tr>
<tr>
<td>snapshot_blkdev_internal</td>
<td>创建一个内部的块设备（主要指硬盘）快照，示例：<br />
<pre class="crayon-plain-tag">info block
# ide0-hd0: /home/alex/Vmware/KVM/fedora-108/hda.img (qcow2)
snapshot_blkdev_internal  ide0-hd0 blankos</pre>
</td>
</tr>
<tr>
<td>snapshot_delete_blkdev_internal</td>
<td>删除一个内部的块设备快照</td>
</tr>
<tr>
<td>snapshot_blkdev</td>
<td>创建一个外部的块设备快照，示例：<br />
<pre class="crayon-plain-tag">snapshot_blkdev ide0-hd0 blankos.img</pre></p>
<p>如果指定了文件参数，则此文件成为新的root镜像</p>
</td>
</tr>
<tr>
<td>migrate</td>
<td>执行虚拟机迁移</td>
</tr>
<tr>
<td>migrate_cancel</td>
<td>取消虚拟机迁移</td>
</tr>
<tr>
<td>migrate_set_speed</td>
<td>限制迁移带宽消耗</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">qemu-system-x86_64</span></div>
<p>该命令即QEMU模拟器，使用它可以指定硬件设备，并从虚拟磁盘镜像启动一台客户机。</p>
<div class="blog_h3"><span class="graybg">常用选项</span></div>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 18%; text-align: center;">选项</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>-machine</td>
<td>
<p>指定虚拟的客户机的类型及其属性，选项格式：</p>
<pre class="crayon-plain-tag">-machine [type=]name[,prop=value[,...]]</pre>
<p>其中type为机器类型，可以调用<pre class="crayon-plain-tag">qemu-system-x86_64 -machine help</pre> 获得完整机器类型列表，每种机器都标注了主板芯片组的类型</p>
<p>你可以指定多个可选的属性：</p>
<p>accel=accels1[:accels2[:...]]    启用加速器，可用的包括kvm、xen、tcg，加速器可以指定多个，后面的是备选</p>
</td>
</tr>
<tr>
<td>-cpu</td>
<td>指定虚拟的CPU类型，可以通过<pre class="crayon-plain-tag">qemu-system-x86_64 -cpu help</pre> 查看可用CPU列表</td>
</tr>
<tr>
<td>-smp</td>
<td>
<p>虚拟一个SMP系统，在PC机最多虚拟255CPU，选项格式：</p>
<pre class="crayon-plain-tag">-smp [cpus=]n[,cores=cores][,threads=threads][,sockets=sockets][,maxcpus=maxcpus]</pre>
<p>你可以指定多个属性：<br />cpus    处理器个数<br />cores     每个处理器的核心数<br />threads    每个核心的线程数<br />sockets    CPU插槽数<br />maxcpus    最大可热拔插的CPU数</p>
</td>
</tr>
<tr>
<td>-global</td>
<td>设置驱动属性为指定的值，选项格式：<pre class="crayon-plain-tag">-global driver.prop=value</pre> ，示例：<br />
<pre class="crayon-plain-tag">-global ide-drive.physical_block_size=4096</pre></p>
<p>使用该选项，你可以改变由机型（machine）预定义的设备属性，如果要添加设备，请使用-device </p>
</td>
</tr>
<tr>
<td>-boot</td>
<td>设置客户机的磁盘启动顺序，选项格式：<br />
<pre class="crayon-plain-tag">-boot [order=drives][,once=drives][,menu=on|off][,splash=sp_name]
      [,splash-time=sp_time][,reboot-timeout=rb_timeout][,strict=on|off]</pre></p>
<p>drives值指定为磁盘符号构成的字符串，这些符号的形式取决于客户机的架构，在X86 PC上：<br />        a  软盘1；b 软盘2；c 第一个硬盘；d 第一个光驱；n-p 四个以太网卡</p>
<p>你可以指定多个属性：<br />order    磁盘启动顺序<br />once      仅生效一次的启动顺序<br />menu    交互式启动，显示菜单<br />splash    显示一个开机画面，图片必须是JPEG/BMP格式且分辨率支持SVGA模式<br />splash-time  开机画面显示的时间<br />reboot-timeout    如果启动失败，多少ms后重启</p>
</td>
</tr>
<tr>
<td>-m</td>
<td>设置客户机的内存大小，单位MB</td>
</tr>
<tr>
<td>-mem-path</td>
<td>从一个临时文件来创建客户机内存<br />可以同时指定  -mem-prealloc  来预分配内存</td>
</tr>
<tr>
<td>-soundhw</td>
<td>启用声卡，选项格式：<br />
<pre class="crayon-plain-tag">-soundhw card1[,card2,...]
# 或者
-soundhw all

# 显示可用硬件列表
qemu-system-x86_64 -soundhw help
# 示例
qemu-system-x86_64 -soundhw ac97 disk.img</pre>
</td>
</tr>
<tr>
<td>-balloon</td>
<td>
<p>控制KVM的Automatic Ballooning功能。virtio balloon设备可以减少KVM客户机的内存大小，该特性用于主持客户机内存的over-committing——宿主机只有2G内存的情况下，创建两台2G内存的客户机。只要客户机实际使用的内存不到2G，那么多余的部分就可以返还给宿主机</p>
<p>选项格式：</p>
<pre class="crayon-plain-tag"># 禁用balloon设备
-balloon none
# 启用balloon设备，可以指定一个PCI地址
-balloon virtio[,addr=addr]</pre>
</td>
</tr>
<tr>
<td>-device driver</td>
<td>添加一个设备驱动，并指定驱动属性，可用的属性取决于具体的驱动，选项格式： <br />
<pre class="crayon-plain-tag">-device driver[,prop[=value][,...]]</pre>
<p>要获得可用驱动、属性列表，可以：<pre class="crayon-plain-tag">-device help</pre>  和<pre class="crayon-plain-tag">-device driver,help</pre> </p>
<p>对于连接到PCI总线的设备，可以指定：<br /><pre class="crayon-plain-tag">bus=pci.x</pre> ，此设备连接到第x+1个总线上<br /><pre class="crayon-plain-tag">addr=0xM.0xN</pre>  此设备是总线上的第M个设备，这里使用设备的第N个Function，如果只有一个Function，则.0xN省略</p>
<p>该选项可以用于<span style="background-color: #c0c0c0;">添加客户机的多种虚拟设备并进行细节上的配置（代替部分选项例如-boolean、-net nic）</span>，例如：</p>
<p><pre class="crayon-plain-tag"># 添加e1000网卡，以network0为后端
-device e1000,netdev=network0
# 添加基于Virtio的网卡，等价于 -net nic,model=virtio ...
-device virtio-net-pci,netdev=network0,id=net0,mac=DE:AD:BE:EF:F1:08,bus=pci.0,addr=0x3

# 启用virtio balloon设备（收回客户机空闲内存），等价于-balloon ...
-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x5

# 添加基于Virtio的硬盘（前端）
-device virtio-blk-pci,scsi=off,bus=pci.0,addr=0x4,drive=drive-virtio-disk0</pre>
</td>
</tr>
<tr>
<td>-name</td>
<td>设置客户机的名称</td>
</tr>
<tr>
<td>-uuid</td>
<td>设置客户机的UUID</td>
</tr>
<tr>
<td style="text-align: center;" colspan="2"><strong><em>块设备选项</em></strong></td>
</tr>
<tr>
<td>-fd*</td>
<td>-fda、-fdb指定0、1软盘的镜像文件，你可以使用主机的软盘，例如/dev/fd0</td>
</tr>
<tr>
<td>-hd*</td>
<td>-hda、-hdb、-hdc、-hdd指定0、1、2、3硬盘的镜像</td>
</tr>
<tr>
<td>-cdrom</td>
<td>指定光驱镜像，你可以可以使用主机的光驱，例如/dev/cdrom</td>
</tr>
<tr>
<td>-drive</td>
<td>
<p>定义一个新的磁盘驱动器，选项格式：<pre class="crayon-plain-tag">-drive option[,option[,option[,...]]]</pre> </p>
<p>你可以使用以下子选项：<br />file    在宿主机上的磁盘镜像文件<br />if    指定磁盘接口类型，可用的类型包括ide, scsi, sd, mtd, floppy, pflash, virtio<br />bus=bus,unit=unit 依据bus号、unit号来定义此磁盘驱动器被连接在何处<br />index    依据针对磁盘接口类型的序号来定义此磁盘驱动器被连接在何处<br />media    媒体类型，可选disk或cdrom<br />snapshot    是否启用快照功能，on/off<br />cache    在访问磁盘上的数据块时如何使用宿主机的缓存功能，none/writeback/unsafe/directsync/writethrough<br />aio    threads基于pthread的异步I/O；native基于Linux原生的异步I/O<br />format     指定磁盘格式，而不是去检测file，可选值raw/qcow2<br />serial    分配给磁盘的串号<br />addr    如果if=virtio，该选项指定磁盘控制器的PIC地址<br />readonly    设置磁盘为只读</p>
<p>cache的默认取值是writeback，该选项意味着，一旦数据进入宿主机的页缓存，QEMU就向客户机报告“写入已完成”。如果客户机程序正确的flush磁盘缓存，此选项是安全的。否则，宿主机断电将会导致客户机数据损坏。<br />为了防止上述数据丢失，你可以考虑设置cache为writethrough，这样只有宿主机把缓存刷出到磁盘后QEMU才会报告“写入已完成”，但是这样设置会导致严重的性能问题<br />directsync：类似于writethrough，只是绕过宿主机的页缓存<br />unsafe：宿主机可以缓存客户机的所有disk IO，客户机的sync请求被忽略</p>
<p>代替-cdrom的配置：<pre class="crayon-plain-tag">-drive file=file,index=2,media=cdrom</pre> <br />代替 -hda, -hdb, -hdc, -hdd的配置：</p>
<p><pre class="crayon-plain-tag">-drive file=file,index=0,media=disk
-drive file=file,index=1,media=disk
-drive file=file,index=2,media=disk
-drive file=file,index=3,media=disk</pre>
</td>
</tr>
<tr>
<td>-mtdblock</td>
<td>指定主板内置闪存的镜像文件</td>
</tr>
<tr>
<td>-sd</td>
<td>指定SD卡镜像</td>
</tr>
<tr>
<td>-snapshot</td>
<td>写入到临时文件，而非硬盘镜像文件，这样，原始硬盘文件就不会被改变</td>
</tr>
<tr>
<td style="text-align: center;" colspan="2"><strong><em>显示选项</em></strong></td>
</tr>
<tr>
<td>-display</td>
<td>选择一个显示类型：<br />curses    基于curses输出，如果客户机的图形设备支持文本模式，QEMU基于curses/ncurses接口显示输出；如果客户机图形设备运行在图形模式或者不支持文本模式，则不显示<br />none    不显示视频输出，客户机仍然可以模拟一个图形卡但是其输出不会显示给用户。该选项与-nographic不同，后者具有附加效果——改变串口、并口数据的目的地<br />gtk    在一个GTK窗口中显示视频输出<br />vnc    启动一个VNC服务器</td>
</tr>
<tr>
<td>-vnc</td>
<td>配置VNC，例如<pre class="crayon-plain-tag">-vnc 0.0.0.0:10</pre> </td>
</tr>
<tr>
<td>-nographic</td>
<td>
<p>通常情况下，QEMU基于SDL库来显示VGA输出，如果使用该选项，则QEMU成为完全的命令行程序</p>
<p>尽管如此，QEMU还是把虚拟的串口重定向到控制台、与monitor复用。你可以基于串口控制台来调试虚拟机的内核</p>
<p>你可以在console和monitor之间切换</p>
</td>
</tr>
<tr>
<td>-curses</td>
<td>
<p>在文本模式下，直接在当前Terminal显示VGA输出，在图形模式下则什么都不显示，仅提示“1024 x 768 Graphic mode”之类的信息</p>
</td>
</tr>
<tr>
<td>-no-frame</td>
<td>不显示虚拟机窗口的外框、标题栏</td>
</tr>
<tr>
<td>-vga</td>
<td>指定虚拟的VGA显卡类型，可用的包括：<br />cirrus    默认，Cirrus Logic GD5446显卡，对于Windows，所有Win95之后的系统都能够识别此卡<br />std    支持Bochs VBE扩展的标准VGA扩展，如果客户机OS支持VESA 2.0 VBE扩展（例如XP）并且你希望使用高分辨率<br />vmware    Vmware的SVGA-II兼容显卡<br />qxl    使用spice协议时推荐此卡<br />none    禁用VGA显卡</td>
</tr>
<tr>
<td>-full-screen</td>
<td>以全屏模式启动</td>
</tr>
<tr>
<td>-g</td>
<td>设置初始的分辨率和颜色深度，选项格式：<pre class="crayon-plain-tag">-g widthxheight[xdepth]</pre> </td>
</tr>
<tr>
<td style="text-align: center;" colspan="2"><strong><em>网络选项</em></strong></td>
</tr>
<tr>
<td>-net nic</td>
<td>
<p>创建一个新的网卡并把它连接到一个VLAN，选项格式：</p>
<pre class="crayon-plain-tag">-net nic[,vlan=n][,macaddr=mac][,model=type] [,name=name][,addr=addr][,vectors=v] </pre>
<p>你可以指定以下属性：<br />vlan=n    虚拟局域网编号，默认0。虚拟局域网仅在<span style="background-color: #c0c0c0;">同一个QEMU进程内部有效</span>，加入到同一个vlan的网卡可以接收到彼此的数据<br />macaddr 改变此网卡的MAC地址<br />addr    设置设备地址（仅PCI卡）<br />name    设备名称，在monitor命令中使用<br />model    网卡型号，在PC上默认创建e1000。你可以使用<pre class="crayon-plain-tag">-net nic,model=help</pre> 列出所有可用的网卡型号</p>
</td>
</tr>
<tr>
<td>-netdev user<br />-net user</td>
<td>添加一个User网络后端，选项格式：<br />
<pre class="crayon-plain-tag">-netdev user,id=id[,option][,option][,...]
-net user[,option][,option][,...]</pre></p>
<p>你可以指定以下属性：<br />vlan=n    连接到虚拟局域网<br />id,name    设备名称，在monitor命令中使用<br />net=addr[/mask]    在客户机看来，此后端的IP地址，默认10.0.2.0/24<br />host=addr    在客户机看来，宿主机的IP地址是多少，默认段内第二个IP，例如10.0.2.2<br />restrict=on|off    如果启用，则客户机被隔离，这意味着客户机不能访问宿主机或者联网<br />hostname    内置DHCP服务器报告的客户机的名称<br />dhcpstart    内置DHCP服务器能分配的IP地址的其实值，默认段内第15各IP，例如10.0.2.15<br />dns    指定客户机看到的虚拟DNS服务器的地址，默认段内第三个IP，例如10.0.2.3<br />smb=dir[,smbserver=addr]    激活一个内置的SMB服务器，默认地址为段内第四个，例如10.2.2.4<br />hostfwd    重定向主机端口上的incoming TCP/UDP流量到客户机端口</p>
</td>
</tr>
<tr>
<td>-netdev tap<br />-net tap</td>
<td>
<p>添加一个TAP网络后端，连接宿主机的TAP网络接口到VLAN，选项格式：</p>
<pre class="crayon-plain-tag">-netdev tap,id=id[,fd=h][,ifname=name][,script=file][,downscript=dfile][,helper=helper]
-net tap[,vlan=n][,name=name][,fd=h][,ifname=name][,script=file][,downscript=dfile][,helper=helper]</pre>
<p>你可以指定以下属性：<br />id    TAP设备的唯一标识，你只需要指定此标识，QEMU会自动在宿主机上创建对应的TAP设备<br />script    配置脚本，默认/etc/qemu-ifup。设置为no则禁用配置脚本<br />downscript    解除配置脚本，默认/etc/qemu-ifdown（空白文件）。设置为no则禁用配置脚本<br />fd    指定已经打开的宿主机TAP设备的句柄</p>
</td>
</tr>
<tr>
<td>-netdev bridge<br /> -net bridge</td>
<td>添加一个TAP网络后端，连接宿主机的TAP网络接口到宿主机的一个网桥，这是TAP后端的script-free的简化版，选项格式：<br />
<pre class="crayon-plain-tag">-netdev bridge,id=id[,br=bridge][,helper=helper]
-net bridge[,vlan=n][,name=name][,br=bridge][,helper=helper]</pre></p>
<p>你可以指定以下属性：<br />br    宿主机网桥名</p>
<p>此后端需要读取配置文件：</p>
<pre class="crayon-plain-tag">allow br0</pre>
</td>
</tr>
<tr>
<td style="text-align: center;" colspan="2"><em><strong>字符设备选项</strong></em></td>
</tr>
<tr>
<td>-chardev</td>
<td>
<p>字符设备选项的通用格式为：
<pre class="crayon-plain-tag">-chardev backend ,id=id [,mux=on|off] [,options]</pre>
<p>backend 包括： null, socket, udp, msmouse, vc, ringbuf, file, pipe, console, serial, pty, stdio, braille, tty, parallel, parport, spicevmc.  spiceport<br />id    所有设备都必须有的标识符，可以是任何长度不超过127的字符串<br />mux=on|off    所有字符设备都可以进入多路复用模式，供多个前端使用。Ctrl + A和Ctrl + C用于切换前端<br />options    取决于后端</p>
</td>
</tr>
<tr>
<td>-chardev null</td>
<td>其行为类似于/dev/null</td>
</tr>
<tr>
<td>-chardev vc</td>
<td>
<p>连接到QEMU的文本控制台（text console），选项格式：</p>
<pre class="crayon-plain-tag">-chardev vc ,id=id [[,width=width] [,height=height]] [[,cols=cols] [,rows=rows]] </pre>
<p>width/height    控制台的宽度高度、单位像素</p>
<p>cols/rows    匹配文本控制台宽高</p>
</td>
</tr>
<tr>
<td>-chardev ringbuf</td>
<td>
<p>创建一个固定大小的环形缓冲区，选项格式：<pre class="crayon-plain-tag">-chardev ringbuf ,id=id [,size=size]</pre> </p>
<p>size    必须是2的幂，默认64K</p>
</td>
</tr>
<tr>
<td>-chardev pipe  </td>
<td>创建一个双向的管道文件，选项格式：<pre class="crayon-plain-tag">-chardev pipe ,id=id ,path=path</pre> </td>
</tr>
<tr>
<td>-chardev file</td>
<td>记录来自客户端的流落到文件，选项格式：<pre class="crayon-plain-tag">-chardev file ,id=id ,path=path</pre> </td>
</tr>
<tr>
<td>-chardev console</td>
<td>发送来自客户端的流量到QEMU的标准输出</td>
</tr>
<tr>
<td>-chardev serial</td>
<td>
<p>发送来自客户端的流量到宿主机的一个串口设备，选项格式：<pre class="crayon-plain-tag">-chardev serial ,id=id ,path=path</pre> </p>
<p>path    宿主机的串口设备</p>
</td>
</tr>
<tr>
<td>-chardev pty</td>
<td>在宿主机上创建一个新的伪终端，并连接到它</td>
</tr>
<tr>
<td>-chardev tty</td>
<td>
<p>在Unix-like系统上可用，-chardev serial的别名</p>
<p>所谓TTY，即电传打字机，是由一个键盘、一个打印机组成的设备，在键盘上每打印一个字就会打印到纸张上。这个概念借用到UNIX领域，则打印的目标变成了屏幕。TTY可以用来指任何形式的Terminal，例如伪终端、虚拟控制台</p>
</td>
</tr>
<tr>
<td colspan="2">
<p style="text-align: center;"><strong><em>Linux/Multiboot相关</em></strong></p>
</td>
</tr>
<tr>
<td>-kernel</td>
<td>
<p>指定内核镜像，目标镜像可以是Linux内核或者multiboot格式</p>
</td>
</tr>
<tr>
<td>
<p>-append</p>
</td>
<td>
<p>指定内核命令行参数</p>
</td>
</tr>
<tr>
<td>-initrd</td>
<td>使用指定的文件作为初始内存盘（initial ram disk）</td>
</tr>
<tr>
<td style="text-align: center;" colspan="2"><em><strong>调试/专家选项</strong></em></td>
</tr>
<tr>
<td>-serial</td>
<td>
<p>-serial dev    重定向虚拟串口到宿主机的字符设备dev，默认设备：图形模式下是vs；非图形模式下是stdio</p>
<p>你可以指定此选项最多4次，模拟最多4个串口；指定 -serial none 禁用所有串口</p>
<p>可用的宿主机字符设备有：<br />vc    虚拟控制台，可选的，指定宽高（像素/字符数）：vc:800x600   vc80C:40Cb<br />stdio     标准输入输出，即启动QEMU的哪个Terminal<br />pty    伪终端<br />none  无设备<br />null    void设备<br />chardev:id    已命名的、通过-chardev选项配置的字符设备<br />/dev/tty    使用宿主机的tty</p>
</td>
</tr>
<tr>
<td>-monitor</td>
<td>-monitor dev 重定向monitor到主机的字符设备dev，可用设备同上</td>
</tr>
<tr>
<td>-qmp</td>
<td>-qmp dev    类似于-monitor但是以control模式开启 </td>
</tr>
<tr>
<td>-debugcon</td>
<td>-debugcon dev    重定向调试控制台到宿主机字符设备</td>
</tr>
<tr>
<td>-pidfile</td>
<td>存储QEMU进程的PID到文件</td>
</tr>
<tr>
<td>-enable-kvm</td>
<td>启用基于KVM的全虚拟化支持 </td>
</tr>
<tr>
<td>-no-reboot</td>
<td>退出而不是重启</td>
</tr>
<tr>
<td>-no-shutdown</td>
<td>当客户机关机时，不退出QEMU而仅仅是停止模拟。你可以切换到Monitor并提交修改到磁盘镜像</td>
</tr>
<tr>
<td>-loadvm</td>
<td>-loadvm file 从一个以保存的状态加载客户机</td>
</tr>
<tr>
<td>-daemonize</td>
<td>
<p>在初始化后，让QEMU变成一个守护进程。使用该选项，可以让QEMU进程和当前Terminal解除关联</p>
<p>此选项在1.4之后不能和<pre class="crayon-plain-tag">-nographic</pre> 联用，但是可以和<pre class="crayon-plain-tag">-display none</pre> 联用</p>
</td>
</tr>
<tr>
<td>-readconfig</td>
<td>从文件读取配置</td>
</tr>
<tr>
<td>-writeconfig </td>
<td>把配置写入到文件，如果指定 - 则打印到屏幕</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">快捷键</span></div>
<p>在图形化模拟期间，你可以使用快捷键：</p>
<table class=" fixed-word-wrap full-width">
<thead>
<tr>
<td style="width: 18%; text-align: center;">快捷键</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>Ctrl-Alt</td>
<td>释放/获取鼠标键盘</td>
</tr>
<tr>
<td>Ctrl-Alt-f</td>
<td>切换全屏模式</td>
</tr>
<tr>
<td>Ctrl-Alt-+</td>
<td>增大屏幕</td>
</tr>
<tr>
<td>Ctrl-Alt--</td>
<td>减小屏幕</td>
</tr>
<tr>
<td>Ctrl-Alt-u</td>
<td>还原原始屏幕大小</td>
</tr>
<tr>
<td>Ctrl-Alt-n</td>
<td>切换到虚拟控制台n，标准控制台为：<br />1    客户机系统的显示<br />2    Monitor<br />3    串口</td>
</tr>
</tbody>
</table>
<p> 如果你使用了-nographic，则可以使用以下快捷键：</p>
<table class=" fixed-word-wrap full-width">
<thead>
<tr>
<td style="width: 18%; text-align: center;">快捷键</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>Ctrl-a h</td>
<td>打印帮助</td>
</tr>
<tr>
<td>Ctrl-a x</td>
<td>退出模拟器</td>
</tr>
<tr>
<td>Ctrl-a s</td>
<td>保存磁盘数据文件（如果使用-snapshot）</td>
</tr>
<tr>
<td>Ctrl-a c</td>
<td>在控制台和Monitor之间切换</td>
</tr>
<tr>
<td>Ctrl-a Ctrl-a</td>
<td>发送Ctrl-a</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">qemu-nbd</span></div>
<p>用于创建QEMU网络块设备（Network Block Device）服务器，即通过NBD协议把磁盘镜像暴露出去。 命令格式：<pre class="crayon-plain-tag">qemu-nbd [OPTION]... diskimgfile</pre> 。</p>
<p>常用选项如下表：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">选项</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>-p</td>
<td>NBD服务监听端口，默认1024</td>
</tr>
<tr>
<td>-b</td>
<td>NBD服务器绑定的网络接口，默认0.0.0.0</td>
</tr>
<tr>
<td>-k</td>
<td>NBD绑定的UNIX socket路径</td>
</tr>
<tr>
<td>-o</td>
<td>访问镜像文件的偏移量</td>
</tr>
<tr>
<td>-f</td>
<td>镜像文件格式</td>
</tr>
<tr>
<td>-r</td>
<td>仅允许只读访问镜像</td>
</tr>
<tr>
<td>-P</td>
<td>--partition=num，仅暴露分区num</td>
</tr>
<tr>
<td>-s</td>
<td>把diskimgfile作为外部快照使用，创建一个新的临时镜像，将其backing_file设置为diskimgfile，写操作都重定向到临时镜像</td>
</tr>
<tr>
<td>-l</td>
<td>--load-snapshot=snapshot_param，加载diskimgfile中的一个内部快照，并暴露其为一个只读设备<br />snapshot_param可以是snapshot.id=id或者snapshot.name=name，或者直接写id/name</td>
</tr>
<tr>
<td>-n</td>
<td>禁用缓存</td>
</tr>
<tr>
<td>--cache=cache</td>
<td>设置缓存模式，支持的模式参考qemu-system-x86 -drive cache=</td>
</tr>
<tr>
<td>--aio=aio</td>
<td>选择AIO模式，threads或者native</td>
</tr>
<tr>
<td>-c</td>
<td>--connect=dev连接diskimgfile到一个NBD设备</td>
</tr>
<tr>
<td>-d</td>
<td>断开指定的设备</td>
</tr>
<tr>
<td>-e</td>
<td>--shared=num  此设备可以被最多num个客户端使用</td>
</tr>
<tr>
<td>-t</td>
<td>即使最后一个连接断开，也不退出</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">常见问题 </span></div>
<div class="blog_h2"><span class="graybg">不支持virtio-9p-pci</span></div>
<p>报错信息：'virtio-9p-pci' is not a valid device model name</p>
<p>解决办法：参考下面的脚本构建QEMU：</p>
<pre class="crayon-plain-tag">apt install libattr1-dev
configure --prefix=/usr --enable-virtfs
make &amp;&amp; make install</pre>
<div class="blog_h2"><span class="graybg">不支持SDL</span></div>
<p>报错信息：qemu-system-x86_64: -sdl: SDL support is disabled</p>
<p>解决办法：参考下面的脚本构建QEMU：</p>
<pre class="crayon-plain-tag">sudo apt install libsdl2-dev
./configure --prefix=/usr --enable-virtfs --enable-sdl</pre>
<div class="blog_h2"><span class="graybg">qcow2镜像损坏处理</span></div>
<p>报错信息：Image is corrupt; cannot be opened read/write</p>
<p>解决办法：</p>
<pre class="crayon-plain-tag">qemu-img check -r all /media/alex/v12n2/libvirt/images/xenial-23</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/kvm-qemu-study-note">KVM和QEMU学习笔记</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/kvm-qemu-study-note/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>libvirt学习笔记</title>
		<link>https://blog.gmem.cc/libvirt-study-note</link>
		<comments>https://blog.gmem.cc/libvirt-study-note#comments</comments>
		<pubDate>Tue, 04 Aug 2015 08:26:13 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Virtualization]]></category>
		<category><![CDATA[libvirt]]></category>
		<category><![CDATA[学习笔记]]></category>

		<guid isPermaLink="false">https://blog.gmem.cc/?p=13220</guid>
		<description><![CDATA[<p>简介 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的二进制组件可能已经随操作系统安装，如果没有，你可以： [crayon-69d26d56e66a0726056360/] 辅助工具 <a class="read-more" href="https://blog.gmem.cc/libvirt-study-note">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/libvirt-study-note">libvirt学习笔记</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h1"><span class="graybg">简介</span></div>
<p>libvirt是广泛使用的、通用虚拟化管理工具，它提供多种命令行工具、多种语言的编程API。</p>
<p>libvirt的目标是：提供一个通用、稳定的抽象层，来安全有效的远程管理一个节点（node）之上的域（domains），因此它需要提供全套的API来完成管理，这些API必须完成Domain的创建、修改、配置、监控、迁移、停止。</p>
<p>libvirt可以管理的虚拟化机制（hypervisor或container）包括：KVM/QEMU、Xen、LXC、OpenVZ、VirtualBox、VMware ESX/GSX、VMware Workstation/Player、Microsoft Hyper-V、IBM PowerVM。</p>
<div class="blog_h2"><span class="graybg">名词术语</span></div>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">术语</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>node</td>
<td>一台物理机器 </td>
</tr>
<tr>
<td>hypervisor </td>
<td>
<p>node上面的一个软件层，它能虚拟化node，并在其上建立多个虚拟机 </p>
<p>libvirt通过所谓driver和各种不同的hypervisor打交道</p>
</td>
</tr>
<tr>
<td>domain</td>
<td>运行在受hypervisor管理的虚拟化node之上的一个操作系统，当基于容器虚拟化时，则是一个子系统</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">安装libvirt</span></div>
<p>libvirt的二进制组件可能已经随操作系统安装，如果没有，你可以：</p>
<pre class="crayon-plain-tag">sudo apt-get install libvirt-bin</pre>
<div class="blog_h2"><span class="graybg">辅助工具</span></div>
<p>可以安装virt-install，这是一个用来创建基于KVM、XEN或者Linux容器的客户机的工具：</p>
<pre class="crayon-plain-tag">sudo apt-get install virtinst</pre>
<p>可以安装virt-manager，它提供了基于libvirt的图形化管理工具：</p>
<pre class="crayon-plain-tag">sudo apt-get install virt-manager</pre>
<p>可以安装virt-viewer，它用于连接到虚拟机的Graphical Console：</p>
<pre class="crayon-plain-tag"># 安装
sudo apt-get install virt-viewer
# 使用
virt-viewer -c qemu:///system</pre>
<div class="blog_h1"><span class="graybg">使用Virsh</span></div>
<p>virsh是libvirt提供的一个命令行工具，利用它你可以通过命令行，交互式的管理你的虚拟机（Domain）。使用此命令，你可以创建、暂停、关闭domain，可以列出当前的domain。</p>
<p>libvirt会在宿主机上运行一个libvirtd守护进程，此进程可以被本地/远程的virsh调用。libvirtd则可以直接调用qemu-kvm来操控客户机。大部分virsh命令需要libvirtd处于运行状态才可用。</p>
<div class="blog_h2"><span class="graybg">Domain管理</span></div>
<p>使用virsh的define、edit、start、shutdown|destroy、reboot、suspend、resumen、undefined子命令，分别可以定义、编辑、启动、关闭、暂停、唤醒、删除Domain。这些命令比较简单，参考virsh命令详解一节。</p>
<div class="blog_h2"><span class="graybg">快照管理</span></div>
<div class="blog_h3"><span class="graybg">快照分类</span></div>
<p>快照可以分为三个级别：</p>
<ol>
<li>卷管理器（Volume Manager） 级别，例如LVM的Snapsot功能</li>
<li>文件系统级别，常用的Ext3不支持，OCFS2支持</li>
<li>文件级别，Raw格式的镜像不支持快照，qcow2格式则支持，且快照分为两类：
<ol>
<li>内部快照：保存在qcow2文件内部的快照：
<ol>
<li>虚拟机状态快照（VM State snapshot）：整个虚拟机的状态，不仅仅是磁盘</li>
<li>磁盘状态快照（Disk State snapshot）：仅仅针对磁盘的快照</li>
</ol>
</li>
<li>外部快照：将原先（Backing）的qcow2镜像设置为只读，新的改变保存到另外的qcow2文件</li>
</ol>
</li>
</ol>
<div class="blog_h3"><span class="graybg">内存快照</span></div>
<p>使用virsh save / virsh restore命令，可以仅仅将Domain的内存状态保存，然后停止Domain，最后恢复。恢复时假设磁盘没有任何改动：</p>
<pre class="crayon-plain-tag"># 保存内存快照
virsh save fedora-10 fedora-10.vmstate

# 恢复内存快照
virsh restore fedora-10.vmstate</pre>
<div class="blog_h3"><span class="graybg">内部快照</span></div>
<p>内部快照、外部快照使用同一组命令来管理的。这些快照默认包含内存、磁盘、设备等全部状态。内部快照示例：</p>
<pre class="crayon-plain-tag"># 创建一个快照
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</pre>
<p>注意，<span style="background-color: #c0c0c0;">一旦创建了快照，Domain就不能被undefine</span>。</p>
<p>要删除内部快照，可以执行：</p>
<pre class="crayon-plain-tag">virsh snapshot-delete fedora-10 1473667716
# Domain snapshot 1473667716 deleted</pre>
<div class="blog_h3"><span class="graybg">外部快照</span></div>
<p>执行下面的命令创建一个外部快照： </p>
<pre class="crayon-plain-tag"># 这里我们仅针对vda磁盘创建了快照，内存状态没有做快照
snapshot-create-as fedora-10 blankos "Initial snapshot" 
    --diskspec=vda,file=/home/alex/Vmware/KVM/fedora-10/blankos.vda.qcow2 --disk-only --atomic</pre>
<p>现在查看客户机关联的块设备：</p>
<pre class="crayon-plain-tag">virsh domblklist fedora-10
# Target     Source
# ------------------------------------------------
# vda        /home/alex/Vmware/KVM/fedora-10/blankos.vda.qcow2</pre>
<p>可以发现关联性转移到外部快照上了，原先的磁盘镜像成为Backing file。注意：<span style="background-color: #c0c0c0;">Domain的后续写操作都发生在新创建的磁盘上</span></p>
<p>要删除外部快照，执行：</p>
<pre class="crayon-plain-tag">virsh snapshot-delete fedora-10 --metadata blankos</pre>
<div class="blog_h3"><span class="graybg">快照链管理</span></div>
<p>我们来创建三个快照：</p>
<pre class="crayon-plain-tag">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</pre>
<p>查看当前快照：</p>
<pre class="crayon-plain-tag"># 默认的，新创建的快照作为当前快照
virsh snapshot-current fedora-10 --name</pre>
<p>查看快照链（Backing chain）：</p>
<pre class="crayon-plain-tag">virsh snapshot-list fedora-10 --tree
# vda.qcow2 是base
# snap0
#   |
#   +- snap1
#       |
#       +- snap2    这个是top</pre>
<p>libvrit支持多种方式来管理磁盘的快照链：</p>
<p><strong>方式一：基于blockcommit，合并到base镜像</strong></p>
<p>我们可以清理快照链条，将snap2、snap1、snap0中的变更都<span style="background-color: #c0c0c0;">提交到</span>vda.qcow2中</p>
<pre class="crayon-plain-tag"># 必须在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</pre>
<p>提交后，可以安全的删除快照及其元数据（snapshot-delete --metadata），libvrit是<span style="background-color: #c0c0c0;">分开管理backing链和snapshot列表</span>的。 </p>
<p><strong>方式二：基于blockpull，合并到top镜像</strong></p>
<p>也可以反过来，把<span style="background-color: #c0c0c0;">base一直pull到top位置（必须是叶子节点）的snapshot</span>，然后此snapshot就成为完整的磁盘镜像了（不依赖backing镜像）： </p>
<pre class="crayon-plain-tag">virsh blockpull fedora-10 --path $DIR/snap2.vda.qcow2 --base $DIR/vda.qcow2 --wait –verbose</pre>
<p><strong>方法三：基于blockcopy，可以在线迁移磁盘</strong></p>
<p>首先，需要取消Domain定义，将其变为transient的：</p>
<pre class="crayon-plain-tag"># 导出Domain配置
virsh dumpxml --inactive fedora-10 $DIR/domain.xml
# 取消定义
virsh undefine fedora-10</pre>
<p>然后执行拷贝：</p>
<pre class="crayon-plain-tag"># --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</pre>
<p>拷贝完成后，瞬时的Domain使用copy继续运行：</p>
<pre class="crayon-plain-tag">virsh domblklist fedora-10
# Target     Source
# ------------------------------------------------
# vda        /home/alex/Vmware/KVM/fedora-10/copy.vda.qcow2 </pre>
<p>而原先的磁盘可以迁移走了。</p>
<div class="blog_h2"><span class="graybg">远程访问</span></div>
<p>要通过virsh来访问远程宿主机上的Domain时，需要提供URI。URI的格式如下：</p>
<pre class="crayon-plain-tag">driver[+transport]://[username@][hostname][:port]/[path][?extraparameters]</pre>
<p>URI各部分说明如下：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 15%; text-align: center;">部分</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>driver</td>
<td>驱动，不同驱动对应了不同的Hypervisor</td>
</tr>
<tr>
<td>transport</td>
<td>
<p>传输协议，主要包括以下几种：</p>
<ol>
<li>unix，使用Unix Domain Socket，仅能在本地使用，不加密，示例：<br />
<pre class="crayon-plain-tag">qemu+unix:///system?socket=/opt/libvirt/run/libvirt/libvirt-sock </pre>
</li>
<li>ssh，通过SSH隧道进行连接，相当于通过SSH隧道在目标宿主机上执行Unix Domain Socket，示例：<br />
<pre class="crayon-plain-tag">qemu+ssh://root@tokyo.gmem.cc/system</pre>
</li>
<li>tcp，通过TCP进行远程连接，通过DIGEST-MD5进行加密，使用SASL/Kerberos进行身份验证，示例：<br />
<pre class="crayon-plain-tag">qemu+tcp://tokyo.gmem.cc/system </pre>
</li>
<li>tls，类似于tcp，但是使用SSL对TCP进行加密，需要配置密钥和证书，使用SASL/Kerberos进行身份验证，示例：<br />
<pre class="crayon-plain-tag">qemu+tls://tokyo.gmem.cc/system</pre>
</li>
</ol>
</td>
</tr>
</tbody>
</table>
<p>要连接到远程宿主机，可以使用-c选项或者connect子命令：</p>
<pre class="crayon-plain-tag">virsh -c qemu+ssh://root@zircon.local/system</pre>
<div class="blog_h3"><span class="graybg">使用unix传输</span></div>
<p>使用该transport时，需要注意配置文件：</p>
<pre class="crayon-plain-tag"># 这些项都是默认值
unix_sock_group = "libvirtd"
unix_sock_ro_perms = "0777"
unix_sock_rw_perms = "0770"</pre>
<p>也就是说，用户必须加入到libvirtd组，才可以使用unix传输，否则会报错：error: Failed to connect socket to '/var/run/libvirt/libvirt-sock': Permission denied。执行下面的命令添加用户到组：</p>
<pre class="crayon-plain-tag">sudo usermod -a -G libvirtd alex</pre>
<p>注意：连接到qemu时，不指定主机名默认使用<span style="color: #000000;">unix socket。</span></p>
<div class="blog_h3"><span class="graybg">使用tcp传输</span></div>
<p>在目标宿主机上，修改配置文件：</p>
<pre class="crayon-plain-tag"># 启用TCP监听
libvirtd_opts="-d -l"</pre>
<p>然后再修改配置文件：</p>
<pre class="crayon-plain-tag"># 默认TCP监听是禁用的
listen_tcp = 1
# 可以修改监听地址和端口
listen_addr = "0.0.0.0"
tcp_port = "16509"
# 可以不启用验证，但是缺乏安全性，所有流量都是明文
auth_tcp = "none"</pre>
<p>最后重启libvirtd即可。  </p>
<div class="blog_h2"><span class="graybg">资源管理</span></div>
<p>相关文章：<a href="/linux-faq#cgroup">Linux知识集锦 - cgroup</a></p>
<p>libvirt基于cgroup来限制客户机对宿主机资源的访问。libvirt不会尝试加载任何controllers，它只会检测哪些controllers被mount。</p>
<p>QEMU驱动支持cpuset, cpu, memory, blkio, devices这几个controller，修改配置文件/etc/libvirt/qemu.conf可以针对QEMU禁用某些controller。</p>
<p>LXC驱动支持 cpuset, cpu, cpuacct, freezer, memory, blkio,devices 这几个controller， 其中cpuacct, devices, memory是必须的，如果这几个controller没有被mount则容器不会被启动。</p>
<div class="blog_h3"><span class="graybg">cgroups布局</span></div>
<p>libvrit引入两个概念，以方便cgroups管理：</p>
<ol>
<li>partitions：不包含任何进程的cgroup，仅仅包含资源控制规则，它可以包含多个子目录，这些子目录要么是partition要么是consumers</li>
<li>consumers：是包含了<span style="background-color: #c0c0c0;">单个</span>虚拟机/容器进程的cgroup</li>
</ol>
<p>对于不使用systemd的宿主机，consumers命名规则为<pre class="crayon-plain-tag">$VMNAME.libvirt-{qemu,lxc}</pre> ，其中VMNAME为虚拟机的名称。默认的，所有consumer都挂在名为machine的partition下：</p>
<pre class="crayon-plain-tag">ls /sys/fs/cgroup/cpu/machine
# fedora-10.libvirt-qemu  ...</pre>
<p>直到cgroups布局后，你就可以直接读写cgroups文件系统，来控制客户机的资源访问。但是virsh也提供了一些命令在运行时控制资源访问。</p>
<div class="blog_h3"><span class="graybg">资源管理命令</span></div>
<ol>
<li>对于CPU访问控制，可以使用virsh schedinfo命令</li>
<li>对于块设备的访问控制，可以使用virsh blkiotune命令</li>
<li>对于网卡流量的控制，可以使用domiftune或者tc命令</li>
</ol>
<div class="blog_h2"><span class="graybg">virsh命令详解</span></div>
<p>该命令最常见的调用形式为：<pre class="crayon-plain-tag">virsh [OPTION]... &lt;command&gt; &lt;domain&gt; [ARG]...</pre> 。其中：</p>
<ol>
<li>command 是一个virsh子命令</li>
<li>domain 是操控的虚拟机的名称、ID或者UUID</li>
<li>ARG是针对特定子命令的参数</li>
<li>OPTION为一般性选项</li>
</ol>
<div class="blog_h3"><span class="graybg">一般选项</span></div>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 10%; text-align: center;">选项</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>-c</td>
<td>--connect URI    连接到指定的URI，而不是默认的连接。此选项的效果如同调用了connect子命令</td>
</tr>
<tr>
<td>-d</td>
<td>--debug LEVEL    设置调试级别，级别范围0-4，默认4</td>
</tr>
<tr>
<td>-k</td>
<td>--keepalive-interval INTERVAL   设置确认服务器连接未断开的心跳的发送间隔，单位秒，设置为0则不检测</td>
</tr>
<tr>
<td>-K</td>
<td>--keepalive-count COUNT    确认连接端口之前，发送心跳的次数</td>
</tr>
<tr>
<td>-l</td>
<td>--log FILE 输出日志到文件</td>
</tr>
<tr>
<td>-q</td>
<td>--quiet    安静模式，避免不必要的信息打印</td>
</tr>
<tr>
<td>-t</td>
<td>--timing    为每个命令打印消耗的时间信息</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">一般子命令</span></div>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 15%; text-align: center;">子命令</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>help</td>
<td>
<p>显示帮助信息：</p>
<pre class="crayon-plain-tag"># 列出子命令列表
virsh help
# 显示一个子命令的用法
virsh help define</pre>
</td>
</tr>
<tr>
<td>quit, exit</td>
<td>退出交互式的Terminal</td>
</tr>
<tr>
<td>version</td>
<td>显示版本信息：libvir库版本、API版本、运行中的hypervisor版本</td>
</tr>
<tr>
<td>cd</td>
<td>改变当前目录，禁用与交互式的terminal</td>
</tr>
<tr>
<td>pwd</td>
<td>打印当前目录名</td>
</tr>
<tr>
<td>connect</td>
<td>
<p>connect [URI] [--readonly]
<p>（重）连接到一个hypervisor，URI指明如何连接到hypervisor，例如：<br />xen:///    连接到本地XEN hypervisor<br />qemu:///system    以root身份连接到本地管理QEMU/KVM domain的hypervisor<br />qemu:///session   以普通用户身份连接到本地，管理他自己的QEMU/KVM domain<br />lxc:///    连接到本地的LXC容器</p>
</td>
</tr>
<tr>
<td>uri</td>
<td>打印当前连接到的hypervisor的URI</td>
</tr>
<tr>
<td>hostname</td>
<td>打印hypervisor的主机名</td>
</tr>
<tr>
<td>capabilities</td>
<td>打印一个描述当前连接到的hypervisor的能力（capabilities）的XML文档</td>
</tr>
<tr>
<td>list</td>
<td>列出存在的Domain，如果不指定参数，则打印所有运行中的Domain信息</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">Domain子命令</span></div>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">子命令</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>autostart</td>
<td><em><span style="text-decoration: underline;">autostart [--disable] domain</span></em><br />用于配置一个Domain随着宿主机而启动</td>
</tr>
<tr>
<td>console</td>
<td><span style="text-decoration: underline;"><em>console domain [devname] [--safe] [--force]</em></span><br />连接到客户机的虚拟串口控制台：<br />devname  设置为一个备选控制台、串口/并口设备的别名，如果不指定则连接到primary控制台</td>
</tr>
<tr>
<td>create</td>
<td><span style="text-decoration: underline;"><em>create FILE [--console] [--paused] [--autodestroy] [--pass-fds N,M,...]</em></span><br />从XML文件FILE创建一个Domain。创建XML的简便方法是调用<pre class="crayon-plain-tag">dumpxml</pre> 子命令来获得既有实例的XML配置：<br />--paused    新的Domain将会暂停，不指定则运行<br />--console    创建后连接到Domain的console<br />--autodestroy    如果virsh断开到libvirt的连接，则自动销毁此domain</td>
</tr>
<tr>
<td>define</td>
<td><em><span style="text-decoration: underline;">define FILE</span></em><br />从XML文件FILE定义一个Domain，此Domain会注册，但是不会自动启动。如果Domain已经在运行，则对其配置的变更在下次启动时生效</td>
</tr>
<tr>
<td>undefine</td>
<td><span style="text-decoration: underline;"><em>undefine domain [--managed-save] [--snapshots-metadata] [ {--storage volumes | --remove-all-storage} --wipe-storage]</em></span><br />解除一个Domain的定义，如果此Domain正在运行，它会被转换为transient的；如果Domain没有运行，则移除它的配置</td>
</tr>
<tr>
<td>desc</td>
<td><span style="text-decoration: underline;"><em>desc domain [[--live] [--config] | [--current]] [--title] [--edit] [--new-desc  new_desc_msg]</em></span><br />显示或者修改Domain的描述、标题，标题通常比较简短</td>
</tr>
<tr>
<td>destroy</td>
<td><span style="text-decoration: underline;"><em>destroy domain [--graceful]</em></span><br />立即终止一个Domain，客户机将没有反应时间，相当于拔掉机器的电源<br />--graceful    避免极度的手段销毁（SIGKILL），如果客户机一段时间后没有关闭，返回一个错误消息</td>
</tr>
<tr>
<td>reboot</td>
<td><span style="text-decoration: underline;"><em>reboot domain [--mode MODE-LIST]</em></span><br />重启一个Domain，效果类似于执行reboot命令</td>
</tr>
<tr>
<td>reset</td>
<td><em><span style="text-decoration: underline;">reset domain</span></em><br />重置一个Domain，效果类似于按主机上的重置按钮，客户机将没有反应时间</td>
</tr>
<tr>
<td>shutdown</td>
<td><em><span style="text-decoration: underline;">shutdown domain [--mode MODE-LIST]</span></em><br />优雅的关闭Domain，此命令将和客户机协商以关机，因此不一定成功，可能消耗较长时间</td>
</tr>
<tr>
<td>start</td>
<td><span style="text-decoration: underline;"><em>start name-or-uuid [--console] [--paused] [--autodestroy] [--bypass-cache] [--force-boot] [--pass-fds N,M,...]</em></span><br />启动一个已经定义的Domain：<br />--paused    此Domain将会暂停<br />--console    连接到客户机的控制台<br />--autodestroy    当virsh断开到libvirtd的连接后，自动销毁Domain</td>
</tr>
<tr>
<td>suspend</td>
<td><em><span style="text-decoration: underline;">suspend domain</span></em><br />暂停一个运行中的Domain，它会维持在内存中，但不再参与调度</td>
</tr>
<tr>
<td>resume</td>
<td><em><span style="text-decoration: underline;">resume domain</span></em><br />从暂停中恢复</td>
</tr>
<tr>
<td>dumpxml</td>
<td><em><span style="text-decoration: underline;">dumpxml domain [--inactive] [--security-info] [--update-cpu] [--migratable]</span></em><br />输出Domain的XML配置信息到屏幕：<br />--migratable    输出一个可迁移的配置<br />--inactive    dump出Domain下次启动时使用的配置，而不是当前正在使用的配置<br />--update-cpu    根据宿主机的CPU，更新Domain配置中的CPU部分</td>
</tr>
<tr>
<td>edit</td>
<td><span style="text-decoration: underline;"><em>edit domain</em></span><br />编辑一个Domain的XML配置，并在下次启动Domain时生效</td>
</tr>
<tr>
<td>save</td>
<td>
<p><span style="text-decoration: underline;"><em>save domain state-file [--bypass-cache] [--xml file] [{--running | --paused}] [--verbose]<br /></em></span></p>
<p>保存一个运行中的Domain的<span style="background-color: #c0c0c0;">内存（而不是磁盘）</span>状态到一个状态文件中，以便后续恢复。一旦被保存，则<span style="background-color: #c0c0c0;">Domain不再继续运行</span>，分配给Domain的内存可以被其它程序使用。该命令类似于Hibernate功能</p>
<p>state-file    状态文件路径<br />--bypass-cache    不包含文件系统缓存，会加快保存速度<br />--verbose    显示保存进度<br />--running    --paused    在恢复后，将Domain变为运行或暂停状态</p>
<p>可以基于domjobinfo子命令监控进度，或者利用domjobabort子命令取消保存，对当前Terminal发送SIGINT（Ctrl + C）也会取消保存</p>
</td>
</tr>
<tr>
<td>restore</td>
<td>
<p><span style="text-decoration: underline;"><em>restore state-file [--bypass-cache] [--xml file] [{--running | --paused}]</em></span></p>
<p>将Domain从virsh save状态中还原</p>
</td>
</tr>
<tr>
<td>domblkstat</td>
<td>
<p><span style="text-decoration: underline;"><em>domblkstat domain [block-device] [--human]<br /></em></span></p>
<p>输出块设备的统计信息<br />block-device    块设备名称（&lt;target dev='name'/&gt;）或块源文件（&lt;source file='name'/&gt;）<br />--human    输出易读的格式</p>
<p>输出列说明：<br />rd_req    读操作次数<br />rd_bytes    读字节数<br />wr_req    写操作次数<br />wr_bytes    写字节数<br />errs    错误计数<br />flush_operations    刷出磁盘的操作次数<br />rd_total_times    读操作总计消耗ns数<br />wr_total_times    写操作总计消耗ns数<br />flush_total_times    刷出操作总计消耗ns数</p>
<p>举例：<pre class="crayon-plain-tag">virsh domblkstat fedora-10 vda</pre> </p>
</td>
</tr>
<tr>
<td>domblkerror</td>
<td>
<p><span style="text-decoration: underline;"><em>domblkerror domain</em></span></p>
<p>显示块设备错误</p>
</td>
</tr>
<tr>
<td>domblkinfo</td>
<td>
<p><span style="text-decoration: underline;"><em> domblkinfo domain block-device</em></span></p>
<p>显示块设备的尺寸相关信息</p>
</td>
</tr>
<tr>
<td>domblklist</td>
<td>
<p><span style="text-decoration: underline;"><em> domblkinfo domain block-device<br /></em></span></p>
<p>以表格形式打印与Domian相关联的块设备的简要信息</p>
</td>
</tr>
<tr>
<td>blockcommit</td>
<td>
<p><span style="text-decoration: underline;"><em>blockcommit domain path [bandwidth] {[base] | [--shallow]} [top] [--delete] [--wait [--verbose] [--timeout seconds] [--async]]</em></span></p>
<p>减少backing镜像链条的长度，将top（最新的）中的变化提交到backing镜像中去。默认的：</p>
<ol>
<li>此命令flatten整个链条</li>
<li>此命令立即返回，commit操作在后台进行，可以使用blockjob检查进度</li>
</ol>
<p>path    磁盘的全限定路径，&lt;target dev='name'/&gt;的name或者&lt;source file='name'/&gt;的file<br />base top    如果指定之一或都指定，则限制commit操作链条的范围<br />--shallow    提交到top直接的backing镜像<br />--delete     操作完成后，删除被合并的文件<br />--wait    阻塞直到操作完成<br />--timeout    阻塞最多的秒数<br />--verbose    显示进度的详细信息<br />--async    尽快的返回，否则在完成commit后还要等待一些清理操作</p>
</td>
</tr>
<tr>
<td>blockpull</td>
<td>
<p><span style="text-decoration: underline;"><em>blockpull domain path [bandwidth] [base] [--wait [--verbose] [--timeout seconds] [--async]]</em></span></p>
<p>从backing镜像链生成一个磁盘。默认的：</p>
<ol>
<li>此命令flatten整个链条</li>
<li>此命令立即返回，commit操作在后台进行，可以使用blockjob检查进度</li>
</ol>
<p>path    磁盘的全限定路径，&lt;target dev='name'/&gt;的name或者&lt;source file='name'/&gt;的file<br />base   backing链条中的这一成员保留，仅它与top之间的backing镜像被合并到top</p>
</td>
</tr>
<tr>
<td>blockcopy</td>
<td>
<p><span style="text-decoration: underline;"><em>blockcopy domain path dest [bandwidth] [--shallow] [--reuse-external] [--raw] [--wait [--verbose] [{--pivot | --finish}] [--timeout seconds] [--async]]</em></span></p>
<p>拷贝磁盘的backing镜像链到dest。默认的：</p>
<ol>
<li>此命令flatten整个链条</li>
<li>此命令立即返回，commit操作在后台进行，可以使用blockjob检查进度</li>
</ol>
<p>domain    操作针对的Domain<br />path     操作针对的磁盘，全限定路径，&lt;target dev='name'/&gt;的name或者&lt;source file='name'/&gt;的file<br />bandwidth    带宽占用限制，MiB/s<br />--shallow  共享backing链，即对于base ⇦ snap0 ⇦ snap1，dest与src共享base ⇦ snap0<br />–pivot    转移，即拷贝完成后，Domain改用dest，不再使用src<br />--reuse-external    指定该选项则dest必须存在，且内容与resulting backing file等同<br />--raw    指定dest的格式，如果指定--reuse-external，则使用dest文件的格式</p>
<p>该命令主要用途是虚拟机的在线磁盘映像拷贝（live disk image copying）或镜像（mirroring），在存储迁移时很有用。应用场景包括：</p>
<ol>
<li>在线磁盘存储迁移</li>
<li>在线磁盘映像、及其backing链的备份</li>
<li>高效的非共享（ non-shared ）存储迁移</li>
</ol>
<p>一个blockcopy操作可以分为两个阶段：</p>
<ol>
<li>所有的源磁盘内容被拷贝到dest。在此阶段，任务可以被取消，dest的状态没有保证</li>
<li>source、dest的内容变得等同，它们将保持mirrored状态，直到调用blockjob  --abort以结束mirroring</li>
</ol>
</td>
</tr>
<tr>
<td>domifstat</td>
<td>
<p><span style="text-decoration: underline;"><em>domifstat domain interface-device</em></span></p>
<p>输出网络接口的统计信息</p>
<p>输出列说明：<br /> rx_bytes 收字节数<br /> rx_packets收IP封包数<br /> rx_errs 错误封包数<br /> rx_drop 丢弃封包数<br /> tx开头的表示发送的统计数据</p>
<p>举例：<pre class="crayon-plain-tag">virsh domifstat fedora-10 tap0</pre> </p>
</td>
</tr>
<tr>
<td>domif-setlink</td>
<td>
<p><span style="text-decoration: underline;"><em>domif-setlink domain interface-device state [--config]</em></span></p>
<p>修改网络接口的状态<br />state    目标状态，up/down<br />--config    --persistent    仅仅修改Domain的持久化配置，不立即改变接口状态</p>
</td>
</tr>
<tr>
<td>domif-getlink</td>
<td>
<p><span style="text-decoration: underline;"><em>domif-getlink domain interface-device [--config]</em></span></p>
<p>获得网络接口的状态</p>
</td>
</tr>
<tr>
<td>domiflist</td>
<td>
<p><span style="text-decoration: underline;"><em> domiflist domain [--inactive]<br /></em></span></p>
<p>以表格形式打印与Domian相关联的网络接口的简要信息</p>
</td>
</tr>
<tr>
<td>dommemstat</td>
<td>
<p><span style="text-decoration: underline;"><em>dommemstat domain [--period seconds] [[--config] [--live] | [--current]]</em></span></p>
<p>获得运行中的Domain的内存统计信息</p>
</td>
</tr>
<tr>
<td>blkdeviotune</td>
<td>
<p><span style="text-decoration: underline;"><em>blkdeviotune domain device [[--config] [--live] | [--current]] [[total-bytes-sec] | [read-bytes-sec] [write-bytes-sec]] [[total-iops-sec] | [read-iops-sec] [write-iops-sec]]<br /></em></span></p>
<p>查询或者设置Domain的某个块设备的I/O参数：<br />domain   目标客户机<br />device    操作针对的磁盘，&lt;target dev='name'/&gt;的name或者&lt;source file='name'/&gt;的file<br />--total-bytes-sec    设置每秒总计吞吐量的限制，单位Byte<br />--read-bytes-sec    设置每秒读取吞吐量的限制，单位Byte<br />--write-bytes-sec   设置每秒写入吞吐量的限制，单位Byte<br />--total-iops-sec    设置每秒IO操作次数限制<br />--read-iops-sec    设置每秒读次数限制<br />--write-iops-sec   设置每秒写次数限制<br />--live    影响正在运行的客户机<br />--config    在下次重启后，影响非瞬时Domain<br />--current    影响当前客户机的状态</p>
</td>
</tr>
<tr>
<td> domiftune</td>
<td>
<p><em><span style="text-decoration: underline;">domiftune domain interface-device [[--config] [--live] | [--current]] [--inbound average,peak,burst] [--outbound average,peak,burst]</span></em></p>
<p>查询或者修改网络接口的带宽参数：<br />interface-device    目标网络接口<br />--inbound    --outbound   修改入站还是出站带宽参数，如果都不指定，则执行查询操作。average/peak以KiB/s解释，burst以单次burst消耗的KiB解释<br />        average  设置网络接口期望的平均速率<br />        peak 设置峰值速率限制<br />        burst 以峰值速率爆发式的传送数据，最多连续传送多是KiB<br />--live    影响当前正在运行的Domain</p>
<p>举例：<pre class="crayon-plain-tag">virsh domiftune fedora-10 tap0</pre> </p>
</td>
</tr>
<tr>
<td>schedinfo </td>
<td>
<p><span style="text-decoration: underline;"><em>schedinfo domain [[--config] [--live] | [--current]] [[--set] parameter=value]...</em></span></p>
<p><span style="text-decoration: underline;"><em> schedinfo [--weight number] [--cap number] domain</em></span></p>
<p>显示或者设置Domain进程在宿主机中调度参数，可用参数：<br />LXC (posix scheduler)：cpu_shares<br />QEMU/KVM (posix scheduler)：cpu_shares, vcpu_period, vcpu_quota, emulator_period, emulator_quota<br />Xen (credit scheduler)： weight, cap<br />ESX (allocation scheduler)：reservation, limit, shares</p>
<p>--live    影响正在运行的Domain<br />--config    在下次重启后，影响非瞬时Domain<br />--set    执行设置操作</p>
<p>cpu_shares    处理器占用权重，范围0-262144，负值被转换为正值因此-1即262144，超过最大值都相当于262144<br />vcpu_period     对调度进行干预的周期，单位us，范围1000-1000000或者0，100000表示100ms干预一次<br />emulator_period<br />vcpu_quota    在干预周期内，进程能得到的时间片数，单位us，25000表示在一个周期内得到25ms的时间片<br />emulator_quota</p>
</td>
</tr>
<tr>
<td>screenshot</td>
<td>
<p><span style="text-decoration: underline;"><em>screenshot domain [imagefilepath] [--screen screenID]<br /></em></span></p>
<p>对Domain当前Console进行截屏，并保存到文件</p>
</td>
</tr>
<tr>
<td>send-key</td>
<td>
<p><span style="text-decoration: underline;"><em>send-key domain [--codeset codeset] [--holdtime holdtime] keycode..</em></span></p>
<p>将keycode的序列转换为按键动作，并发送到Domainkey，可用参数：<br />code可以是数字或者是codeset中的符号名称<br />--holdtime  每个按键按下持续的毫秒数<br />--codeset 指定代码集，默认Linux</p>
<p>举例：</p>
<pre class="crayon-plain-tag"># 发送右侧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 </pre>
</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">迁移子命令</span></div>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">子命令</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>migrate</td>
<td>
<p><span style="text-decoration: underline;"><em>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]</em></span>
<p>将客户机迁移到另外一台宿主机上，可用参数：<br />--live  在线迁移，迁移期间，源宿主机上的客户机不被暂停<br />--p2p  使用点对点迁移<br />--direct  使用直接迁移<br />--tunnelled  使用隧道迁移<br />--offline  不在目标机器上启动客户机，也不再源机器上停止客户机。通常用于inactive客户机的迁移<br />--persistent  让Domain在目标机器上持久化<br />--undefinesource  取消Domain在源机器上的定义<br />--suspend  让客户机在目标机器上停留在suspend状态<br />--copy-storage-all  提示不使用共享存储，进行完整的磁盘拷贝<br />--copy-storage-inc  提示不使用共享存储，进行增量的磁盘拷贝（即源、目标的共享backing file不拷贝）<br />注意：上面两个参数要求目标镜像文件存在于目标机器的对应位置<br />--change-protection  确保在迁移完成前，不得对Domain执行不兼容的配置更改。如果Hypervisor支持，该参数会自动包含，手工指定此参数的话，如果Hypervisor不支持change protection，则迁移操作被libvirt拒绝<br />--verbose  显示迁移进度<br />--compressed  对于需要在迁移过程中反复传输的内存页，执行压缩<br />--abort-on-error  当发生软错误（Soft error，例如I/O错误）时取消迁移<br />--unsafe  允许不安全的迁移（可能导致数据损坏）<br />desturi  目标宿主机的连接URI。对于点对点迁移，该参数是源宿主机看到的目标主机的URI；对于普通迁移则是客户端看到的URI<br />domain  被迁移的客户机名称<br />dname   在迁移时，修改Domain的名称<br />--xml  在目标机器上，为Domain指定机器特定的Domain配置信息，例如存储卷的名称（对于同一底层存储两台机器的命名可能不同）<br />--timeout  如果在线迁移超过指定的秒数，则Domain被强制suspend，然后转入离线迁移，必须和--live联用<br />listen-address  设置目标主机上的Hypervisor监听迁移请求的地址和端口</p>
<p>注意：</p>
<ol>
<li>Hypervisor可能不支持所有的迁移类型，例如QEMU不支持直接迁移</li>
<li>如果迁移可能导致数据损坏，libvirt可能拒绝迁移请求。例如对于QEMU，除非设置磁盘的缓存模式（cache mode）为none或者存储位于一致性的集群文件系统（GFS或者GPFS），迁移会被拒绝。这时可以使用--unsafe强制迁移</li>
</ol>
</td>
</tr>
<tr>
<td>migrate-compcache</td>
<td>
<p><span style="text-decoration: underline;"><em> migrate-compcache domain [--size bytes]<br /></em></span></p>
<p>设置或者取得在线迁移过程中，重复的用来压缩被传输的内存页的缓存的大小，单位字节<br />--size 如果指定此参数，则为设置操作，否则为读取操作</p>
</td>
</tr>
<tr>
<td>migrate-setmaxdowntime</td>
<td>
<p><span style="text-decoration: underline;"><em> migrate-setmaxdowntime domain downtime</em></span></p>
<p>设置domain在线迁移时，能够容忍的最大宕机时间，单位毫秒</p>
</td>
</tr>
<tr>
<td>migrate-compcache</td>
<td> </td>
</tr>
<tr>
<td>migrate-setspeed</td>
<td>
<p><span style="text-decoration: underline;"><em> migrate-setspeed domain bandwidth</em></span></p>
<p>设置domain迁移到其它宿主机上时，最大使用的带宽，单位MiB/s</p>
</td>
</tr>
<tr>
<td>migrate-getspeed</td>
<td>
<p><span style="text-decoration: underline;"><em> migrate-getspeed domain</em></span></p>
<p>获取domain迁移时的最大带宽</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">设备子命令</span></div>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">子命令</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>attach-device</td>
<td>
<p><span style="text-decoration: underline;"><em> attach-device domain FILE [[[--live] [--config] | [--current]] | [--persistent]]</em></span></p>
<p>为domain添加一个新的设备<br />FILE  设备的XML配置，以interface/disk之类的元素为根元素<br />--config   在永久Domian下次启动时生效<br />--live  影响运行中的Domain<br />--current  影响当前Domain状态<br />注意：--config --live可以联用，但是--current不能和它们联用</p>
</td>
</tr>
<tr>
<td>attach-disk</td>
<td>
<p><span style="text-decoration: underline;"><em>attach-disk domain source target [[[--live] [--config] | [--current]] | [--persistent]] [--driver driver] [--subdriver subdriver] [--cache cache] [--type type] [--mode</em></span><br /><span style="text-decoration: underline;"><em> mode] [--config] [--sourcetype soucetype] [--serial serial] [--wwn wwn] [--rawio] [--address address] [--multifunction] [--print-xml]</em></span></p>
<p>添加一个磁盘，参数与磁盘的XML配置元素对应</p>
</td>
</tr>
<tr>
<td>attach-interface</td>
<td>
<p><span style="text-decoration: underline;"><em>attach-interface domain type source [[[--live] [--config] | [--current]] | [--persistent]] [--target target] [--mac mac] [--script script] [--model model] [--config]</em></span><br /><span style="text-decoration: underline;"><em> [--inbound average,peak,burst] [--outbound average,peak,burst]</em></span></p>
<p>添加一个磁盘，参数与网络接口的XML配置元素对应</p>
</td>
</tr>
<tr>
<td>detach-device</td>
<td>
<p><span style="text-decoration: underline;"><em>detach-device domain FILE [[[--live] [--config] | [--current]] | [--persistent]]</em></span></p>
<p>移除一个设备</p>
</td>
</tr>
<tr>
<td>detach-disk</td>
<td>
<p><span style="text-decoration: underline;"><em> detach-disk domain target [[[--live] [--config] | [--current]] | [--persistent]]</em></span></p>
<p>移除一个磁盘</p>
</td>
</tr>
<tr>
<td>detach-interface</td>
<td>
<p><span style="text-decoration: underline;"><em> detach-interface domain type [--mac mac] [[[--live] [--config] | [--current]] | [--persistent]]</em></span></p>
<p>移除一个网络接口</p>
</td>
</tr>
<tr>
<td>update-device</td>
<td>
<p><span style="text-decoration: underline;"><em> update-device domain file [--force] [[[--live] [--config] | [--current]] | [--persistent]]</em></span></p>
<p>更新设备的配置，可用参数：<br />--force  强制设备更新</p>
</td>
</tr>
<tr>
<td>change-media</td>
<td>
<p><span style="text-decoration: underline;"><em> change-media domain path [--eject] [--insert] [--update] [source] [--force] [[--live] [--config] | [--current]]</em></span></p>
<p>改变光驱或者软驱的媒体（盘），可用参数：<br />path  光驱/软驱的全限定路径或者名称，例如hdc<br />source   更换的新媒体镜像的路径<br />--eject  弹出媒介<br />--insert  插入媒介</p>
<p>举例：</p>
<pre class="crayon-plain-tag">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</pre>
</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">虚拟网络子命令</span></div>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">子命令</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>net-autostart</td>
<td>
<p><span style="text-decoration: underline;"><em>net-autostart network [--disable]</em></span>
<p>启用/禁用虚拟网络的自动启动</p>
</td>
</tr>
<tr>
<td>net-create</td>
<td>
<p><span style="text-decoration: underline;"><em>net-create file</em></span></p>
<p>从XML配置创建一个临时的虚拟网络并立即启动</p>
</td>
</tr>
<tr>
<td>net-define</td>
<td>
<p><span style="text-decoration: underline;"><em>net-define file<br /></em></span></p>
<p>从XML配置文件创建一个永久的虚拟网络</p>
</td>
</tr>
<tr>
<td>net-destroy</td>
<td>
<p><span style="text-decoration: underline;"><em>net-destroy network</em></span></p>
<p>根据名称或者UUID停止一个虚拟网络</p>
</td>
</tr>
<tr>
<td>net-dumpxml</td>
<td>
<p><span style="text-decoration: underline;"><em>net-dumpxml network [--inactive]</em></span></p>
<p>导出虚拟网络的XML配置</p>
</td>
</tr>
<tr>
<td>net-edit</td>
<td>
<p><span style="text-decoration: underline;"><em>net-edit network</em></span></p>
<p>编辑一个虚拟网络的配置</p>
</td>
</tr>
<tr>
<td>net-info</td>
<td>
<p><span style="text-decoration: underline;"><em> net-info network</em></span></p>
<p>显示一个虚拟网络的详细信息</p>
</td>
</tr>
<tr>
<td>net-list</td>
<td>
<p><span style="text-decoration: underline;"><em> net-list [--inactive | --all] [--persistent] [&lt;--transient&gt;] [--autostart] [&lt;--no-autostart&gt;]<br /></em></span></p>
<p>显示虚拟网络的列表</p>
</td>
</tr>
<tr>
<td>net-start</td>
<td>
<p><span style="text-decoration: underline;"><em>net-start network</em></span></p>
<p>启动一个虚拟网络</p>
</td>
</tr>
<tr>
<td>net-undefine</td>
<td>
<p><span style="text-decoration: underline;"><em> net-undefine network<br /></em></span></p>
<p>取消虚拟网络的定义</p>
</td>
</tr>
<tr>
<td>net-update</td>
<td>
<p><span style="text-decoration: underline;"><em> net-update network command section xml [--parent-index index] [[--live] [--config] | [--current]]<br /></em></span></p>
<p>更新虚拟网络的指定配置片断，可用参数：<br />section 片断名称：bridge, domain, ip, ip-dhcp-host, ip-dhcp-range, forward, forward-interface, forward-pf, portgroup, dns-host, dns-srv，这些名称对应了虚拟网络XML配置的相应子代元素<br />xml  配置片断，要么是XML片断文本，要么是包含XML片断的文件名称<br />--live  影响正在运行的虚拟网络<br />--config  在永久虚拟网络重启后生效</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">快照子命令</span></div>
<p>这些子命令用来管理Domain的快照，快照是Domain的磁盘、内存、设备在某一个时刻的状态，这些状态可以在未来恢复。每个快照由唯一性的名字来识别。</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">子命令</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>snapshot-create</td>
<td>
<p><span style="text-decoration: underline;"><em>snapshot-create domain [xmlfile] {[--redefine [--current]] | [--no-metadata] [--halt] [--disk-only] [--reuse-external] [--quiesce] [--atomic] [--live]}</em></span></p>
<p>为domain创建一个快照，<span style="background-color: #c0c0c0;">创建快照期间客户机通常处于暂停状态</span>。新创建的快照将成为当前快照，可以通过子命令snapshot-current查看。子命令参数：<br /><em>xmlfile</em>    指定此快照的属性，一般仅仅包含name、description元素，如果指定--disk-only则可以包含disks元素<br /><em>--halt</em>    Domain在创建快照后，进入停止（inactive）状态<br /><em>--disk-only</em>    仅仅对磁盘进行快照，与--halt联用则所有没有flush到磁盘的数据丢失<br /><em>--redefine</em>    如果指定，则snapshot-dumpxml生成的所有元素均有效，可用于跨机器迁移快照层次<br /><em>--no-metadata</em>    丢弃源数据，快照不被标记为current，除非后续使用--redefine，无法恢复快照<br /><em>--reuse-external</em>    重用此文件指向的外部快照，目标文件必须存在<br /><em>--live</em>    创建快照时，客户机将处于运行状态，仅支virsh snapshot-create-as fedora-10 snap2 "snap2" --diskspec=vda,file=$DIR/snap2.vda.qcow2 --disk-only --atomic<br />持外部检查点</p>
</td>
</tr>
<tr>
<td>snapshot-create-as</td>
<td>
<p><span style="text-decoration: underline;"><em>snapshot-create-as domain {[--print-xml] | [--no-metadata] [--halt] [--reuse-external]} [name] [description] [--disk-only [--quiesce]] [--atomic] [[--live] [--memspec memspec]] [--diskspec] diskspec]...</em></span></p>
<p>以指定的name和description创建快照<br /><em>--print-xml</em>    仅创建snapshot-create使用的xmlfile，不进行快照创建<br /><em>--memspec=[file=]name[,snapshot=type]</em>    控制内存快照的创建方式，type可取值none,internal,external<br /><em>--diskspec=disk[,snapshot=type][,driver=type][,file=name]</em>    控制--disk-only和外部检查点创建外部文件的方式。此参数可以出现多次（对应Domain配置中磁盘的个数）<br /><em>--atomic</em>  libvrit保证原子性操作，快照要么完整的创建成功，要么彻底失败</p>
</td>
</tr>
<tr>
<td>snapshot-current</td>
<td>
<p><span style="text-decoration: underline;"><em>snapshot-current domain {[--name] | [--security-info] | [snapshotname]}</em></span></p>
<p>查看和设置当前快照：如果不指定snapshotname，则输出Domain当前快照的XML。否则把snapshotname设置为当前快照<br /><em>--name</em>    仅仅输出当前快照的名称，而非XML<br /><em>--security-info</em>    在XML中包含安全性敏感的信息</p>
</td>
</tr>
<tr>
<td>snapshot-edit</td>
<td>
<p><em><span style="text-decoration: underline;">snapshot-edit domain [snapshotname] [--current] {[--rename] | [--clone]}</span></em></p>
<p>编辑snapshotname的XML信息<br /><em>--current</em>    编辑当前快照，如果同时指定snapshotname，则snapshotname被设置为当前快照<br /><em>--rename</em>    允许设置快照名称<br /><em>--clone</em>    改变快照名称后，会创建一份快照元数据的克隆</p>
</td>
</tr>
<tr>
<td>snapshot-info</td>
<td>
<p><span style="text-decoration: underline;"><em>snapshot-info domain {snapshot | --current}</em></span></p>
<p>输出当前快照或者指定快照的基本信息</p>
</td>
</tr>
<tr>
<td>snapshot-list</td>
<td>
<p><span style="text-decoration: underline;"><em>snapshot-list domain [--metadata] [--no-metadata] [{--parent | --roots | [{--tree | --name}]}] [{[--from] snapshot | --current} [--descendants]] [--leaves] [--no-leaves] p[--inactive] [--active] [--disk-only] [--internal] [--external]</em></span></p>
<p>列出Domain所有可用的快照，默认输出列：快照名称、创建时间、Domain的状态</p>
<p><em>--parent</em>   输出parent列，显示父快照名称<br /><em>--roots</em>    仅列出没有parent快照的那些快照<br /><em>--tree</em>    以树状输出结果，显示快照父子关系<br /><em>--name</em>    仅仅显示快照名称，与--tree互斥<br /><em>--from</em>    仅仅显示指定快照的子快照，联用--current则显示当前快照的子快照<br /><em>--descendants</em>    包含所有后代快照<br /><em>--leaves</em>    仅仅输出没有子代的那些快照<br /><em>--inactive    --active </em>   根据快照中Domain的状态过滤<br /><em>--internal   --external</em>    根据内/外部快照过滤</p>
</td>
</tr>
<tr>
<td>snapshot-dumpxml</td>
<td>
<p><span style="text-decoration: underline;"><em>snapshot-dumpxml domain snapshot [--security-info]<br /></em></span></p>
<p>显示指定快照的XML</p>
</td>
</tr>
<tr>
<td>snapshot-parent</td>
<td>
<p><span style="text-decoration: underline;"><em>snapshot-parent domain {snapshot | --current}</em></span></p>
<p>显示父快照的名称</p>
</td>
</tr>
<tr>
<td>snapshot-revert</td>
<td>
<p><span style="text-decoration: underline;"><em>snapshot-revert domain {snapshot | --current} [{--running | --paused}] [--force]</em></span></p>
<p>恢复Domain到指定的快照状态，此最后一次快照以来对Domain的变更将消失<br /><em>--running    --paused</em> 通常恢复后Domain处于创建快照时的状态，这两个参数可以覆盖之<br /><em>--force</em>    强制恢复快照</p>
</td>
</tr>
<tr>
<td>snapshot-delete</td>
<td>
<p><span style="text-decoration: underline;"><em>snapshot-delete domain {snapshot | --current} [--metadata] [{--children | --children-only}]<br /></em></span></p>
<p>删除Domian的快照<br /><em>--children</em>   删除指定的快照及其子代<br /><em>--children-only</em>    仅仅删除子代，指定的快照本身被保留<br /><em>--metadata</em>    仅仅删除libvirt管理的元数据，不理会快照文件</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">存储池子命令</span></div>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">子命令</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>find-storage-pool-sources</td>
<td>
<p><span style="text-decoration: underline;"><em>find-storage-pool-sources type [srcSpec]</em></span></p>
<p>返回一个XML，描述所有能够找到的type类型的存储池<br />srcSpec  包含额外限制条件的XML</p>
</td>
</tr>
<tr>
<td>find-storage-pool-sources-as</td>
<td>
<p><span style="text-decoration: underline;"><em>find-storage-pool-sources-as type [host] [port] [initiator]</em></span></p>
<p>类似上面，host port initiator限制查询条件</p>
</td>
</tr>
<tr>
<td>pool-autostart</td>
<td>
<p><span style="text-decoration: underline;"><em>pool-autostart pool-or-uuid [--disable]</em></span></p>
<p>配置存储池的自动启动</p>
</td>
</tr>
<tr>
<td>pool-build</td>
<td>
<p><span style="text-decoration: underline;"><em> pool-build pool-or-uuid [--overwrite] [--no-overwrite]</em></span></p>
<p>构建一个存储池<br />--overwrite  --no-overwrite 仅仅用于文件系统池，指定覆盖时，如果文件系统已经存在于目标设备上，mkfs也会执行，既有文件系统将被破坏</p>
</td>
</tr>
<tr>
<td>pool-create</td>
<td>
<p><span style="text-decoration: underline;"><em>pool-create file</em></span></p>
<p>从配置文件定义并启动一个存储池</p>
</td>
</tr>
<tr>
<td>pool-create-as</td>
<td>
<p><span style="text-decoration: underline;"><em>pool-create-as name --print-xml type [source-host] [source-path] [source-dev] [source-name] [&lt;target&gt;] [--source-format format]</em></span></p>
<p>类似上面，但是通过命令行来指定所需参数：<br />--print-xml  打印生成的池的XML配置</p>
</td>
</tr>
<tr>
<td>pool-define</td>
<td>
<p><em><span style="text-decoration: underline;"> pool-define file</span></em></p>
<p>定义，但不启动池</p>
</td>
</tr>
<tr>
<td>pool-define-as</td>
<td>
<p><span style="text-decoration: underline;"><em> pool-define-as name --print-xml type [source-host] [source-path] [source-dev] [source-name] [&lt;target&gt;] [--source-format format]</em></span></p>
<p>类似上面，但是通过命令行来指定所需参数：<br />--print-xml 打印生成的池的XML配置</p>
</td>
</tr>
<tr>
<td>pool-destroy</td>
<td>
<p><span style="text-decoration: underline;"><em>pool-destroy pool-or-uuid</em></span></p>
<p>停止一个存储池，池中的数据不会消失</p>
</td>
</tr>
<tr>
<td>pool-delete</td>
<td>
<p><span style="text-decoration: underline;"><em>pool-delete pool-or-uuid</em></span></p>
<p>销毁池使用的所有资源，但是池本身仍然存在，你可以在其中存储新的卷</p>
</td>
</tr>
<tr>
<td>pool-dumpxml</td>
<td>
<p><span style="text-decoration: underline;"><em>pool-dumpxml [--inactive] pool-or-uuid</em></span></p>
<p>打印池的配置信息</p>
</td>
</tr>
<tr>
<td>pool-edit</td>
<td>
<p><span style="text-decoration: underline;"><em>pool-edit pool-or-uuid</em></span></p>
<p>编辑池的XML配置</p>
</td>
</tr>
<tr>
<td>pool-info</td>
<td>
<p><span style="text-decoration: underline;"><em> pool-info pool-or-uuid<br /></em></span></p>
<p>显示池的基本信息</p>
</td>
</tr>
<tr>
<td>pool-list</td>
<td>
<p><span style="text-decoration: underline;"><em> pool-list [--inactive] [--all] [--persistent] [--transient] [--autostart] [--no-autostart] [[--details] [&lt;type&gt;]</em></span></p>
<p>列出libvirt所知道的全部存储池</p>
</td>
</tr>
<tr>
<td>pool-name<br />pool-uuid</td>
<td>
<p><span style="text-decoration: underline;"><em>pool-name uuid <br />pool-uuid pool</em></span></p>
<p>显示指定uuid对应的池名称，或者显示池名称对应的UUID </p>
</td>
</tr>
<tr>
<td>pool-refresh</td>
<td>
<p><span style="text-decoration: underline;"><em>pool-refresh pool-or-uuid</em></span></p>
<p>刷新池中卷的列表</p>
</td>
</tr>
<tr>
<td>pool-start</td>
<td>
<p><span style="text-decoration: underline;"><em> pool-start pool-or-uuid</em></span></p>
<p>启动一个存储池</p>
</td>
</tr>
<tr>
<td>pool-undefine</td>
<td>
<p><span style="text-decoration: underline;"><em> pool-undefine pool-or-uuid</em></span></p>
<p>解除一个存储池的定义</p>
</td>
</tr>
<tr>
<td>vol-create</td>
<td>
<p><span style="text-decoration: underline;"><em>vol-create pool-or-uuid FILE [--prealloc-metadata]</em></span></p>
<p>在池中创建一个卷，对于基于目录/文件系统的池，卷的本质就是一个镜像文件。可用参数：<br />pool-or-uuid 池的名称或者UUID<br />FILE  卷的配置XML<br />--prealloc-metadata  预分配元数据，用于qcow2之类不支持完全分配的镜像格式，该参数可以提高性能</p>
<p>示例：<pre class="crayon-plain-tag">vol-create default definitions/volumes/fedora-10.xml</pre> </p>
</td>
</tr>
<tr>
<td>vol-create-from</td>
<td>
<p><span style="text-decoration: underline;"><em> vol-create-from pool-or-uuid FILE [--inputpool pool-or-uuid] vol-name-or-key-or-path [--prealloc-metadata]<br /></em></span></p>
<p>创建一个卷，使用另外一个卷vol-name-or-key-or-path作为输入</p>
</td>
</tr>
<tr>
<td>vol-create-as</td>
<td>
<p><span style="text-decoration: underline;"><em>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]<br /></em></span></p>
<p>创建一个卷，从命令行参数读取配置信息</p>
<pre class="crayon-plain-tag">virsh vol-create-as v12n1 centos7-base 128G --format qcow2</pre>
</td>
</tr>
<tr>
<td>vol-clone</td>
<td>
<p><span style="text-decoration: underline;"><em>vol-clone [--pool pool-or-uuid] vol-name-or-key-or-path name [--prealloc-metadata]</em></span>
<p>克隆一个既有的卷，没有vol-create-from强大但是比它简单。可用参数：<br />--pool  指定在其中创建新卷的池<br />vol-name-or-key-or-path  被克隆的卷<br />name  新的卷的名称</p>
</td>
</tr>
<tr>
<td>vol-delete</td>
<td>
<p><span style="text-decoration: underline;"><em> vol-delete [--pool pool-or-uuid] vol-name-or-key-or-path<br /></em></span></p>
<p>删除一个卷，底层的镜像文件将被删除，示例：</p>
<pre class="crayon-plain-tag">vol-delete --pool default fedora-10.qcow2</pre>
</td>
</tr>
<tr>
<td>vol-upload</td>
<td>
<p><span style="text-decoration: underline;"><em> vol-upload [--pool pool-or-uuid] [--offset bytes] [--length bytes] vol-name-or-key-or-path local-file<br /></em></span>
<p>上传本地文件的内容到一个卷，可用参数：<br />--pool  目标存储池<br />vol-name-or-key-or-path  目标卷<br />local-file  被上传的本地文件<br />--offset  在存储卷的什么位置开始写入数据<br />--length  写入数据的长度</p>
</td>
</tr>
<tr>
<td>vol-download</td>
<td>
<p><span style="text-decoration: underline;"><em> vol-download [--pool pool-or-uuid] [--offset bytes] [--length bytes] vol-name-or-key-or-path local-file<br /></em></span></p>
<p>下载存储卷中的内容到本地文件</p>
</td>
</tr>
<tr>
<td>vol-wipe</td>
<td>
<p><span style="text-decoration: underline;"><em> vol-wipe [--pool pool-or-uuid] [--algorithm algorithm] vol-name-or-key-or-path<br /></em></span></p>
<p>擦除一个卷的内容，可用参数：<br />--algorithm  擦除算法：zero nnsa dod bsi gutmann schneier pfitzner7 pfitzner33 random</p>
</td>
</tr>
<tr>
<td>vol-dumpxml</td>
<td>
<p><span style="text-decoration: underline;"><em> vol-dumpxml [--pool pool-or-uuid] vol-name-or-key-or-path<br /></em></span></p>
<p>打印卷的XML配置，示例：</p>
<pre class="crayon-plain-tag">vol-dumpxml --pool default coreos.qcow2</pre>
</td>
</tr>
<tr>
<td>vol-info</td>
<td>
<p><span style="text-decoration: underline;"><em> vol-info [--pool pool-or-uuid] vol-name-or-key-or-path<br /></em></span>
<p>显示卷的基本信息</p>
</td>
</tr>
<tr>
<td>vol-list</td>
<td>
<p><span style="text-decoration: underline;"><em> vol-list [--pool pool-or-uuid] [--details]<br /></em></span></p>
<p>列出一个存储池中的卷，示例：</p>
<pre class="crayon-plain-tag">vol-list default</pre>
</td>
</tr>
<tr>
<td>vol-pool</td>
<td>
<p><span style="text-decoration: underline;"><em> vol-pool [--uuid] vol-key-or-path<br /></em></span>
<p>根据卷的名称或者路径，返回其所在存储池的信息<br />--uuid 返回UUID而不是池名称</p>
</td>
</tr>
<tr>
<td>vol-path</td>
<td>
<p><span style="text-decoration: underline;"><em> vol-path [--pool pool-or-uuid] vol-name-or-key<br /></em></span></p>
<p>返回指定卷的路径</p>
</td>
</tr>
<tr>
<td>vol-name<br />vol-key</td>
<td>
<p><span style="text-decoration: underline;"><em> vol-name vol-key-or-path<br /> vol-key [--pool pool-or-uuid] vol-name-or-path<br /></em></span></p>
<p>查询卷的名称或者key</p>
</td>
</tr>
<tr>
<td>vol-resize</td>
<td>
<p><span style="text-decoration: underline;"><em> vol-resize [--pool pool-or-uuid] vol-name-or-path pool-or-uuid capacity [--allocate] [--delta] [--shrink]<br /></em></span></p>
<p>重新设定卷的容量，以字节为单位，可用参数：<br />--allocate  分配空间，否则新容量是稀疏的<br />capacity  新的容量，如果指定了--delta，则是增加的容量<br />--shrink  缩小卷大小，不指定此参数，降低容量会报错</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">结合QEMU/KVM</span></div>
<p>模拟器QEMU和hypervisor KVM可以被libvirt管理。</p>
<div class="blog_h2"><span class="graybg">QEMU驱动</span></div>
<p>如果driver检测到/usr/bin/qemu-system-*则QEMU可用；如果driver检测到设备节点/dev/kvm和可执行文件/usr/bin/qemu-kvm则支持KVM全虚拟化和客户机硬件加速。</p>
<p>QEMU的驱动是一个多实例驱动，包含一个系统级别的特权驱动（system实例）和多个用户级别的非特权驱动。驱动的URI的协议名为qemu，URI示例：</p>
<pre class="crayon-plain-tag"># 本地访问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</pre>
<div class="blog_h2"><span class="graybg">导入/出QEMU配置</span></div>
<div class="blog_h3"><span class="graybg">导入</span></div>
<p><pre class="crayon-plain-tag">virsh domxml-from-native</pre> 命令可以将QEMU命令行选项转换为libvirt的Domain配置格式：</p>
<ol>
<li>将QEMU命令行保存到文件qemu.cmd：<pre class="crayon-plain-tag">/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</pre> </li>
<li>执行命令：<pre class="crayon-plain-tag">virsh domxml-from-native qemu-argv ~/Vmware/KVM/fedora-10/qemu.cmd ~/Vmware/KVM/fedora-10/domain.xml</pre> </li>
</ol>
<p>生成的配置文件内容如下：</p>
<pre class="crayon-plain-tag">&lt;domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'&gt;
  &lt;name&gt;fedora-10&lt;/name&gt;
  &lt;uuid&gt;51480ab5-864e-4eb7-9e1c-55b56105139e&lt;/uuid&gt;
  &lt;memory unit='KiB'&gt;524288&lt;/memory&gt;
  &lt;currentMemory unit='KiB'&gt;524288&lt;/currentMemory&gt;
  &lt;vcpu placement='static'&gt;1&lt;/vcpu&gt;
  &lt;os&gt;
    &lt;type arch='x86_64' machine='pc'&gt;hvm&lt;/type&gt;
  &lt;/os&gt;
  &lt;features&gt;
    &lt;acpi/&gt;
  &lt;/features&gt;
  &lt;cpu mode='custom' match='exact'&gt;
    &lt;model fallback='allow'&gt;Haswell&lt;/model&gt;
  &lt;/cpu&gt;
  &lt;clock offset='utc'/&gt;
  &lt;on_poweroff&gt;destroy&lt;/on_poweroff&gt;
  &lt;on_reboot&gt;restart&lt;/on_reboot&gt;
  &lt;on_crash&gt;destroy&lt;/on_crash&gt;
  &lt;devices&gt;
    &lt;emulator&gt;/usr/bin/qemu-system-x86_64&lt;/emulator&gt;
    &lt;disk type='file' device='disk'&gt;
      &lt;driver name='qemu' type='raw'/&gt;
      &lt;source file='/home/alex/Vmware/KVM/fedora-10/hda.img'/&gt;
      &lt;target dev='vda' bus='virtio'/&gt;
    &lt;/disk&gt;
    &lt;controller type='usb' index='0'/&gt;
    &lt;controller type='pci' index='0' model='pci-root'/&gt;
    &lt;input type='mouse' bus='ps2'/&gt;
    &lt;input type='keyboard' bus='ps2'/&gt;
    &lt;graphics type='sdl'/&gt;
    &lt;video&gt;
      &lt;model type='cirrus' vram='9216' heads='1'/&gt;
    &lt;/video&gt;
    &lt;memballoon model='virtio'/&gt;
  &lt;/devices&gt;
  &lt;qemu:commandline&gt;
    &lt;qemu:arg value='-daemonize'/&gt;
    &lt;qemu:arg value='-display'/&gt;
    &lt;qemu:arg value='none'/&gt;
    &lt;qemu:arg value='-netdev'/&gt;
    &lt;qemu:arg value='bridge,id=tap0,br=br0'/&gt;
    &lt;qemu:arg value='-device'/&gt;
    &lt;qemu:arg value='virtio-net-pci,netdev=tap0,mac=DE:AD:BE:EF:F1:00'/&gt;
  &lt;/qemu:commandline&gt;
&lt;/domain&gt;</pre>
<p>可以看到，很多QEMU选项没有对应到常规的Domain配置元素，而是使用qemu:commandline的形式，在启动客户机的时候直接传递给QEMU了。因此，新建客户机时，不要使用这种导入配置的方法，而应调用libvirt API或者手工创建Domain的XML配置。</p>
<p>注意：virsh自动导入得到XML配置存在不少错误，需要调整后才能使用。上例修改后的配置如下：</p>
<pre class="crayon-plain-tag">&lt;domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'&gt;
    &lt;name&gt;fedora-10&lt;/name&gt;
    &lt;memory unit='KiB'&gt;524288&lt;/memory&gt;
    &lt;currentMemory unit='KiB'&gt;524288&lt;/currentMemory&gt;
    &lt;vcpu placement='static'&gt;1&lt;/vcpu&gt;
    &lt;os&gt;
        &lt;type arch='x86_64' machine='pc'&gt;hvm&lt;/type&gt;
    &lt;/os&gt;
    &lt;features&gt;
        &lt;acpi/&gt;
    &lt;/features&gt;
    &lt;cpu mode='custom' match='exact'&gt;
        &lt;model fallback='allow'&gt;SandyBridge&lt;/model&gt;
    &lt;/cpu&gt;
    &lt;clock offset='utc'/&gt;
    &lt;on_poweroff&gt;destroy&lt;/on_poweroff&gt;
    &lt;on_reboot&gt;restart&lt;/on_reboot&gt;
    &lt;on_crash&gt;destroy&lt;/on_crash&gt;
    &lt;devices&gt;
        &lt;emulator&gt;/usr/bin/qemu-system-x86_64&lt;/emulator&gt;
        &lt;disk type='file' device='disk'&gt;
            &lt;driver name='qemu' type='qcow2'/&gt; &lt;!-- 镜像格式要设置对 --&gt;
            &lt;source file='/home/alex/Vmware/KVM/fedora-10/hda.img'/&gt;
            &lt;target dev='vda' bus='virtio'/&gt;
            &lt;boot order='1'/&gt;  &lt;!-- 要指定启动顺序，否则不会从此硬盘启动系统 --&gt;
        &lt;/disk&gt;
        &lt;controller type='usb' index='0'/&gt;
        &lt;controller type='pci' index='0' model='pci-root'/&gt;
        &lt;!-- 把基于qemu:commandline的网络配置改为标准的Domain配置方式 --&gt;
        &lt;interface type='bridge'&gt;
            &lt;mac address='DE:AD:BE:EF:F1:00'/&gt;
            &lt;source bridge='br0'/&gt;
            &lt;target dev='tap0'/&gt;
            &lt;model type='virtio'/&gt;
        &lt;/interface&gt; 
        &lt;!-- 添加串口控制台配置，去掉视频相关配置 --&gt;
        &lt;serial type='pty'&gt;
            &lt;target port='0'/&gt;
        &lt;/serial&gt;
        &lt;console type='pty'&gt;
            &lt;target type='serial' port='0'/&gt;
        &lt;/console&gt;
        &lt;input type='mouse' bus='ps2'/&gt;
        &lt;input type='keyboard' bus='ps2'/&gt;
        &lt;memballoon model='virtio'/&gt;
    &lt;/devices&gt;
&lt;/domain&gt; </pre>
<div class="blog_h3"><span class="graybg">导出</span></div>
<p>类似的，可以把Domain配置文件转换为QEMU命令行：</p>
<pre class="crayon-plain-tag">virsh domxml-to-native qemu-argv ~/Vmware/KVM/fedora-10/domain.xml</pre>
<div class="blog_h1"><span class="graybg">Domain配置文件</span></div>
<div class="blog_h2"><span class="graybg">基本信息</span></div>
<p>libvirt使用XML文件描述一个Domain的全部配置信息：</p>
<pre class="crayon-plain-tag">&lt;!--
任何Domain配置的根元素都是domin，它有两个属性：
    type 驱动（hypervisor）的类型，可选值：xen | kvm | qemu | lxc | kqemu
    id 正在运行的Domain的唯一标识，整数。非活动Domain没有id
--&gt;
&lt;domain type='kvm' id='1'&gt;
    &lt;!-- 在单个node下唯一的虚拟机名字 --&gt;
    &lt;name&gt;fedora-10&lt;/name&gt;
    &lt;!-- RFC 4122兼容的、虚拟机的全局唯一标识，如果在定义/创建虚拟机时不指定，则会自动生成一个  --&gt;
    &lt;uuid&gt;4dea22b31d52d8f32516782e98ab3fa0&lt;/uuid&gt;
    &lt;!-- 虚拟机的简短描述，可以多行 --&gt;
    &lt;title&gt;A short description&lt;/title&gt;
    &lt;!-- 虚拟机的描述--&gt;
    &lt;description&gt;Some human readable description&lt;/description&gt;
    &lt;!-- 元数据：由应用程序使用，子树必须使用应用程序自己的名字空间 --&gt;
    &lt;metadata&gt;
        &lt;app1:foo xmlns:app1="http://app1.org/app1/"&gt;..&lt;/app1:foo&gt;
        &lt;app2:bar xmlns:app2="http://app1.org/app2/"&gt;..&lt;/app2:bar&gt;
    &lt;/metadata&gt;
&lt;/domain&gt;</pre>
<p>下面介绍如何配置Domain各方面的细节。</p>
<div class="blog_h2"><span class="graybg">Domain启动配置</span></div>
<p>虚拟机可以不同的方式启动，各有其优缺点。</p>
<div class="blog_h3"><span class="graybg">BIOS bootloader</span></div>
<p>对于全虚拟化的hypervisor可以选择通过BIOS启动，BIOS定义启动优先级，来确定从软盘、硬盘、光驱还是网络获取启动镜像（boot image）。配置示例：</p>
<pre class="crayon-plain-tag">&lt;os&gt;
    &lt;type&gt;hvm&lt;/type&gt;
    &lt;loader readonly='yes' secure='no' type='rom'&gt;/usr/lib/xen/boot/hvmloader&lt;/loader&gt;
    &lt;nvram template='/usr/share/OVMF/OVMF_VARS.fd'&gt;/var/lib/libvirt/nvram/guest_VARS.fd&lt;/nvram&gt;
    &lt;boot dev='hd'/&gt;
    &lt;boot dev='cdrom'/&gt;
    &lt;bootmenu enable='yes' timeout='3000'/&gt;
    &lt;smbios mode='sysinfo'/&gt;
    &lt;bios useserial='yes' rebootTimeout='0'/&gt;
&lt;/os&gt;</pre>
<p>各子元素的说明如下：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">元素</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>type</td>
<td>
<p>指定需要被启动的虚拟机操作系统的类型：<br />hvm    操作系统设计用来在裸金属（bare metal）上运行，即未修改的操作系统，需要全虚拟化<br />xen     表示支持Xen3的Guest API的操作系统，别名linux<br />exe    基于容器的虚拟化<br />uml   用户模式的Linux</p>
<p>元素属性：<br />arch    指定被虚拟化的CPU的体系结构<br />machine    指定机器类型</p>
</td>
</tr>
<tr>
<td>loader</td>
<td>
<p>指定虚拟机固件镜像的（宿主机的）绝对路径。用于Xen全虚拟化、QEMU/KVM的BIOS文件路径设置</p>
<p>元素属性：<br />readonly    固件是否只读，yes/no<br />type    rom/pflash，告知hypervisor把固件映射到客户机内存的什么位置，如果loader指定UEFI镜像，则该属性应为pflash<br />secure    指示固件实现了安全启动（secure boot）特性</p>
</td>
</tr>
<tr>
<td>boot</td>
<td>
<p>此元素可以出现多次，其dev属性可以是fd/hd/cdrom/network，用来确定优先从哪种设备启动虚拟机，写在最前面的那种设备优先级高</p>
<p>如果同一类型的设备配置了多个，它们将依据总线顺序排列，第一个被标记为可启动的。该元素难以细粒度的控制启动顺序，可以使用Per-device的boot元素代替（后者与此元素互斥）</p>
</td>
</tr>
<tr>
<td>smbios</td>
<td>产生客户机可见的SMBios信息，引用一个sysinfo元素</td>
</tr>
<tr>
<td>bootmenu</td>
<td>可以使用一个交互式的启动菜单</td>
</tr>
<tr>
<td>bios</td>
<td>useserial：可以设置为yes/no，来启用/禁用Serial Graphics Adapter ，SGA允许用户通过串口看到BIOS信息<br />rebootTimeout   如果启动失败，多久重启，单位毫秒，-1禁止重启</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">Container boot<br /></span></div>
<p>当启动基于容器虚拟化的Domain时，需要指定一个init程序：</p>
<pre class="crayon-plain-tag">&lt;os&gt;
    &lt;type arch='x86_64'&gt;exe&lt;/type&gt;
    &lt;!-- init binary --&gt;
    &lt;init&gt;/bin/systemd&lt;/init&gt;
    &lt;!-- argumsnts --&gt;
    &lt;initarg&gt;--unit&lt;/initarg&gt;
    &lt;initarg&gt;emergency.service&lt;/initarg&gt;
&lt;/os&gt;</pre>
<p>如果你要启用user namespace映射，可以：</p>
<pre class="crayon-plain-tag">&lt;idmap&gt;
    &lt;!-- start容器内第1个用户的ID，target容器内第1个用户映射到宿主机的用户的ID，count容器最多映射宿主机多少用户--&gt;
    &lt;uid start='0' target='1000' count='10'/&gt;
    &lt;gid start='0' target='1000' count='10'/&gt;
&lt;/idmap&gt;</pre>
<div class="blog_h2"><span class="graybg">指定SMBIOS信息</span></div>
<pre class="crayon-plain-tag">&lt;os&gt;
    &lt;smbios mode='sysinfo'/&gt;
&lt;/os&gt;
&lt;!-- type属性必须，它的值决定子元素第布局--&gt;
&lt;sysinfo type='smbios'&gt;
    &lt;bios&gt;
        &lt;entry name='vendor'&gt;LENOVO&lt;/entry&gt;
    &lt;/bios&gt;
    &lt;system&gt;
        &lt;entry name='manufacturer'&gt;Fedora&lt;/entry&gt;
        &lt;entry name='product'&gt;Virt-Manager&lt;/entry&gt;
        &lt;entry name='version'&gt;0.9.4&lt;/entry&gt;
    &lt;/system&gt;
    &lt;baseBoard&gt;
        &lt;entry name='manufacturer'&gt;LENOVO&lt;/entry&gt;
        &lt;entry name='product'&gt;20BE0061MC&lt;/entry&gt;
        &lt;entry name='version'&gt;0B98401 Pro&lt;/entry&gt;
        &lt;entry name='serial'&gt;W1KS427111E&lt;/entry&gt;
    &lt;/baseBoard&gt;
&lt;/sysinfo&gt;</pre>
<div class="blog_h2"><span class="graybg">CPU分配</span></div>
<p>配置示例如下：</p>
<pre class="crayon-plain-tag">&lt;vcpu placement='static' cpuset="1-4,^3,6" current="1"&gt;2&lt;/vcpu&gt;
&lt;vcpus&gt;
    &lt;vcpu id='0' enabled='yes' hotpluggable='no' order='1'/&gt;
    &lt;vcpu id='1' enabled='no' hotpluggable='yes'/&gt;
&lt;/vcpus&gt;</pre>
<div class="blog_h3"><span class="graybg">vcpu元素</span></div>
<p>此元素定义客户机<span style="background-color: #c0c0c0;">最大的虚拟CPU的数量</span>，有效值的范围是1-hypervisor支持的最大数量。属性说明如下：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">属性</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>cpuset</td>
<td>
<p>逗号分隔的，Domain进程及虚拟CPU默认能够Pin到的物理CPU序号。可以用-指定范围，^进行排除</p>
<p>Domain进程及虚拟CPU的Pin策略可以由cputune指定，如果cputune的emulatorpin属性被设置，则当前属性被忽略<br />对于指定了vcpupin的虚拟CPU，当前属性被忽略</p>
</td>
</tr>
<tr>
<td>current</td>
<td>
<p>启用比最大数量更少的虚拟CPU</p>
</td>
</tr>
<tr>
<td>placement</td>
<td>
<p>指定Domain进程的CPU placement mode，static/auto</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">vcpus</span></div>
<p>此元素控制每个单独虚拟CPU的状态，每个vcpu子元素对应一个虚拟CPU，vcpu子元素的属性说明如下：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">属性</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>id</td>
<td>虚拟CPU的标识符，libvirt在其它地方（例如pinning）引用之。有效值范围0到最大虚拟CPU数-1之间</td>
</tr>
<tr>
<td>enabled</td>
<td>控制此虚拟CPU是否启用，yes/no</td>
</tr>
<tr>
<td>hotpluggable</td>
<td>此虚拟CPU是否可以热拔插，注意，所有enabled=no的CPU都是可以热拔插的，yes/no</td>
</tr>
<tr>
<td>order</td>
<td>此虚拟CPU的顺序号，此值越小，则CPU越先被热插</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">CPU微调</span></div>
<p>cputune元素可以对Domain的虚拟CPU进行微调，配置示例如下：</p>
<pre class="crayon-plain-tag">&lt;cputune&gt;
    &lt;vcpupin vcpu="0" cpuset="1-4,^2"/&gt;
    &lt;vcpupin vcpu="1" cpuset="0,1"/&gt;
    &lt;vcpupin vcpu="2" cpuset="2,3"/&gt;
    &lt;vcpupin vcpu="3" cpuset="0,4"/&gt;
    &lt;emulatorpin cpuset="1-3"/&gt;
    &lt;iothreadpin iothread="1" cpuset="5,6"/&gt;
    &lt;iothreadpin iothread="2" cpuset="7,8"/&gt;
    &lt;shares&gt;2048&lt;/shares&gt;
    &lt;period&gt;1000000&lt;/period&gt;
    &lt;quota&gt;-1&lt;/quota&gt;
    &lt;emulator_period&gt;1000000&lt;/emulator_period&gt;
    &lt;emulator_quota&gt;-1&lt;/emulator_quota&gt;
    &lt;iothread_period&gt;1000000&lt;/iothread_period&gt;
    &lt;iothread_quota&gt;-1&lt;/iothread_quota&gt;
    &lt;vcpusched vcpus='0-4,^3' scheduler='fifo' priority='1'/&gt;
    &lt;iothreadsched iothreads='2' scheduler='batch'/&gt;
&lt;/cputune&gt;</pre>
<p>各子元素说明如下：</p>
<table class=" fixed-word-wrap full-width">
<thead>
<tr>
<td style="width: 20%; text-align: center;">元素</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>vcpupin</td>
<td>指定虚拟CPU与物理CPU之间的Pin关系。不指定的情况下，虚拟CPU可以Pin到所有物理CPU</td>
</tr>
<tr>
<td>emulatorpin</td>
<td>模拟器线程被Pin到哪些物理CPU，模拟器线程是Domain执行序列中除了虚拟CPU、IO线程之外的部分<br />如果此属性未指定，且vcpu的cpuset属性没有设置，则默认Pin到所有物理CPU</td>
</tr>
<tr>
<td>iothreadpin</td>
<td>IO线程被Pin到哪些物理CPU<br />如果此属性未指定，且vcpu的cpuset属性没有设置，则默认Pin到所有物理CPU</td>
</tr>
<tr>
<td>shares</td>
<td>此Domain占用CPU时间的权重，这是一个相对值，它对应的具体时长取决于其它虚拟机的设置<br />2048比1024多获得1倍CPU时间</td>
</tr>
<tr>
<td>period</td>
<td>
<p>用来指定强制的interval，单位毫秒，可取值 [1000, 1000000]之间。在一个period内，Domain中的每个虚拟CPU消耗的带宽不得超过quota</p>
<p>仅QEMU 0.9.4+、LXC 0.9.10+支持</p>
</td>
</tr>
<tr>
<td>quota</td>
<td>用来指定最大允许带宽，单位毫秒，可取值 [1000, 18446744073709551]，负值表示不限制</td>
</tr>
<tr>
<td>emulator_period</td>
<td rowspan="2">与上面类似，但是针对模拟器线程</td>
</tr>
<tr>
<td>emulator_quota</td>
</tr>
<tr>
<td>iothread_period</td>
<td rowspan="2">与上面类似，但是针对IO线程</td>
</tr>
<tr>
<td>iothread_quota</td>
</tr>
<tr>
<td>vcpusched</td>
<td>指定特定虚拟CPU的调度类型：<br />scheduler，调度类型，可选batch, idle, fifo, rr<br />vcpus，针对的虚拟CPU<br />priority，对于实时调度器fifo, rr必须，值范围一般1-99之间，取决于宿主机内核</td>
</tr>
<tr>
<td>iothreadsched</td>
<td>与上面类似</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">IO线程分配</span></div>
<p>IO线程是一种专门的事件循环线程，用于提高磁盘Block I/O的scalability，这些线程会分配给支持的磁盘设备。每个物理CPU只有1-2个IO线程，每个IO线程也可能分配给多个磁盘设备。配置示例：</p>
<pre class="crayon-plain-tag">&lt;!-- 分配给Domain使用的IO线程数--&gt;
&lt;iothreads&gt;4&lt;/iothreads&gt;

&lt;!-- 定义每个IO线程的ID --&gt;
&lt;iothreadids&gt;
    &lt;iothread id="2"/&gt;
    &lt;iothread id="4"/&gt;
    &lt;iothread id="6"/&gt;
    &lt;iothread id="8"/&gt;
&lt;/iothreadids&gt;</pre>
<div class="blog_h2"><span class="graybg">内存分配</span></div>
<p>配置示例如下：</p>
<pre class="crayon-plain-tag">&lt;maxMemory slots='16' unit='KiB'&gt;1524288&lt;/maxMemory&gt;
&lt;memory unit='KiB'&gt;524288&lt;/memory&gt;
&lt;currentMemory unit='KiB'&gt;524288&lt;/currentMemory&gt;</pre>
<p>各元素说明如下：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">元素</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>memory</td>
<td>在启动时，分配给Domain的内存的数量<br />unit，单位，Ki按1024，K按1000计，可用B|KB|MB|GB|TB<br />dumpCore，在Domain崩溃后，是否包含其内存映像到生成的coredump中，仅QEMU</td>
</tr>
<tr>
<td>maxMemory</td>
<td>运行期间允许的最大内存，仅QEMU</td>
</tr>
<tr>
<td>currentMemory</td>
<td>当前实际分配给Domain的内存，默认与memory相同</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">内存Backing</span></div>
<p>memoryBacking元素控制虚拟内存页如何映射到宿主机的内存页，配置示例：</p>
<pre class="crayon-plain-tag">&lt;memoryBacking&gt;
    &lt;hugepages&gt;
        &lt;!-- 为了除4之外的numa节点分配1G的巨页 --&gt;
        &lt;page size="1" unit="G" nodeset="0-3,5"/&gt;
        &lt;page size="2" unit="M" nodeset="4"/&gt;
    &lt;/hugepages&gt;
    &lt;nosharepages/&gt;
    &lt;locked/&gt;
&lt;/memoryBacking&gt;</pre>
<p>子元素说明如下：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">元素</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>hugepages</td>
<td>
<p>在Linux 64位系统里面，默认内存是以<span style="background-color: #c0c0c0;">4K的页面（Page）</span>来管理的，当系统有非常多的内存的时候，管理这些内存的消耗就比较大。HugePage使用<span style="background-color: #c0c0c0;">2M大小的页面来减小管理开销</span>。<span style="background-color: #c0c0c0;">HugePage管理的内存并不能被Swap</span>，这就避免了swap引发的性能问题。如果系统经常碰到因为swap引发的性能问题可以考虑启用HugePage</p>
<p>告知hypervisor，客户机的内存基于hugepage而不是宿主机Native页大小来分配。从1.2.5开始，可以为每个numa节点更加细致的设置huagepages：<br />size/unit    指定huge页的大小<br />nodeset    给于特定numa节点hugepage</p>
</td>
</tr>
<tr>
<td>nosharepages</td>
<td>nosharepages，用于提示hypervisor禁止此Domain的共享页面（内存合并，KSM）</td>
</tr>
<tr>
<td>locked</td>
<td>
<p>如果hypervisor支持，设置此元素可以禁止属于Domain的内存页被swap out</p>
<p>对于QEMU/KVM，使用此设置前你需要设置memtune的hard_limit，并且设置maxMemory=Domain所需内存+QEMU进程本身所需内存。注意：启用locked且设置过多的内存可能导致宿主机内核内存溢出</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">内存微调</span></div>
<p>memtune提供Domain的内存微调参数，如果不设置这些参数，则使用OS提供的默认值。对于QEMU/KVM，这些参数限制包含QEMU进程本身的内存消耗</p>
<pre class="crayon-plain-tag">&lt;memtune&gt;
    &lt;hard_limit unit='G'&gt;1&lt;/hard_limit&gt;
    &lt;soft_limit unit='M'&gt;128&lt;/soft_limit&gt;
    &lt;swap_hard_limit unit='G'&gt;2&lt;/swap_hard_limit&gt;
    &lt;min_guarantee unit='bytes'&gt;67108864&lt;/min_guarantee&gt;
&lt;/memtune&gt;</pre>
<p>子元素说明如下：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">元素</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>hard_limit</td>
<td>限制客户机能够使用的最大内存，对于QEMU/KVM建议不要设置</td>
</tr>
<tr>
<td>soft_limit</td>
<td>出现内存争用时的软限制</td>
</tr>
<tr>
<td>swap_hard_limit</td>
<td>最大内存 + 交换文件总大小限制</td>
</tr>
<tr>
<td>min_guarantee</td>
<td>确保最小低分配给客户机的内存，仅VMware ESX、OpenVZ支持</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">NUMA节点微调</span></div>
<p>numatune元素通过控制针对Domain进程的numa策略来影响宿主机的性能，配置示例如下：</p>
<pre class="crayon-plain-tag">&lt;numatune&gt;
    &lt;memory mode="strict" nodeset="1-4,^3"/&gt;
    &lt;memnode cellid="0" mode="strict" nodeset="1"/&gt;
    &lt;memnode cellid="2" mode="preferred" nodeset="2"/&gt;
&lt;/numatune&gt;</pre>
<p>子元素说明如下：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">元素</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>memory</td>
<td>如何在numa主机上为Domain分配内存：<br />mode，可选值interleave,strict,preferred，默认strict<br />nodeset，影响的numa节点</td>
</tr>
<tr>
<td>memnode</td>
<td>类似，针对单个numa节点设置</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">块I/O微调</span></div>
<p>blkiotune元素能够微调Domain的Blkio cgroup可调整参数，如果不指定此元素，则使用OS默认值。配置示例如下：</p>
<pre class="crayon-plain-tag">&lt;blkiotune&gt;
    &lt;weight&gt;800&lt;/weight&gt;
    &lt;device&gt;
        &lt;path&gt;/dev/sda&lt;/path&gt;
        &lt;weight&gt;1000&lt;/weight&gt;
    &lt;/device&gt;
    &lt;device&gt;
        &lt;path&gt;/dev/sdb&lt;/path&gt;
        &lt;weight&gt;500&lt;/weight&gt;
        &lt;read_bytes_sec&gt;10000&lt;/read_bytes_sec&gt;
        &lt;write_bytes_sec&gt;10000&lt;/write_bytes_sec&gt;
        &lt;read_iops_sec&gt;20000&lt;/read_iops_sec&gt;
        &lt;write_iops_sec&gt;20000&lt;/write_iops_sec&gt;
    &lt;/device&gt;
&lt;/blkiotune&gt;</pre>
<p>子元素说明如下：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">元素</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>weight</td>
<td>Domain的整体I/O权重，值范围[100, 1000]，自2.6.39内核之后，值范围[10, 1000]</td>
</tr>
<tr>
<td>device</td>
<td>
<p>此元素可以有多个，用来设置Domain针对宿主机每一个块设备的I/O权重。子元素：<br />path    宿主机块设备文件的路径<br />weight    针对此块设备，Domain的权重<br />read_bytes_sec    读吞吐量限制，bytes/s<br />write_bytes_sec    写吞吐量限制，bytes/s<br />read_iops_sec    读次数限制，bytes/s<br />write_iops_sec    写次数限制，bytes/s</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">CPU型号与拓扑</span></div>
<p>对CPU型号、特性的要求，以及它的拓扑结构的要求，可以使用如下方式配置：</p>
<pre class="crayon-plain-tag">&lt;cpu match='exact'&gt;
    &lt;model fallback='allow'&gt;core2duo&lt;/model&gt;
    &lt;vendor&gt;Intel&lt;/vendor&gt;
    &lt;topology sockets='1' cores='2' threads='1'/&gt;
    &lt;feature policy='disable' name='lahf_lm'/&gt;
&lt;/cpu&gt;

&lt;cpu mode='host-model'&gt;
    &lt;model fallback='forbid'/&gt;
    &lt;topology sockets='1' cores='2' threads='1'/&gt;
&lt;/cpu&gt;

&lt;cpu mode='host-passthrough'&gt;
    &lt;feature policy='disable' name='lahf_lm'/&gt;
&lt;/cpu&gt;</pre>
<p>cpu元素是描述客户机CPU需求的容器元素，它的属性如下：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">属性</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>match</td>
<td>宿主机必须满足客户机CPU需求的严格程度，可选值：<br />minimum    满足CPU型号、feature的要求<br />exact    完全满足<br />strict    除非完全满足，否则客户机不被创建</td>
</tr>
<tr>
<td>mode<a id="cpu-mode"></a></td>
<td>
<p>用于简化客户机的配置，让它尽量和宿主机CPU匹配。可选值：</p>
<ol style="list-style-type: undefined;">
<li>custom（94.73%性能）：这种模式下虚拟机 CPU 指令集数最少，故性能相对最差，但是它在热迁移时跨不同型号 CPU 的能力最强。此外，custom 模式下支持用户添加额外的指令集</li>
<li>host-model（95.84%性能）：libvirt 根据当前宿主机 CPU 指令集从配置文件 /usr/share/libvirt/cpu_map.xml 选择一种最相配的 CPU 型号。在这种 mode 下，虚拟机的指令集往往比宿主机少，性能相对 host-passthrough 要差一点，但是热迁移时，它允许目的节点 CPU 和源节点的存在一定的差异</li>
<li>host-passthrough(100%性能）：libvirt 令 KVM 把宿主机的 CPU 指令集全部透传给虚拟机。因此虚拟机能够最大限度的使用宿主机 CPU 指令集，故性能是最好的。但是在热迁移时，它要求目的节点的 CPU 和源节点的一致</li>
</ol>
<p>关于热迁移，理论上来说：</p>
<ol style="list-style-type: undefined;">
<li>host-passthrough: 要求源节点和目的节点的指令集完全一致</li>
<li>host-model: 允许源节点和目的节点的指令集存在轻微差异</li>
<li>custom: 允许源节点和目的节点指令集存在较大差异</li>
</ol>
<p>从实际情况来看，公司不同时间采购的 CPU 型号可能不相同；不同业务对 CPU 型号的要求也有差异。虽然互联网多采用 intel E5 系列的 CPU，但是该系列的 CPU 也有多种型号，常见的有 Xeon，Haswell，IvyBridge，SandyBridge 等等。即使是 host-model，在这些不同型号的 CPU 之间热迁移虚拟机也可能失败。所以从热迁移的角度，在选择 host-mode 时：</p>
<ol>
<li>需要充分考虑既有宿主机类型，以后采购扩容时，也需要考虑相同问题</li>
<li>除非不存在热迁移的场景，否则不应用选择 host-passthrough</li>
<li>host-model 下不同型号的 CPU 最好能以 aggregate hosts 划分，在迁移时可以使用 aggregate filter 来匹配相同型号的物理机</li>
<li>如果 CPU 型号过多，且不便用 aggregate hosts 划分，建议使用 custom mode</li>
</ol>
</td>
</tr>
</tbody>
</table>
<table class=" fixed-word-wrap full-width">
<thead>
<tr>
<td style="width: 20%; text-align: center;">元素</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>model</td>
<td>
<p>指定客户机要求的CPU型号，可用型号的列表在/usr/share/libvirt/cpu_map.xml<br />如果hypervisor不能使用精确的CPU，libvirt会自动fallback到特性最接近的CPU</p>
<p>属性列表：<br />fallback，是否允许fallback，可选值allow,forbid，默认allow<br />vendor_id，设置客户机看到的vendor_id，必须12字符长，典型值AuthenticAMD、GenuineIntel</p>
</td>
</tr>
<tr>
<td>vendor</td>
<td>设置客户机要求CPU的厂商，可用厂商列在cpu_map.xml</td>
</tr>
<tr>
<td>topology</td>
<td>规定总的CPU插槽数，每个CPU的核心数，每个核心的硬件线程数</td>
</tr>
<tr>
<td>feature</td>
<td>可以包含多个这样的元素，用来细粒度的规定CPU的特性：<br />name    特性名称<br />policy    策略：force强制要求此特性，即使宿主机不支持；require如果宿主机支持则要求此特性；optional可有可无；disable此特性在客户机上禁用；forbid如果宿主机支持此特性则客户机失败</td>
</tr>
<tr>
<td>numa</td>
<td>
<p>仅适用于QEMU/KVM，指定客户机的numa拓扑，举例：</p>
<pre class="crayon-plain-tag">&lt;numa&gt;
    &lt;cell id='0' cpus='0-3' memory='512000' unit='KiB'/&gt;
    &lt;cell id='1' cpus='4-7' memory='512000' unit='KiB' memAccess='shared'/&gt;
&lt;/numa&gt;</pre>
<p> 每个cell子元素对应一个numa 节点（cell）：<br />cpus指定节点对应的CPU范围<br />memory指定节点本地内存大小<br />memAccess控制内存被映射为shared还是private，此属性仅针对基于hugepage的内存</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">事件配置</span></div>
<p>你可能需要覆盖某些事件发生时采取的动作，注意并非所有hypervisors支持所有事件和动作。使用<pre class="crayon-plain-tag">virsh reboot</pre> 或者<pre class="crayon-plain-tag">virsh shutdown</pre> 可以触发事件。配置示例：</p>
<pre class="crayon-plain-tag">&lt;on_poweroff&gt;destroy&lt;/on_poweroff&gt;
&lt;on_reboot&gt;restart&lt;/on_reboot&gt;
&lt;on_crash&gt;restart&lt;/on_crash&gt;
&lt;on_lockfailure&gt;poweroff&lt;/on_lockfailure&gt;</pre>
<p> 事件类型采用元素表示：</p>
<table class=" fixed-word-wrap full-width">
<thead>
<tr>
<td style="width: 20%; text-align: center;">元素</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>on_poweroff</td>
<td>指定客户机请求断开电源时采取的动作</td>
</tr>
<tr>
<td>on_reboot</td>
<td>指定客户机请求重启时采取的动作</td>
</tr>
<tr>
<td>on_crash</td>
<td>指定客户机崩溃时采取的动作</td>
</tr>
</tbody>
</table>
<p>这几种事件都支持的动作包括：</p>
<ol>
<li>destroy，终止Domain并释放一切相关资源</li>
<li>restart，Domain被终止，并以相同的配置再次启动</li>
<li>preserve，Domain被终止但是其资源被保留供分析</li>
<li>rename-restart，以另外一个名字重启Domain</li>
</ol>
<p>on_crash支持额外的动作：</p>
<ol>
<li>coredump-destroy，崩溃Domain的core被dump出来，然后destroy</li>
<li>coredump-restart，崩溃Domain的core被dump出来，然后重启</li>
</ol>
<div class="blog_h2"><span class="graybg">电源管理</span></div>
<p>仅QEMU支持，强制启用/禁止客户机BIOS的电源管理功能：</p>
<pre class="crayon-plain-tag">&lt;pm&gt;
    &lt;!-- 是否启用ACPI的S4睡眠状态 --&gt;
    &lt;suspend-to-disk enabled='no'/&gt;
    &lt;!-- 是否启用ACPI的S3睡眠状态 --&gt;
    &lt;suspend-to-mem enabled='yes'/&gt;
&lt;/pm&gt;</pre>
<div class="blog_h2"><span class="graybg">Hypervisor特性</span></div>
<p>Hypervisor能够启用/禁用一些CPU/机器特性。配置示例：</p>
<pre class="crayon-plain-tag">&lt;features&gt;
    &lt;pae/&gt; &lt;!-- 物理地址扩展，允许32位客户机访问超过4GB内存 --&gt;
    &lt;acpi/&gt; &lt;!-- 对于电源管理有用，例如可以优雅的关闭KVM客户机 --&gt;
    &lt;apic/&gt; &lt;!-- 支持可编程的中断请求管理 --&gt;
    &lt;hap/&gt;  &lt;!-- 根据state=on|off，启禁硬件辅助paging --&gt;
    &lt;privnet/&gt; &lt;!-- 总是创建私有的网络命名空间，如果任何网络接口设备被添加，则自动设置。仅和容器虚拟化相关 --&gt;
    &lt;hyperv&gt; &lt;!-- 调整Windows客户机的性能，仅QEMU2.x --&gt;
        &lt;relaxed state='on'/&gt;
        &lt;vapic state='on'/&gt;
        &lt;spinlocks state='on' retries='4096'/&gt;
        &lt;vpindex state='on'/&gt;
        &lt;runtime state='on'/&gt;
        &lt;synic state='on'/&gt;
        &lt;reset state='on'/&gt;
        &lt;vendor_id state='on' value='KVM Hv'/&gt;
    &lt;/hyperv&gt;
    &lt;kvm&gt;
        &lt;hidden state='on'/&gt; &lt;!-- 从标准的MSR发现中隐藏KVM的Hypervisor --&gt;
    &lt;/kvm&gt;
    &lt;pvspinlock state='on'/&gt;  &lt;!-- 通知客户机，宿主机支持半虚拟化的自旋锁 --&gt;
&lt;/features&gt;</pre>
<div class="blog_h2"><span class="graybg">时间保持</span></div>
<p>客户机的时间通常是基于宿主机时间来初始化的，大部分OS期望硬件中存储的是UTC时间，然而Windows期望的则是“本地时间”。</p>
<p>配置示例：</p>
<pre class="crayon-plain-tag">&lt;clock offset='localtime'&gt;
    &lt;timer name='rtc' tickpolicy='catchup' track='guest'&gt;
        &lt;catchup threshold='123' slew='120' limit='10000'/&gt;
    &lt;/timer&gt;
    &lt;timer name='pit' tickpolicy='delay'/&gt;
&lt;/clock&gt;</pre>
<p>clock的offset属性控制客户机的时间如何与宿主机同步：</p>
<ol>
<li>utc，客户机启动时总是基于UTC时间来同步</li>
<li>localtime，客户机启动时基于宿主机的timezone配置来同步时间</li>
<li>timezone，客户机基于指定的时区来同步</li>
<li>variable，客户机的时钟相对于UTC或者localtime（由basis属性指定，默认utc）具有一定的偏移，偏移量单位秒，由adjustment指定</li>
</ol>
<div class="blog_h2"><span class="graybg">设备配置</span></div>
<p>提供给客户机的所有设备，都在<pre class="crayon-plain-tag">&lt;devices&gt;</pre> 元素中配置。本章后续内容讲述各种设备的XML配置信息。</p>
<div class="blog_h2"><span class="graybg">模拟器路径</span></div>
<p>可以使用下面的元素来指定模拟器全限定的路径：</p>
<pre class="crayon-plain-tag">&lt;devices&gt;
    &lt;emulator&gt;/usr/lib/xen/bin/qemu-dm&lt;/emulator&gt;
&lt;/devices&gt;</pre>
<p>capabilities的XML配置指明了特定Domain类型-体系结构组合对应的最佳模拟器。</p>
<div class="blog_h2"><span class="graybg">软/硬/光盘</span></div>
<p>任何软盘、硬盘、光盘或者半虚拟化的驱动器，都是通过<pre class="crayon-plain-tag">disk</pre> 元素来指定的。配置示例：</p>
<pre class="crayon-plain-tag">&lt;devices&gt;
    &lt;!-- 后端支持是文件的磁盘，使用外部快照 --&gt;
    &lt;disk type='file' snapshot='external'&gt;
        &lt;!-- xen的驱动微调 --&gt;
        &lt;driver name="tap" type="aio" cache="default"/&gt;
        &lt;!-- 磁盘的源——文件的位置 --&gt;
        &lt;source file='/var/lib/xen/images/fv0' startupPolicy='optional'&gt;
            &lt;seclabel relabel='no'/&gt;
        &lt;/source&gt;
        &lt;!-- 磁盘在客户机中的期望设备名，总线类型 --&gt;
        &lt;target dev='hda' bus='ide'/&gt;
        &lt;!-- IO节流阀 --&gt;
        &lt;iotune&gt;
            &lt;total_bytes_sec&gt;10000000&lt;/total_bytes_sec&gt;
            &lt;read_iops_sec&gt;400000&lt;/read_iops_sec&gt;
            &lt;write_iops_sec&gt;100000&lt;/write_iops_sec&gt;
        &lt;/iotune&gt;
        &lt;!-- 该磁盘可以作为启动盘，顺序2 --&gt;
        &lt;boot order='2'/&gt;
        &lt;!-- 启用磁盘加密 --&gt;
        &lt;encryption type='...'&gt;
            ...
        &lt;/encryption&gt;
        &lt;shareable/&gt;&lt;!-- 磁盘可以被多个Domain共享--&gt;
        &lt;serial&gt;...&lt;/serial&gt;&lt;!-- 指定磁盘序列号 --&gt;
    &lt;/disk&gt;
    &lt;!-- 该磁盘的源位于网络上 --&gt;
    &lt;disk type='network'&gt;
        &lt;!-- QEMU驱动微调，指定AIO基于pthread --&gt;
        &lt;driver name="qemu" type="raw" io="threads" ioeventfd="on" event_idx="off"/&gt;
        &lt;!-- 基于sheepdog的网络源 --&gt;
        &lt;source protocol="sheepdog" name="image_name"&gt;
            &lt;host name="hostname" port="7000"/&gt;&lt;!-- 磁盘源所在的主机 --&gt;
        &lt;/source&gt;
        &lt;target dev="hdb" bus="ide"/&gt;
        &lt;boot order='1'/&gt;
        &lt;transient/&gt;
        &lt;address type='drive' controller='0' bus='1' unit='0'/&gt;
    &lt;/disk&gt;
    &lt;disk type='network'&gt;
        &lt;driver name="qemu" type="raw"/&gt;
        &lt;source protocol="rbd" name="image_name2"&gt;
            &lt;host name="hostname" port="7000"/&gt;
            &lt;snapshot name="snapname"/&gt;
            &lt;config file="/path/to/file"/&gt;
        &lt;/source&gt;
        &lt;target dev="hdc" bus="ide"/&gt;
        &lt;!-- 提供网络身份验证信息 --&gt;
        &lt;auth username='myuser'&gt;
            &lt;secret type='ceph' usage='mypassid'/&gt;
        &lt;/auth&gt;
    &lt;/disk&gt;
    &lt;!-- 定义一个光驱设备 --&gt;
    &lt;disk type='block' device='cdrom'&gt;
        &lt;driver name='qemu' type='raw'/&gt;
        &lt;target dev='hdd' bus='ide' tray='open'/&gt;
    &lt;/disk&gt;
    &lt;!-- 定义一个光驱设备，使用ISO镜像文件 --&gt;
    &lt;disk type='file' device='cdrom'&gt;
        &lt;driver name='qemu' type='raw'/&gt;
        &lt;!-- 使用镜像文件 --&gt;
        &lt;source file='/home/alex/Vmware/KVM/coreos.iso'/&gt;
        &lt;target dev='hdd' bus='ide'/&gt;
        &lt;readonly/&gt;
    &lt;/disk&gt;
    &lt;!-- 从libvirt管理的存储池中取得源 --&gt;
    &lt;disk type='volume' device='disk'&gt;
        &lt;driver name='qemu' type='qcow2'/&gt;
        &lt;source pool="default" volume="coreos.qcow2" /&gt;
        &lt;target dev='vda' bus='virtio'/&gt;
        &lt;boot order='1'/&gt;
    &lt;/disk&gt;
    &lt;!-- 来自iscsi池的源 --&gt;
    &lt;disk type='network' device='disk'&gt;
        &lt;driver name='qemu' type='raw'/&gt;
        &lt;source protocol='iscsi' name='iqn.gmem.cc.example:iscsi-nopool/2'&gt;
            &lt;host name='example.com' port='3260'/&gt;
        &lt;/source&gt;
        &lt;auth username='myuser'&gt;
            &lt;secret type='iscsi' usage='libvirtiscsi'/&gt;
        &lt;/auth&gt;
        &lt;target dev='vda' bus='virtio'/&gt;
    &lt;/disk&gt;
    &lt;!-- 来自iscsi池的源，lun与disk不同的是，客户机的请求直接pass through到物理设备 --&gt;
    &lt;disk type='network' device='lun'&gt;
        &lt;driver name='qemu' type='raw'/&gt;
        &lt;source protocol='iscsi' name='iqn.gmem.cc.example:iscsi-nopool/1'&gt;
            &lt;host name='example.com' port='3260'/&gt;
        &lt;/source&gt;
        &lt;auth username='myuser'&gt;
            &lt;secret type='iscsi' usage='libvirtiscsi'/&gt;
        &lt;/auth&gt;
        &lt;target dev='sdb' bus='scsi'/&gt;
    &lt;/disk&gt;
    &lt;disk type='file' device='disk'&gt;
        &lt;driver name='qemu' type='qcow2'/&gt;
        &lt;source file='/var/lib/libvirt/images/domain.qcow'/&gt;
        &lt;!-- 多级嵌套的backing store --&gt;
        &lt;backingStore type='file'&gt;
            &lt;format type='qcow2'/&gt;
            &lt;source file='/var/lib/libvirt/images/snapshot.qcow'/&gt;
            &lt;backingStore type='block'&gt;
                &lt;format type='raw'/&gt;
                &lt;source dev='/dev/mapper/base'/&gt;
                &lt;backingStore/&gt;
            &lt;/backingStore&gt;
        &lt;/backingStore&gt;
        &lt;target dev='vdd' bus='virtio'/&gt;
    &lt;/disk&gt;
&lt;/devices&gt;</pre>
<div class="blog_h3"><span class="graybg">disk元素属性</span></div>
<table class=" fixed-word-wrap full-width">
<thead>
<tr>
<td style="width: 20%; text-align: center;">属性</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>type</td>
<td>
<p>磁盘的来源（source）类型。有效值：file,block,dir,network,volume</p>
<p>其中volume表示磁盘的来源是存储池中的一个卷</p>
</td>
</tr>
<tr>
<td>device</td>
<td>
<p>客户机看到的磁盘类别。有效值：floppy,disk,cdrom,lun，默认disk</p>
<p>仅当type=block|network且protocol=iscsi时；或者type=volume且使用protocol=iscsi、mode=host的source pool时可设置为lun，lun的行为与disk相同，除了来自客户机的SCSI命令被接收并pass through到物理设备。lun仅被raw设备识别，不能被分区识别</p>
</td>
</tr>
<tr>
<td>rawio</td>
<td>仅当device=lun时使用，是否启用rawio，有效值：yes/no</td>
</tr>
<tr>
<td>snapshot</td>
<td>指定快照行为：<br />internal    使用内部快照<br />external    使用外部快照<br />no    该磁盘不参与快照</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg"> source子元素</span></div>
<p> 指定磁盘的来源（source），其包含的属性依赖于disk的type属性：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 12%; text-align: center;">disk.type</td>
<td style="width: 12%; text-align: center;">属性</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>file</td>
<td>file</td>
<td>指定虚拟磁盘对应的镜像文件的全限定路径</td>
</tr>
<tr>
<td>block</td>
<td>dev</td>
<td>指定虚拟设备映射到的宿主机块设备的全限定路径</td>
</tr>
<tr>
<td>dir</td>
<td>dir</td>
<td>指定宿主机目录，此目录作为客户机的磁盘使用</td>
</tr>
<tr>
<td rowspan="2">network</td>
<td>protocol</td>
<td>访问磁盘映像的协议，有效值：nbd、iscsi、rbd、sheepdog、gluster</td>
</tr>
<tr>
<td>name</td>
<td>如果protocol=rbd、sheepdog、gluster，则此属性必须，用于指定什么卷/镜像被使用<br />对于protocol=iscsi，name可能包含一个逻辑单元号（ logical unit number，LUN），例如xx:iscsi-pool/1，不指定LUN则默认0</td>
</tr>
<tr>
<td rowspan="3">volume</td>
<td>pool</td>
<td>指定由libvirt管理的存储池（storage pool）的名称，磁盘来源位于此池中</td>
</tr>
<tr>
<td>volume</td>
<td>指定用作磁盘来源的，由libvirt关联的存储卷（ storage volume ）的名称</td>
</tr>
<tr>
<td>mode</td>
<td>指示如何将LUN暴露为磁盘来源：<br />direct<br />host，默认值</td>
</tr>
</tbody>
</table>
<p>source子元素可以包含以下子元素：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">子元素</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>host</td>
<td>当disk.type=network时，可以包含若干各host子元素，用来指定需要连接的主机。host具有以下属性：<br />name，主机名<br />port，监听端口<br />transport，传输协议类型<br />socket，UNIX套接字路径</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">backingStore子元素</span> </div>
<p>紧跟着source元素，用于指定磁盘使用的backing store ，backing store是构成磁盘的逻辑成分（类似于QEMU的backing file）。如果不指定此元素，则意味着source是自包含的。backingStore元素的属性列表如下：</p>
<table class=" fixed-word-wrap full-width">
<thead>
<tr>
<td style="width: 20%; text-align: center;">属性</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>type</td>
<td>backing store使用的磁盘类型，类似于disk.type</td>
</tr>
<tr>
<td>index</td>
<td> </td>
</tr>
</tbody>
</table>
<p>backingStore可以有下列子元素：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">元素</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>format</td>
<td>其type属性指定backing store内部的镜像格式，例如raw、qcow2</td>
</tr>
<tr>
<td>source</td>
<td>类似于disk.source</td>
</tr>
<tr>
<td>backingStore</td>
<td>如果此backing store也不是自包含，而依赖于其它backing store时，使用此元素递归的指定</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">target子元素</span></div>
<p>此子元素控制虚拟磁盘在什么总线/设备下暴露给客户机。属性如下：</p>
<table class=" fixed-word-wrap full-width">
<thead>
<tr>
<td style="width: 20%; text-align: center;">属性</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>dev</td>
<td>磁盘在客户机下的逻辑名称，此名称并不确保映射到相应的设备名称，你只能将其作为设备的“顺序提示”</td>
</tr>
<tr>
<td>bus</td>
<td>设置模拟的磁盘类型，有效值包括ide、scsi、virtio、xen、usb、sata、sd。如果不指定，bus的值根据dev的风格来推断，例如hda可以推断出ide</td>
</tr>
<tr>
<td>tray</td>
<td>可移动磁盘（光盘、软盘）的一个状态字段，有效值包括open、closed，默认closed</td>
</tr>
<tr>
<td>removable</td>
<td>设置USB磁盘的可移除标记，on/off，默认off</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">iotune子元素</span></div>
<p>针对单块磁盘进行IO微调，与 blkiotune 功能类似，但是后者针对Domain全局。</p>
<p>目前可设置的微调项都是针对QEMU的IO throttling微调，这些微调由子元素指定，取值0表示无限制。子元素列表：</p>
<table class=" fixed-word-wrap full-width">
<thead>
<tr>
<td style="width: 22%; text-align: center;">元素</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>total_bytes_sec</td>
<td rowspan="3">每秒钟I/O吞吐量的限制。total_bytes_sec不能与后两者同时出现</td>
</tr>
<tr>
<td>read_bytes_sec</td>
</tr>
<tr>
<td>write_bytes_sec</td>
</tr>
<tr>
<td>total_iops_sec</td>
<td rowspan="3">每秒钟I/O操作次数的限制。total_iops_sec不能与后两者同时出现</td>
</tr>
<tr>
<td>read_iops_sec</td>
</tr>
<tr>
<td>write_iops_sec</td>
</tr>
<tr>
<td>total_bytes_sec_max</td>
<td rowspan="6">与上面类似，但是限制最大值</td>
</tr>
<tr>
<td>read_bytes_sec_max</td>
</tr>
<tr>
<td>write_bytes_sec_max</td>
</tr>
<tr>
<td>total_iops_sec_max</td>
</tr>
<tr>
<td>write_iops_sec_max</td>
</tr>
<tr>
<td>size_iops_sec</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">driver子元素</span></div>
<p>与hypervisor驱动相关的更多细节配置，属性列表：</p>
<table class=" fixed-word-wrap full-width">
<thead>
<tr>
<td style="width: 20%; text-align: center;">属性</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>name    type</td>
<td>
<p>如果hypervisor支持多个backend驱动，则name属性指定primary后端驱动的名称，而type则指定一个子类型。例如：</p>
<ol>
<li>xen支持的name有tap、tap2、phy、file，支持type有aio</li>
<li>QEMU仅支持name为qemu，而支持raw、bochs、qcow2、qed等type</li>
</ol>
</td>
</tr>
<tr>
<td>cache</td>
<td>
<p>控制IO缓存策略，有效值包括</p>
<ol style="list-style-type: undefined;">
<li>default 由hypervisor自动选择。qemu-kvm 1.2-默认writethrough。之后的版本，对于客户机驱动ide/scsi/virtio来说，默认值可能是 writethrough</li>
<li>none 相当于直接使用宿主机的物理磁盘缓存，性能不错</li>
<li>writethrough 数据<span style="background-color: #c0c0c0;">直接写入磁盘（O_DSYNC）里，不使用缓存</span>；在<span style="background-color: #c0c0c0;">数据更新时，同时写入缓存Cache和后端存储</span>。此模式的优点是操作简单；缺点是因为数据修改需要同时写入存储，数据写入<span style="background-color: #c0c0c0;">速度较慢</span></li>
<li>writeback 在数据更新时<span style="background-color: #c0c0c0;">只写入缓存Cache（不使用O_DSYNC、O_DIRECT）</span>。只在<span style="background-color: #c0c0c0;">数据被替换出缓存时，被修改的缓存数据才会被写到后端存储</span>。此模式的优点是数据<span style="background-color: #c0c0c0;">写入速度快</span>，因为不需要写存储；缺点是一旦<span style="background-color: #c0c0c0;">更新后的数据未被写入存储时出现系统掉电的情况</span>，数据将无法找回</li>
<li>directsync，写入磁盘时，qemu-kvm将使用O_DSYNC + O_DIRECT。速度慢</li>
<li>unsafe 任何时候都不要在生产环境使用，cache flush不会传播到宿主机，因此任何意外的VM关机都会摧毁虚拟机文件系统</li>
</ol>
<p>最后两种几乎不会使用</p>
<p>writethrough、none、directsync的安全性好，只要客户机操作系统是现代且行为正常的 —— 必要时会执行flush</p>
<p>writeback的安全性次之，它给提示后端写缓存的存在，依赖于客户机发送必须的flush命令来保证客户机磁盘的数据完整性 —— 这是现代文件系统应有的正常行为。但是，在报告（给客户端应用程序）IO操作完成，到数据提交到宿主机磁盘，存在一个时间窗口。如果宿主机宕机，可能导致数据丢失</p>
<p>unsafe安全性差，和writeback的差异在于，客户机的flush命令被忽略</p>
<p>性能上： writeback &gt; none &gt; writethrough</p>
</td>
</tr>
<tr>
<td>error_policy</td>
<td>当磁盘出现读写错误时hypervisor的处理策略，有效值：stop、report、ignore、enospace</td>
</tr>
<tr>
<td>io</td>
<td>
<p>控制AIO的策略，QEMU支持：</p>
<ol style="list-style-type: undefined;">
<li>threads：用户空间异步IO的实现，其实它不是真正的异步IO，是通过启动一定数量的 blocking IO线程来模拟异步IO</li>
<li>native：Kernel native AIO :，Kernel的原生态异步IO实现</li>
</ol>
<p>native的性能更好</p>
</td>
</tr>
<tr>
<td>copy_on_read</td>
<td>
<p>当读取backing文件时，是否将读取的内容复制到当前的镜像文件中，当backing文件位于慢速网络中时可以设置为on</p>
<p>仅用于QEMU/KVM</p>
</td>
</tr>
<tr>
<td>iothread</td>
<td>将磁盘分配给Domain的iothreads元素定义的IO线程</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">boot子元素</span></div>
<p>用于指定该磁盘是可启动的，order属性指定其启动顺序。</p>
<div class="blog_h3"><span class="graybg">encryption子元素</span></div>
<p>指定卷如何被加密。</p>
<div class="blog_h3"><span class="graybg">readonly子元素</span></div>
<p>指定此磁盘不能被客户机修改，对于device=cdrom的设备默认true。</p>
<div class="blog_h3"><span class="graybg">shareable子元素</span></div>
<p>假设hypervisor和OS支持的话，指示此设备可以被多个Domain共享。指定此元素，应当同时禁用磁盘的缓存。</p>
<div class="blog_h3"><span class="graybg">transient子元素</span></div>
<p>指示当客户机退出时，对磁盘的所有修改将回退。对于某些hypervisor，把磁盘标记为transient会禁止快照与迁移。</p>
<div class="blog_h3"><span class="graybg">serial子元素</span></div>
<p>指定磁盘的序列号。</p>
<div class="blog_h3"><span class="graybg">wwn子元素</span></div>
<p>指定磁盘的世界范围名称（World Wide Name），此值必须唯一，由16位16进制数字组成。</p>
<div class="blog_h3"><span class="graybg">vendor子元素</span></div>
<p>指定磁盘的生产厂商，不超过8个可打印字符</p>
<div class="blog_h3"><span class="graybg">product子元素</span></div>
<p>指定磁盘的产品名称，不超过16个可打印字符</p>
<div class="blog_h3"><span class="graybg">address子元素</span></div>
<p>很多设备可以提供一个address 子元素，来指明设备挂载客户机虚拟总线的什么位置上。如果不指定address，libvirt会生成一个合适的地址。该子元素的属性列表如下：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">属性</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>type</td>
<td>必须，有效值包括：<br />pci    可配额外属性domain/bus/slot/function/multifunction<br />drive    可配额外属性controller/bus/target/unit<br />ccid     用于智能卡，可配额外属性bus/slot<br />usb    可配额外属性bus/port，其中port位点号分隔的单字节数字，例如1.2或者2.1.3.1</td>
</tr>
<tr>
<td>bus</td>
<td>磁盘绑定到的总线，对于PCI范围在0-0xff之间，对于其它为2位的bus号</td>
</tr>
<tr>
<td>slot</td>
<td>磁盘绑定到总线上的slot，范围在0x0-0x1f之间，对于其它为2位的slot号</td>
</tr>
<tr>
<td>function</td>
<td>磁盘的function号，范围在0-7之间</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">auth子元素</span></div>
<p>对于disk.type=network，且protocol为rbd、iscsi的磁盘，可以指定此子元素，提供访问磁盘源时使用的凭据。</p>
<div class="blog_h3"><span class="graybg">blockio子元素</span></div>
<p>用于QEMU/KVM，覆盖块设备的属性。属性列表：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">属性</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>logical_block_size</td>
<td>报告给客户机的逻辑块大小。对于Linux来说，BLKSSZGET ioctl会返回此值，表示最小单元的磁盘IO大小</td>
</tr>
<tr>
<td>physical_block_size</td>
<td>报告给客户机的物理块大小。对于Linux来说，BLKPBSZGET ioctl会返回此值，表示硬件扇区的大小</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">文件系统配置</span></div>
<p>使用filesystem元素可以把宿主机上的目录直接暴露给客户机访问，配置示例：</p>
<pre class="crayon-plain-tag">&lt;devices&gt;
    &lt;filesystem type='template'&gt;
        &lt;source name='my-vm-template'/&gt;&lt;!-- OpenVZ模板名称 --&gt;
        &lt;target dir='/'/&gt;
    &lt;/filesystem&gt;
    &lt;filesystem type='mount' accessmode='passthrough'&gt;
        &lt;driver type='path' wrpolicy='immediate'/&gt;
        &lt;source dir='/export/to/guest'/&gt; &lt;!-- 指定宿主机目录 --&gt;
        &lt;target dir='/import/from/host'/&gt;
        &lt;readonly/&gt;
    &lt;/filesystem&gt;
    &lt;filesystem type='file' accessmode='passthrough'&gt;
        &lt;driver name='loop' type='raw'/&gt;
        &lt;driver type='path' wrpolicy='immediate'/&gt;
        &lt;source file='/export/to/guest.img'/&gt;
        &lt;target dir='/import/from/host'/&gt;
        &lt;readonly/&gt;
    &lt;/filesystem&gt;
&lt;/devices&gt;</pre>
<p>filesystem元素的属性列表：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">属性</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td><span style="color: #000000;">type</span></td>
<td>
<p>指定文件系统的来源，有效值包括：<br />mount    默认值，挂载到客户机的一个宿主机目录，支持LXC、OpenVZ、QEMU/KVM<br />template    OpenVZ模板<br />file    一个宿主机文件被作为镜像，挂载到客户机，仅LXC<br />block    一个宿主机块设备，挂载到客户机，仅LXC<br />ram    一个内存文件系统<br />bind    绑定宿主机中的一个目录到客户机的一个目录，仅LXC</p>
</td>
</tr>
<tr>
<td> accessmode</td>
<td>访问源的安全模式，有效值包括：<br />passthrough    默认值，基于客户机用户的权限访问源<br />mapped    基于hypervisor（QEMU进程）的权限访问源<br />squash     类似于passthrough，只是忽略chown之类操作的错误</td>
</tr>
</tbody>
</table>
<p>子元素列表：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">元素</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>driver</td>
<td>
<p>指定hypervisor驱动的更多细节，属性列表：<br />type    如果hypervisor支持多种backend驱动，使用该属性指定primary后端驱动的名称<br />format    指定格式类型</p>
<p>对于LXC，支持type=loop&amp;format=raw，或者type=nbd；QEMU支持type=path|handle，不支持format</p>
</td>
</tr>
<tr>
<td>source</td>
<td>标注宿主机上的资源，该资源暴露为客户机的文件系统，属性列表：<br />name    仅用于filesystem.type=template，指定模板的名字<br />dir    仅用于filesystem.type=mount，指定宿主机目录<br />usage    仅用于filesystem.type='ram' 以KiB（可以使用units属性指定单位）限制内存用量</td>
</tr>
<tr>
<td>target</td>
<td>对于QEMU，指定文件系统在客户机的挂载点</td>
</tr>
<tr>
<td>readonly</td>
<td>文件系统对于客户机是否只读，仅QEMU/KVM</td>
</tr>
<tr>
<td>space_hard_limit</td>
<td rowspan="2">文件系统对于客户机的容量软硬限制</td>
</tr>
<tr>
<td>space_soft_limit</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">网络接口配置</span></div>
<p>有几种方式（type）来指定客户机能够看到的网络接口，网络接口的容器元素是<pre class="crayon-plain-tag">interface</pre> 。每个interface元素可以拥有一个address子元素，指定其在PCI上的slot。interface元素的属性列表：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 22%; text-align: center;">属性</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>type</td>
<td>网络接口的类型，有效值：<br />network    虚拟网络<br />bridge    直接桥接VM到局域网</td>
</tr>
<tr>
<td>trustGuestRxFilters</td>
<td>
<p>如果设置为true，则宿主机能够检测到并信任来自客户机的关于接口MAC地址变更的报告，并接收filters</p>
<p>对此设置的支持，取决于客户机的网络设备型号，以及宿主机的连接类型。当前只有virtio型号和macvtap宿主机连接类型支持</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">虚拟网络</span></div>
<p>对于基于动态地址分配/无线网络的宿主机获得连接性的虚拟机，推荐此方式。</p>
<p>虚拟网络提供一个其详细信息由一个命名网络定义（named network definition）所描述的连接。依据虚拟网络的转发模式（forward mode）设置，它可能是：</p>
<ol>
<li>完全隔离的，不配置<pre class="crayon-plain-tag">&lt;forward&gt;</pre> 元素</li>
<li>NAT到一个指定的网络设备或者默认路由，配置<pre class="crayon-plain-tag">&lt;forward mode='nat'&gt;</pre> </li>
<li>不基于NAT来路由，配置<pre class="crayon-plain-tag">&lt;forward mode='route'/&gt;</pre> </li>
<li>直接连接到宿主机的网络接口（通过macvtap）或桥接设备，配置<pre class="crayon-plain-tag">&lt;forward mode='bridge|private|vepa|passthrough'/&gt;</pre> </li>
</ol>
<p>对于转发模式设置为bridge | private | vepa | passthrough的网络，宿主机在libvirt管理范围之外应拥有必要的DNS、DHCP服务。对于转发模式设置为isolated | nat |routed的网络，libvirt提供的虚拟网络中包含了DHCP和DNS。虚拟网络自动分配的IP地址范围可以通过命令</p>
<pre class="crayon-plain-tag">virsh net-dumpxml [networkname]</pre>
<p>得到。一个<span style="background-color: #c0c0c0;">开箱即用的、称为default的虚拟网络</span>NAT到宿主机默认路由，其IP地址范围是192.168.122.0/24，在宿主机中你可以ifconfig看到一个名为virbr0的网络接口，与这个default虚拟网络有关。要自定义虚拟网络，需要修改其它类型（network XML）的配置文件L。</p>
<p>每个客户机会有一个命名为<pre class="crayon-plain-tag">vnetN</pre> 的tun设备，你可以利用target元素覆盖此命名。</p>
<p>类似于direct类型的接口，network类型的接口可以指定一个virtualport子元素，用于将配置信息转发给vepa（802.1Qbg）或 802.1Qbh兼容的交换机，或Open vSwich虚拟交换机。</p>
<p>配置示例：</p>
<pre class="crayon-plain-tag">&lt;devices&gt;
    &lt;interface type='network'&gt;
        &lt;!-- 如果接口的source是一个网络，则可以设置soure元素的portgroup属性，一个网络可能定义了多个portgroup，
             每个portgroup包含些许不同的配置信息，用于不同类别的网络连接 --&gt;
        &lt;source network='default'/&gt;
    &lt;/interface&gt;
    &lt;interface type='network'&gt;
        &lt;!-- --&gt;
        &lt;source network='default' portgroup='engineering'/&gt;
        &lt;target dev='vnet7'/&gt;  &lt;!-- 设置虚拟网卡的名称 --&gt;
        &lt;mac address="00:11:22:33:44:55"/&gt;&lt;!-- 设置虚拟网卡的MAC地址 --&gt;
        &lt;virtualport&gt;
            &lt;parameters instanceid='09b11c53-8b5c-4eeb-8f00-d84eaa0aaa4f'/&gt;
        &lt;/virtualport&gt;
    &lt;/interface&gt;
&lt;/devices&gt;</pre>
<div class="blog_h3"><span class="graybg">桥接到LAN</span></div>
<p>对于基于静态地址的有线网络的宿主机获得连接性的虚拟机，推荐此方式。</p>
<p>该方式将虚拟机直接桥接到宿主机所在的局域网，libvirt假设宿主机上的网桥设备enslaved了1-N个物理网卡。客户机的IP地址范围与宿主机局域网的IP地址范围一样。</p>
<p>在Linux系统中，网桥通常是标准的Linux主机网桥（host bridge）。如果主机支持Open vSwitch，则可以添加<pre class="crayon-plain-tag">&lt;virtualport type='openvswitch'/&gt;</pre> 子元素以连接到Open vSwitch网桥。</p>
<p>每个客户机会有一个命名为<pre class="crayon-plain-tag">vnetN</pre> 的tun设备，你可以利用target元素覆盖此命名。此tun设备会自动enslaved到宿主机网桥。</p>
<p>配置示例：</p>
<pre class="crayon-plain-tag">&lt;interface type='bridge'&gt;
    &lt;!-- 桥接到宿主机的br0网桥 --&gt;
    &lt;source bridge='br0'/&gt;
&lt;/interface&gt;
&lt;interface type='bridge'&gt;
    &lt;source bridge='br1'/&gt;
    &lt;!-- 设置客户机中虚拟网卡的名称和MAC --&gt;
    &lt;target dev='vnet7'/&gt;
    &lt;mac address="00:11:22:33:44:55"/&gt;
&lt;/interface&gt;
&lt;interface type='bridge'&gt;
    &lt;source bridge='ovsbr'/&gt;
    &lt;!-- 连接到Open vSwitch --&gt;
    &lt;virtualport type='openvswitch'&gt;
        &lt;!-- interfaceid是此虚拟网卡的唯一标识，如果不指定自动生成；profileid作为虚拟网卡的port-profile发送给vSwitch --&gt;
        &lt;parameters profileid='menial' interfaceid='09b11c53-8b5c-4eeb-8f00-d84eaa0aaa4f'/&gt;
    &lt;/virtualport&gt;
&lt;/interface&gt;</pre>
<div class="blog_h3"><span class="graybg">用户空间SLIRP栈</span></div>
<p>提供一个虚拟局域网并NAT到外面的世界，此虚拟网络使用10.0.2.x网段。默认路由10.0.2.2，DNS服务器10.0.2.3，客户机地址从10.0.2.15开始。此网络仅仅用于没有特权的宿主机用户。配置示例：</p>
<pre class="crayon-plain-tag">&lt;interface type='user'/&gt;
&lt;interface type='user'&gt;
    &lt;mac address="00:11:22:33:44:55"/&gt;
&lt;/interface&gt;</pre>
<div class="blog_h3"><span class="graybg">设置虚拟网卡型号</span></div>
<p>如果hypervisor支持，则可以设置虚拟网卡的型号。示例：</p>
<pre class="crayon-plain-tag">&lt;interface type='network'&gt;
    &lt;model type='ne2k_pci'/&gt;
&lt;/interface&gt;</pre>
<p>QEMU支持的型号包括 ne2k_isa i82551 i82557b i82559er ne2k_pci pcnet rtl8139 e1000 virtio。</p>
<div class="blog_h3"><span class="graybg">修改虚拟网卡状态</span></div>
<p>可以设置网卡是启用还是断开的：</p>
<pre class="crayon-plain-tag">&lt;interface type='network'&gt;
    &lt;link state='down'/&gt;
&lt;/interface&gt;</pre>
<div class="blog_h3"><span class="graybg">IP配置</span></div>
<p>网络设备、具有网络特性的hostdev设备可以配置一个或者多个IP地址，某些hypervisor会忽略这些配置。配置示例：</p>
<pre class="crayon-plain-tag">&lt;devices&gt;
    &lt;interface type='network'&gt;
        &lt;source network='default'/&gt;
        &lt;target dev='vnet0'/&gt;
        &lt;ip address='192.168.122.5' prefix='24'/&gt;
        &lt;!-- peer指定点对点连接中对端的IP地址 --&gt;
        &lt;ip address='192.168.122.5' prefix='24' peer='10.0.0.10'/&gt;
        &lt;!-- route仅用于LXC --&gt;
        &lt;route family='ipv4' address='192.168.122.0' prefix='24' gateway='192.168.122.1'/&gt;
        &lt;route family='ipv4' address='192.168.122.8' gateway='192.168.122.1'/&gt;
    &lt;/interface&gt;
    &lt;hostdev mode='capabilities' type='net'&gt;
        &lt;source&gt;
            &lt;interface&gt;eth0&lt;/interface&gt;
        &lt;/source&gt;
        &lt;ip address='192.168.122.6' prefix='24'/&gt;
        &lt;route family='ipv4' address='192.168.122.0' prefix='24' gateway='192.168.122.1'/&gt;
        &lt;route family='ipv4' address='192.168.122.8' gateway='192.168.122.1'/&gt;
    &lt;/hostdev&gt;
&lt;/devices&gt; </pre>
<div class="blog_h2"><span class="graybg">图形配置</span></div>
<p>配置图形（Graphical）设备，可以让你与客户机进行图形化的交互。客户机通常提供一个framebuffer或者text console，作为人机接口。配置示例：</p>
<pre class="crayon-plain-tag">&lt;!-- 基于SDL的图形接口，弹出QEMU的图形化控制台窗口 --&gt;
&lt;graphics type='sdl' display=':0' xauth='/home/alex/.Xauthority'/&gt;
&lt;!-- 基于VNC的图形接口 --&gt;
&lt;graphics type='vnc' port='-1' autoport='yes' listen='0.0.0.0' sharePolicy='allow-exclusive'&gt;
    &lt;listen type='address' address='0.0.0.0'/&gt;
&lt;/graphics&gt;

&lt;graphics type='rdp' autoport='yes' multiUser='yes'/&gt;
&lt;graphics type='desktop' fullscreen='yes'/&gt;
&lt;graphics type='spice'&gt;
    &lt;listen type='network' network='rednet'/&gt;
&lt;/graphics&gt;</pre>
<p>根据强制属性type的取值，grpahics的属性、子元素有所差异：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 10%; text-align: center;">type</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>sdl</td>
<td>在宿主机的桌面上显示一个窗口，额外属性：<br />display    使用哪个显示器<br />xauth    验证标识符<br />fullscreen    是否全屏，yes/no</td>
</tr>
<tr>
<td>vnc</td>
<td>启动一个VNC服务器，额外属性：<br />port    监听端口，-1表示自动分配<br />autoport    表示自动分配端口<br />passwd    VNC密码明文<br />keymap    使用的keymap<br />passwdValidTo    密码有效期限（UTC），示例'2010-04-09T15:51:00'<br />sharePolicy    显示共享策略，allow-exclusive独占并丢弃其它连接，force-shared禁止独占，ignore无条件允许任何连接<br />socket    对于QEMU，可以指定一个UNIX domain socket而非TCP/IP</td>
</tr>
<tr>
<td>spice</td>
<td>
<p>启动一个SPICE服务器，额外属性：<br />port   监听端口，-1表示自动分配<br />tlsPort    安全协议端口<br />autoport   表示自动分配端口<br />passwd   SPICE密码明文<br />keymap    使用的keymap<br />passwdValidTo    密码有效期限（UTC），示例'2010-04-09T15:51:00'<br />connected     如果密码改变，如何控制已连接的客户端。keep保持连接，disconnect断开连接，fail禁止修改密码<br />defaultMode    设置默认的通道安全策略。有效值secure、insecure、any</p>
<p>如果SPICE同时配置了普通端口、TLS安全端口。则可以利用channel子元素限制某个通道使用的端口。可用的通道名包括main, display, inputs, cursor, playback, record,smartcard,usbredir。配置示例：</p>
<pre class="crayon-plain-tag">&lt;channel name='main' mode='secure'/&gt;
&lt;channel name='record' mode='insecure'/&gt;</pre>
<p>SPICE支持音频、图片、流的压缩。你可以设置以下子元素的compression属性：<br />image    图片压缩，支持compression取值auto_glz, auto_lz, quic, glz, lz, off<br />jpeg    基于WAN访问时的JPEG压缩，支持compression取值 auto, never, always<br />zlib，基于WAN访问时的图片压缩，支持compression取值auto, never, always<br />playback，音频流压缩，支持compression取值onn,off</p>
<p>配置示例：</p>
<pre class="crayon-plain-tag">&lt;image compression='auto_glz'/&gt; </pre>
<p>可以使用streaming子元素设置流模式，其mode属性可以取值filter, all,off，配置示例：</p>
<pre class="crayon-plain-tag">&lt;streaming mode='filter'/&gt;</pre>
<p>基于Spice agent的复制/粘贴功能可以利用clipboard子元素设置，默认启用，配置示例：</p>
<pre class="crayon-plain-tag">&lt;clipboard copypaste='no'/&gt; </pre>
<p>鼠标模式可以利用mouse子元素设置，mode取值client,server，配置示例：</p>
<pre class="crayon-plain-tag">&lt;mouse mode='client'/&gt;</pre>
<p>文件传输功能可以利用filetransfer设置，默认启用，配置示例：</p>
<pre class="crayon-plain-tag">&lt;filetransfer enable='no'/&gt;</pre>
<p>SPICE支持服务器端的基于OpenGL的加速渲染（仅QEMU），配置示例：<pre class="crayon-plain-tag">&lt;gl enable='yes'/&gt;</pre> </p>
</td>
</tr>
<tr>
<td>rdp</td>
<td>启用一个RDP服务器，额外属性：<br />port 监听端口，-1表示自动分配<br />autoport 表示自动分配端口<br />replaceUser    布尔值，是否允许多个用户同时连接</td>
</tr>
<tr>
<td>desktop</td>
<td>保留给VirtualBox Domain，配置类似于sdl</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">listen子元素</span></div>
<p>用于指明在何处监听客户机连接。</p>
<div class="blog_h2"><span class="graybg">视频设备配置</span></div>
<p>video是描述视频设备的容器，为了向后兼容，如果配置了graphics却没有配置video，libvirt会根据客户机的类型自动添加一个video。配置示例：</p>
<pre class="crayon-plain-tag">&lt;!-- 默认第一个配置video是主视频设备，可以用primary属性覆盖 --&gt;
&lt;video primary='yes'&gt;
    &lt;model type='vga' vram='16384' heads='1'&gt;
        &lt;acceleration accel3d='yes' accel2d='yes'/&gt;
    &lt;/model&gt;
&lt;/video&gt;
&lt;video&gt;
   &lt;!-- 下面的配置是KVM客户机的默认值 --&gt;
   &lt;model type='cirrus' vram='16384' heads='1' /&gt;
&lt;/video&gt;</pre>
<p>子元素说明如下：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">元素</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>model</td>
<td>type     视频设备类型，可选值：vga、cirrus、vmvga、xen、vbox、qxl、virtio，基于hypervisor来选择<br />vram    现存容量<br />heads   设置屏幕的数量</td>
</tr>
<tr>
<td>acceleration</td>
<td>accel2d    启用2D加速，仅vbox<br />accel3d    启用3D加速，仅vbox、QEMU</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg"> 使用libguestfs</span></div>
<p>libguestfs是一组工具集，用来（在不启动客户机的情况下）访问、修改虚拟机的磁盘文件，通过libguestfs你可以好对磁盘进行以下操作：</p>
<ol>
<li>查看或者修改文件</li>
<li>创建虚拟磁盘</li>
<li>改变虚拟磁盘大小</li>
<li>执行磁盘备份、克隆等操作</li>
</ol>
<p>libguestfs支持多种虚拟磁盘格式，包括Vmware、Hyper-V。日常工作中我们主要使用libguestfs提供的命令行guestfish。libguestfs不依赖于libvirt。</p>
<p>与libguestfs类型工具包括：</p>
<ol>
<li>kpartx 需要root权限，并且将文件系统挂载到宿主机的内核中。相比之下libguestfs把文件系统隔离在appliance中，安全性高</li>
<li>vdfuse 该工具类似于kpartx，但是仅仅针对VirtualBox虚拟磁盘</li>
<li>qemu-nbd  用QEMU提供的工具，基于QEMU支持的磁盘格式（raw、qcow2）构建网络块服务器。libguestfs可以与之配合使用：<br />
<pre class="crayon-plain-tag">guestfish -a nbd://remote</pre>
</li>
</ol>
<div class="blog_h2"><span class="graybg">安装与配置</span></div>
<p>执行下面的命令安装libguestfs：</p>
<pre class="crayon-plain-tag">sudo apt-get install libguestfs-tools</pre>
<p>在Ubuntu下，需要执行：</p>
<pre class="crayon-plain-tag">sudo chmod 0644 /boot/vmlinuz* </pre>
<p>否则在使用过程中你会遇到cp: cannot open '/boot/vmlinuz-4.4.0-38-generic' for reading: Permission denied错误。</p>
<div class="blog_h2"><span class="graybg">使用guestfish</span></div>
<p>执行下面的命令，以编辑一个虚拟磁盘：</p>
<pre class="crayon-plain-tag"># 附加-v参数，可以看到很多调试信息，例如appliance的启动过程日志
guestfish -a vda.qcow2

# 出现提示符
&gt;&lt;fs&gt; 

# 添加一个磁盘，只能在run之前执行
# add-drive filename [readonly:true|false] [format:..] [iface:..] [name:..] [label:..] [protocol:..] [server:..]
add-drive vdb,qcow2 format:qcow2

# 执行run命令，一个appliance（类似于微型虚拟机）会被启动
&gt;&lt;fs&gt; run

# 列出设备
&gt;&lt;fs&gt; list-devices
# 输出：/dev/sda

# 列出分区
&gt;&lt;fs&gt; list-partitions
# 输出：
# /dev/sda1
# /dev/sda2

# 显示各分区详细信息
&gt;&lt;fs&gt; 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
# }
# 测试第一个分区是否可启动
&gt;&lt;fs&gt; 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
# 列出文件系统
&gt;&lt;fs&gt; list-filesystems
# 输出：
# /dev/sda1: ext4
# /dev/fedora_bogon/root: xfs
# /dev/fedora_bogon/swap: swap

# 挂载文件系统
&gt;&lt;fs&gt; mount /dev/fedora_bogon/root /

# 列出目录
&gt;&lt;fs&gt; ls /
# 创建新目录
&gt;&lt;fs&gt; mkdir /temp
# 其它支持的文件系统命令包括：cp chown chmod cp

# 下载文件到当前目录
download /root/.bash_history test
# 上传文件到虚拟磁盘
upload test /temp/test

# 查看文件内容
cat /temp/test

# 退出
&gt;&lt;fs&gt; quit</pre>
<p>执行run子命令后，需要等待一会，这时libguestfs在启动一个 appliance。再此appliance中会运行一个Linux内核，LVM、ext2等用户空间工具，以及一个守护程序guestfsd。宿主机进程基于RPC与这个守护进程通信，完成对磁盘镜像的操作。</p>
<div class="blog_h2"><span class="graybg">使用Guestmount</span></div>
<p>该命令可以把虚拟磁盘挂载到宿主机的目录上：</p>
<pre class="crayon-plain-tag"># 创建挂载点
mkdir vda
# 挂载一个文件系统
guestmount -a vda.qcow2 --rw -m /dev/fedora_bogon/root vda
# 现在你可以在宿主机中修改虚拟磁盘中的文件
# 操作完毕后，取消挂载
guestunmount vda</pre>
<div class="blog_h2"><span class="graybg">使用virt-builder</span></div>
<p>该命令可以用来快速的创建虚拟机磁盘镜像：</p>
<pre class="crayon-plain-tag"># 列出所有支持的客户机类型
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  # 第一次启动时执行的脚本</pre>
<div class="blog_h2"><span class="graybg">其它命令</span></div>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">命令</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>virt-ls</td>
<td>列出虚拟磁盘中的文件，示例：<pre class="crayon-plain-tag">virt-ls -a vda.qcow2 /</pre> </td>
</tr>
<tr>
<td>virt-cat</td>
<td>查看虚拟磁盘中某个文件的内容，示例：<pre class="crayon-plain-tag">virt-cat -a vda.qcow2 /root/.bashrc</pre> </td>
</tr>
<tr>
<td>virt-copy-in</td>
<td>拷贝目录或者文件到虚拟磁盘中，示例：<pre class="crayon-plain-tag">virt-copy-in -a vda.qcow2 hello /root/</pre></td>
</tr>
<tr>
<td>virt-copy-out </td>
<td>从虚拟磁盘中拷贝文件到本地，示例：<pre class="crayon-plain-tag">virt-copy-out -a vda.qcow2 /root/hello .</pre> </td>
</tr>
<tr>
<td>virt-df</td>
<td>显示虚拟磁盘文件系统的剩余空间情况</td>
</tr>
<tr>
<td>virt-diff</td>
<td>显示两个Domain或者虚拟磁盘中同一文件的差异</td>
</tr>
<tr>
<td>virt-edit</td>
<td>编辑一个文件</td>
</tr>
<tr>
<td>virt-format</td>
<td>执行格式化操作</td>
</tr>
<tr>
<td>virt-inspector</td>
<td>显示虚拟磁盘中操作系统的版本、以及其它信息</td>
</tr>
<tr>
<td>virt-make-fs</td>
<td>从一系列文件，或者tar来创建一个文件系统</td>
</tr>
<tr>
<td>virt-resize</td>
<td>改变虚拟磁盘的尺寸</td>
</tr>
<tr>
<td>virt-sparsify</td>
<td>稀疏化，虚拟磁盘中的空闲空间将归还给宿主机</td>
</tr>
<tr>
<td>virt-tar-in</td>
<td>打包并上传</td>
</tr>
<tr>
<td>virt-tar-out</td>
<td>打包并下载</td>
</tr>
<tr>
<td>virt-win-reg</td>
<td>导入导出Windows的注册表键值</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">管理虚拟局域网</span></div>
<p>通过配置libvirt的虚拟局域网，可以简化Domain的网络接口配置，比QEMU的脚本方式好很多。此外虚拟局域网还提供DHCP服务。</p>
<div class="blog_h2"><span class="graybg">虚拟网络交换机</span></div>
<p>libvirt引入了virtual network switch的概念，这是运行在宿主机上的软件。客户机可以“插入”到这个交换机上并传递流量。在Linux宿主机上，这个交换机表现为网络接口——默认情况下名字为virbr0，这个接口实质上是一个网桥。</p>
<div class="blog_h3"><span class="graybg">NAT</span></div>
<p>默认情况下虚拟网络交换机工作在NAT模式下，实质上是基于宿主机的iptables设置IP遮掩（不使用SNAT/DNAT），客户机对外通信时，使用宿主机的IP地址。</p>
<div class="blog_h3"><span class="graybg">路由模式</span></div>
<p>与NAT不同，这种模式直接转发客户机的IP封包，不进行NAT转换。这需要物理网络的路由器配置适当的路由，让客户机子网的流量流向所在的宿主机。</p>
<div class="blog_h3"><span class="graybg">隔离模式</span></div>
<p>这种模式下，虚拟网络交换机不把客户机的IP封包转发到真实网络上去。只有宿主机、各客户机之间可以进行通信。</p>
<div class="blog_h3"><span class="graybg">DNS &amp;DHCP</span></div>
<p>每个虚拟网络交换机可以设置一个用于动态分配的IP地址范围，供连接到此交换机的客户机使用，客户机可以通过DHCP服务自动获得IP地址。</p>
<p>libvirt基于dnsmasq实现DHCP和DNS，对于每个需要DHCP的虚拟网络交换机，libvirt在宿主机上启动一个dnsmasq实例。</p>
<p>除了简单的DNS请求转发，dnsmasq可以做更多的事情：</p>
<ol>
<li>它可以读取宿主机的 /etc/hosts 中条目，来应答DNS查询请求</li>
</ol>
<div class="blog_h2"><span class="graybg">网络架构示意</span></div>
<p>一个可能的虚拟网络架构的逻辑图如下：</p>
<p><img class="aligncenter size-full wp-image-13475" src="https://blog.gmem.cc/wp-content/uploads/2015/08/libvirt-net-logical.png" alt="libvirt-net-logical" width="679" height="498" /></p>
<p>对应的物理拓扑如下：</p>
<p><img class="aligncenter size-full wp-image-13476" src="https://blog.gmem.cc/wp-content/uploads/2015/08/libvirt-net-physical.png" alt="libvirt-net-physical" width="674" height="513" /></p>
<p>其中：</p>
<ol>
<li>VLAN 1，这个虚拟局域网通过网桥virbr0与eth1进行桥接，并基于NAT连接到真实局域网lan2</li>
<li>VLAN 2，这个虚拟局域网桥接到virbr0，但是与真实局域网完全隔离</li>
<li>Guest A，该虚拟机的：
<ol>
<li>eth0桥接到宿主机的网桥eth0，后者直接连接到真实网卡pth0，从而与lan1互联</li>
<li>eth1桥接到virbr0，可以基于NAT受限访问lan2</li>
</ol>
</li>
<li>Guest B，基于NAT、隔离网络</li>
<li>Guest C，基于隔离网络</li>
</ol>
<div class="blog_h2"><span class="graybg">相关virsh子命令</span></div>
<pre class="crayon-plain-tag"># 列出所有活动的虚拟局域网，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</pre>
<div class="blog_h2"><span class="graybg">虚拟局域网配置</span></div>
<p>类似于Domain，虚拟局域网的配置也是存放在XML文件中的。每个虚拟局域网对应一个XML配置文件，根元素为network。包括属性：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 22%; text-align: center;">属性</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>ipv6</td>
<td>yes/no，设置为yes时定义一个虚拟网络，该网络不指定网关的IPv6地址，但是允许客户机-客户机之间的通讯</td>
</tr>
<tr>
<td>trustGuestRxFilters</td>
<td>yese/no，设置连接到此VLAN的Domain的interface元素的同名属性</td>
</tr>
</tbody>
</table>
<p>包含以下基本子元素：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">子元素</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>name</td>
<td>此VLAN的短名，此名字被用作构成持久化配置文件的名字</td>
</tr>
<tr>
<td>uuid</td>
<td>全局唯一的VLAN标识符，例如3e3fce45-4f53-4fa7-bb32-11f34168b82a</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">连通性配置</span></div>
<p>以下子元素用于配置VLAN的连通性：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">子元素</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>bridge</td>
<td>说明如何创建让VLAN连接到物理网络的、宿主机上的网桥设备（虚拟网络交换机）：<br /><strong>name</strong>  <br />网桥设备的名称，这样所有使用此VLAN的客户机可以相互通信，网桥本身可以连接到真实局域网（LAN），取决于宿主机的配置<br />当使用mode=nat|route指定forward子元素，或者不指定forward子元素（隔离的VLAN）时，如果你不指定name属性，则libvirt会自动创建为网桥生成唯一名称并记住。libvirt推荐以virbr前缀指定name<br /><strong>stp</strong><br />是否启用 Spanning Tree Protocol，默认on<br /><strong>delay</strong><br />网桥转发的延迟秒数，默认0<br /><strong>macTableManager</strong><br />告知libvirt，如何管理MAC地址表（用于判断数据包的出口），默认值kernel，可设置为libvirt。设置为libvirt可以提高性能，但是导致vlan tagging, multicast等功能失效。需要内核版本3.17+</td>
</tr>
<tr>
<td>domain</td>
<td>DHCP相关配置：<br /><strong>name</strong><br />定义DHCP服务器的DNS Domain（域名）<br /><strong>localOnly</strong><br />如果设置为yes，则name对应子域名的解析均由VLAN自己的DNS服务器负责，与宿主机的DNS无关；如果设置为no，则无法解析的DNS请求转发给宿主机DNS处理</td>
</tr>
<tr>
<td>forward</td>
<td>
<p>通过配置此元素，可以让VLAN连接到物理网络。如果不指定此子元素，则VLAN与其它网络隔离（isolated mode）<br /><strong>mode</strong><br />定义包转发方式：</p>
<p style="padding-left: 30px;">nat  所有连接到此VLAN的客户机、物理网络之间的流量，均forward到宿主机的IP路由栈。从宿主机外部看来，所有客户机均使用宿主机的IP地址。这种mode适合多个客户机需要访问物理网络，而宿主机仅仅允许用于一个公共IP地址的应用场景。如果网络分配的IPv6地址，那么IPv6流量通过plain路由转发，因为IPv6没有NAT的概念。同一VLAN上地址相互通信，不进行NAT</p>
<p style="padding-left: 30px;">route  来自客户机的流量forward到宿主机的IP路由栈，但是不进行NAT。要使用这种方式，LAN路由器必须包含适当的路由表项，将流量返回给宿主机（进而转发给对应客户机）。使用此方式，则客户机的入站/出站会话不受限制</p>
<p style="padding-left: 30px;">open  类似于route，但是libvirt不会在宿主机上应用任何Firewall规则，也不支持设置dev属性</p>
<p style="padding-left: 30px;">bridge 桥接到既有网桥，该模式下libvirt不去创建网桥设备：</p>
<p style="padding-left: 60px;">当配置了<pre class="crayon-plain-tag">&lt;bridge name='br'/&gt;</pre> 时：桥接到一个宿主机既有（非libvirt管理）的网桥<br />当配置了<pre class="crayon-plain-tag">&lt;virtualport type='openvswitch'/&gt;</pre>  时：桥接到一个既有的Open vSwitch网桥<br />当配置了<pre class="crayon-plain-tag">&lt;interface dev='eth0' /&gt;</pre> 时：通过macvtap的桥接模式，直连到物理网络</p>
<p><strong>dev</strong><br />如果设置此属性，当mode=nat|route时，Firewall规则将限定仅仅forward到dev设备上，否则应用到所有设备。当设置为wlan0时生成的iptables规则：</p>
<pre class="crayon-plain-tag"># 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</pre>
<p>如果不设置dev，默认值为*，这就意味着：<span style="background-color: #c0c0c0;">从虚拟机中访问任何非10.0.0.0/16网段时，不管路由出口是哪个网卡</span>，封包到达宿主机后，会强制进行SNAT —— 哪怕目标地址在另外一台虚拟机上。这会让Flannel的host-gw模式失效，因为，Flannel的容器网络CIDR肯定不是10.0.0.0/16，这意味着跨节点（虚拟机）的CNI通讯会发生NAT，这显然是不期望发生的。 </p>
<p><span style="background-color: #c0c0c0;">示例：</span></p>
<pre class="crayon-plain-tag">&lt;!-- 桥接到物理网络，基于macvtap --&gt;
&lt;network connections='1'&gt;
  &lt;name&gt;default&lt;/name&gt;
  &lt;uuid&gt;519cbf63-8ec0-4893-ba9c-0747430bdecd&lt;/uuid&gt;
  &lt;forward dev='eth0' mode='bridge'&gt;
    &lt;interface dev='eth0' connections='1'/&gt;
  &lt;/forward&gt;
&lt;/network&gt;

&lt;!-- NAT转发，创建VLAN --&gt;
&lt;network connections='1'&gt;
  &lt;name&gt;default&lt;/name&gt;
  &lt;uuid&gt;9bae4de8-ca58-48c5-ba58-109aebf8b954&lt;/uuid&gt;
  &lt;forward mode='nat' dev="wlan0"&gt;
    &lt;nat&gt;
      &lt;port start='1024' end='65535'/&gt;
    &lt;/nat&gt;
  &lt;/forward&gt;
  &lt;bridge name='virbr0' stp='off' delay='0'/&gt;
  &lt;mac address='de:ad:be:ef:00:00'/&gt;
  &lt;ip address='10.0.0.1' netmask='255.0.0.0'&gt;
    &lt;dhcp&gt;
      &lt;range start='10.0.0.100' end='10.0.0.200'/&gt;
      &lt;host mac='DE:AD:BE:EF:00:02' ip='10.0.0.2'/&gt;
    &lt;/dhcp&gt;
  &lt;/ip&gt;
&lt;/network&gt;</pre>
</td>
</tr>
<tr>
<td>bandwidth</td>
<td>
<p>配置虚拟网络的QoS，仅支持fowward mode=route|nat或者隔离网络。配置示例：
<pre class="crayon-plain-tag">&lt;bandwidth&gt;
    &lt;inbound average='1000' peak='5000' burst='5120'/&gt;
    &lt;outbound average='128' peak='256' burst='256'/&gt;
&lt;/bandwidth&gt;</pre>
</td>
</tr>
<tr>
<td>ip</td>
<td>
<p>设置虚拟局域网的子网、网桥（DHCP服务器）的IP地址、DHCP和DNS配置
<p><strong>ip子元素</strong></p>
<p>该元素设置子网、网桥地址：</p>
<pre class="crayon-plain-tag">&lt;!-- 子网10.0.0.0/8，网桥地址10.0.0.1 --&gt;
&lt;ip address='10.0.0.1' netmask='255.0.0.0'&gt;</pre>
<p><strong>ip/dhcp子元素</strong></p>
<p>设置DHCP自动分配的地址范围、静态映射MAC地址到IP： </p>
<pre class="crayon-plain-tag">&lt;dhcp&gt;
    &lt;!-- 自动分配的地址范围 --&gt;
    &lt;range start='10.0.0.100' end='10.0.0.200'/&gt;
    &lt;!-- 对于给定的MAC地址，总是绑定到某个IP、机器网络名 --&gt;
    &lt;host mac="DE:AD:BE:EF:F1:10" name="fedora-10" ip="10.0.0.10" /&gt;
&lt;/dhcp&gt;</pre>
</td>
</tr>
<tr>
<td>dns</td>
<td>
<p>配置虚拟网络的DNS服务器，示例：
<pre class="crayon-plain-tag">&lt;!-- 如果enable为no，则libvirt不会启动DNS服务 --&gt;
&lt;dns enable="yes"&gt;
    &lt;!--
        每个forwarder元素定义一个备选DNS服务器：
        如果指定domain属性，则仅针对该domain下的子域名的查询请求转发给addr对应的DNS服务器
    --&gt;
    &lt;forwarder addr="8.8.8.8"/&gt;
    &lt;forwarder domain='example.com' addr="8.8.4.4"/&gt;
    &lt;forwarder domain='www.example.com'/&gt;
    &lt;!-- 静态DNS映射 --&gt;
    &lt;host ip='10.0.0.10'&gt;
        &lt;hostname&gt;fedora-10&lt;/hostname&gt;
        &lt;hostname&gt;fedora-10.local&lt;/hostname&gt;
    &lt;/host&gt;
&lt;/dns&gt;</pre>
</td>
</tr>
<tr>
<td>mac</td>
<td>
<p> 设置网桥的MAC地址，示例：
<p><pre class="crayon-plain-tag">&lt;mac address='DE:AD:BE:EF:F1:00'/&gt;</pre>
</td>
</tr>
</tbody>
</table>
<p>当你创建一个新的虚拟网络后，libvrit会在<pre class="crayon-plain-tag">/etc/libvirt/qemu/networks</pre> 目录中生成一个XML文件，该文件基于virsh net-define指定的那个配置文件。</p>
<div class="blog_h3"><span class="graybg">配置示例</span></div>
<p>基于NAT转发，启用DHCP，静态映射MAC到IP地址的例子：</p>
<pre class="crayon-plain-tag">&lt;network&gt;
  &lt;name&gt;default&lt;/name&gt;
  &lt;forward mode='nat'&gt;
  &lt;/forward&gt;
  &lt;bridge name='virbr0' stp='off' delay='0'/&gt;
  &lt;mac address='DE:AD:BE:EF:F1:00'/&gt;
  &lt;ip address='10.0.0.1' netmask='255.0.0.0'&gt;
    &lt;dhcp&gt;
      &lt;range start='10.0.0.100' end='10.0.0.200'/&gt;
      &lt;host mac="DE:AD:BE:EF:F1:10" ip="10.0.0.10" /&gt;
      &lt;host mac="DE:AD:BE:EF:F1:11" ip="10.0.0.11" /&gt;
    &lt;/dhcp&gt;
  &lt;/ip&gt;
&lt;/network&gt;</pre>
<p>使用宿主机既有网桥的例子： </p>
<pre class="crayon-plain-tag">&lt;network&gt;
    &lt;name&gt;default&lt;/name&gt;
    &lt;forward mode="bridge"/&gt;
    &lt;!-- br1是宿主机上既有的网桥，它可能直接连接到物理网络 --&gt;
    &lt;bridge name="br1"/&gt;
&lt;/network&gt;</pre>
<p>macvtap直连，需要 2.6.34+内核、仅支持QEMU/KVM。通过macvtap可以通过一组物理网络接口（不需要网桥）之一，直接连接到物理网络。客户机将直接具有物理网络的子网IP地址，并且与物理网络中其它机器互联互通。示例：</p>
<pre class="crayon-plain-tag">&lt;network&gt;
    &lt;name&gt;direct-macvtap&lt;/name&gt;
    &lt;forward mode="bridge"&gt;
        &lt;interface dev="eth0"/&gt;
        &lt;interface dev="eth1"/&gt;
    &lt;/forward&gt;
&lt;/network&gt;</pre>
<div class="blog_h1"><span class="graybg">存储池管理</span></div>
<p>通过存储池，libvirt集中管理物理机器上的存储资源，供客户机使用。存储池被划分为卷（volume），卷则可以作为块设备分配给客户机使用。</p>
<div class="blog_h2"><span class="graybg">池类型</span></div>
<p>libvirt支持以下类型的存储池后端：</p>
<table class=" fixed-word-wrap full-width">
<thead>
<tr>
<td style="width: 20%; text-align: center;">后端类型</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>目录</td>
<td>将宿主机的一个目录作为池看待，该目录中的文件可以包含各种客户机磁盘文件、镜像文件</td>
</tr>
<tr>
<td>本地文件系统</td>
<td>将宿主机上一个格式化好的文件系统作为池看待，文件系统类型可以是ext2,ext3,vfat </td>
</tr>
<tr>
<td>网络文件系统</td>
<td>使用远端网络文件系统服务器的导出目录作为存储池。默认为 NFS 网络文件系统</td>
</tr>
<tr>
<td>逻辑卷</td>
<td>使用已经创建好的 LVM 卷组，或者提供一系列生成卷组的源设备，libvirt 会在其上创建卷组，生成存储池</td>
</tr>
<tr>
<td>磁盘</td>
<td>使用磁盘作为存储池</td>
</tr>
<tr>
<td>iSCSI</td>
<td>使用 iSCSI 设备作为存储池</td>
</tr>
<tr>
<td>其它</td>
<td>SCSI、Multipath、RBD、Sheepdog、Gluster 、ZFS</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">NFS池</span></div>
<p>通常存储管理员负责维护存储设备，例如NFS服务器。而宿主机的管理员定义存储池，存储池包含了NFS shares到挂载目录的映射。当存储池启动时，libvirt会自动执行挂载操作。一旦存储池启动完毕，NFS share中的文件被当作卷看待，你可以在客户机的Domain配置文件中引用卷的路径，作为块设备的source。</p>
<p>使用NFS时，基于libvirt的API可以在池中创建/删除卷（即NFS上的文件），但是不能超过池的尺寸限制（NFS share有容量控制）。注意不是所有类型的池支持卷的创建/删除操作。 </p>
<div class="blog_h2"><span class="graybg">池XML配置</span></div>
<p>你可以通过XML文件来定义存储池，使用virsh命令可以定义、启动、停止、解除定义池，就像操作Domain、虚拟网络一样。你的配置文件被libvrit修改后，存放在<pre class="crayon-plain-tag">/etc/libvirt/storage/</pre> 目录中。配置示例：</p>
<pre class="crayon-plain-tag">&lt;!-- 基于本地目录的存储池 --&gt;
&lt;pool type="dir"&gt;
    &lt;name&gt;virtimages&lt;/name&gt;
    &lt;target&gt;
        &lt;path&gt;/var/lib/virt/images&lt;/path&gt;
    &lt;/target&gt;
&lt;/pool&gt;

&lt;!-- 基于iscsi的存储池 --&gt;
&lt;pool type="iscsi"&gt;
    &lt;name&gt;virtimages&lt;/name&gt;
    &lt;source&gt;
        &lt;host name="iscsi.example.com"/&gt;
        &lt;device path="iqn.2013-06.com.example:iscsi-pool"/&gt;
        &lt;auth type='chap' username='myuser'&gt;
            &lt;secret usage='libvirtiscsi'/&gt;
        &lt;/auth&gt;
    &lt;/source&gt;
    &lt;target&gt;
        &lt;path&gt;/dev/disk/by-path&lt;/path&gt;
    &lt;/target&gt;
&lt;/pool&gt;

&lt;!-- 基于本地文件系统的池 --&gt;
&lt;pool type="fs"&gt;
    &lt;name&gt;sda&lt;/name&gt;
    &lt;source&gt;
        &lt;!-- 使用的文件系统 --&gt;
    	&lt;device path="/dev/sda3"/&gt;
  	&lt;/source&gt;
    &lt;target&gt;
    	&lt;!-- 指定挂载点 --&gt;
        &lt;path&gt;/home/alex/Vmware/libvirt/images/sda&lt;/path&gt;
        &lt;permissions&gt;
            &lt;mode&gt;0777&lt;/mode&gt;
        &lt;/permissions&gt;
    &lt;/target&gt;
&lt;/pool&gt;</pre>
<div class="blog_h3"><span class="graybg">基本配置</span></div>
<p>一个存储池配置的根元素为pool，最基础的配置项如下：</p>
<pre class="crayon-plain-tag">&lt;pool type="iscsi"&gt;
    &lt;name&gt;virtimages&lt;/name&gt;
    &lt;uuid&gt;3e3fce45-4f53-4fa7-bb32-11f34168b82b&lt;/uuid&gt;
    &lt;allocation&gt;10000000&lt;/allocation&gt;
    &lt;capacity&gt;50000000&lt;/capacity&gt;
    &lt;available&gt;40000000&lt;/available&gt;
&lt;/pool&gt;</pre>
<p>属性和子元素说明如下：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">属性/子元素</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>@type</td>
<td>存储池类型，支持取值dir, fs, netfs, disk, iscsi, logical, scsi,mpath,rbd,sheepdog,gluster,zfs</td>
</tr>
<tr>
<td>name</td>
<td>对于宿主机来说，唯一的名称</td>
</tr>
<tr>
<td>uuid</td>
<td>全局唯一的UUID，如果忽略libvirt会自动生成</td>
</tr>
<tr>
<td>allocation</td>
<td>当前分配给池的容量，单位字节。创建池时不能使用此元素</td>
</tr>
<tr>
<td>capacity</td>
<td>池的总容量，单位字节。创建池时不能使用此元素</td>
</tr>
<tr>
<td>available</td>
<td>设置可以分配给新的卷使用的空闲容量。创建池时不能使用此元素</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">source子元素</span></div>
<p>存储池具有最多一个（某些类型的池后端没有）source子元素，用于描述池的来源。source包含的内容取决于池的type。 配置示例：</p>
<pre class="crayon-plain-tag">&lt;!-- disk池配置 --&gt;
&lt;source&gt;
    &lt;device path='/dev/mapper/mpatha' part_separator='no'/&gt;
    &lt;format type='gpt'/&gt;
&lt;/source&gt;

&lt;!-- SCSI池配置 --&gt;
&lt;source&gt;
    &lt;adapter type='scsi_host' name='scsi_host1'/&gt;
&lt;/source&gt;
&lt;source&gt;
    &lt;adapter type='scsi_host'&gt;
        &lt;parentaddr unique_id='1'&gt;
            &lt;address domain='0x0000' bus='0x00' slot='0x1f' addr='0x2'/&gt;
        &lt;/parentaddr&gt;
    &lt;/adapter&gt;
&lt;/source&gt;</pre>
<p>各子元素说明如下：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">source的子元素</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>device</td>
<td>对于fs, logical, disk, iscsi, zfs类型的池后端，指定存储池的后端物理设备。对于某些池后端，该元素可能出现多次。支持以下属性：<br />path，指向物理设备的全限定路径，或者iSCSI全限定名称（IQN）<br />part_separator，yes/no，用于disk后端</td>
</tr>
<tr>
<td>dir</td>
<td>对于 dir, netfs, gluster类型的池后端，指定存储池对应的文件目录。仅能出现一次。支持以下属性：<br />path，目录的全限定名称。对于Samba share，路径不包含起始的斜杠</td>
</tr>
<tr>
<td>adapter</td>
<td>对于scsi类型的池后端，指定存储池使用的SCSI适配器</td>
</tr>
<tr>
<td>host</td>
<td>对于 netfs, iscsi, rbd, sheepdog, gluster类型的池后端，如果存储池位于远程机器上，指定远程机器的信息，必须和device或者dir元素联用。支持以下属性：<br />name  远程机器的IP或者主机名<br />port  监听端口，可选，默认值依据远程协议确定</td>
</tr>
<tr>
<td>auth</td>
<td>提供访问池后端所需的身份验证信息。支持以下属性：<br />type  可以是chap或者ceph，分别用于iscsi、rbd<br />username  用户名<br />secret  密码信息（libvirt secret object）</td>
</tr>
<tr>
<td>name</td>
<td>对于logical, rbd, sheepdog, gluster类型的池后端，依据已命名元素提供池的源</td>
</tr>
<tr>
<td>format</td>
<td>
<p>指定源的格式</p>
<p>对于fs池后端，指定的是文件系统的类型：auto  ext2  ext3  ext4  ufs  iso9660  udf  gfs  gfs2  vfat  hfs+  xfs  ocfs2</p>
<p>对于netfs池后端，指定的是网络协议的类型：auto  nfs  glusterfs  cifs</p>
<p>对于logical池后端，只能是lvm2</p>
<p>对于disk池后端，指定的是分区表类型：dos  dvh  gpt  mac  bsd  pc98  sun  lvm2</p>
</td>
</tr>
<tr>
<td>vendor</td>
<td>指定存储设备的供应商信息</td>
</tr>
<tr>
<td>product</td>
<td>指定存储设备的产品信息</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">target子元素</span></div>
<p>对于dir, fs, netfs, logical, disk, iscsi, scsi, mpath类型池后端，可以具有单个target子元素。该元素描述<span style="background-color: #c0c0c0;">如何映射存储池的source到宿主机的文件系统命名空间</span>。配置示例：</p>
<pre class="crayon-plain-tag">&lt;target&gt;
    &lt;path&gt;/dev/disk/by-path&lt;/path&gt;
    &lt;permissions&gt;
        &lt;owner&gt;107&lt;/owner&gt;
        &lt;group&gt;107&lt;/group&gt;
        &lt;mode&gt;0744&lt;/mode&gt;
        &lt;label&gt;virt_image_t&lt;/label&gt;
    &lt;/permissions&gt;
    &lt;timestamps&gt;
        &lt;atime&gt;1341933637.273190990&lt;/atime&gt;
        &lt;mtime&gt;1341930622.047245868&lt;/mtime&gt;
        &lt;ctime&gt;1341930622.047245868&lt;/ctime&gt;
    &lt;/timestamps&gt;
    &lt;encryption type='...'&gt;&lt;/encryption&gt;
&lt;/target&gt;</pre>
<p>各子元素说明如下：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">target的子元素</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>path</td>
<td>存储池映射到宿主机目录树的什么位置。对于：
<ol>
<li><span style="color: #333333; font-family: Ubuntu, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: 13px; line-height: 22px;">fs,dir池后端，该路径是在其中创建卷的绝对路径</span></li>
<li>基于设备的后端，该路径是设备节点所在路径，最好使用稳定（重启后不变化）的路径：/dev/disk/by-{path|id|uuid|label}，避免重启后路径变化</li>
<li>mpath后端，设置该子元素无效，总是使用默认值/dev/mapper</li>
</ol>
</td>
</tr>
<tr>
<td>permissions</td>
<td>仅用于fs,dir池后端，指定池对应目录的文件模式</td>
</tr>
<tr>
<td>timestamps</td>
<td>提供卷的时间戳信息</td>
</tr>
<tr>
<td>encryption</td>
<td>指定卷的加密方式</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">卷XML配置</span></div>
<p>存储卷，通常对应一个文件或者设备节点。注意：当创建了存储池后，<span style="background-color: #c0c0c0;">池中的镜像文件会被libvirt自动识别</span>，并创建相应的卷定义。</p>
<p>配置示例：</p>
<pre class="crayon-plain-tag">&lt;!-- 基于文件的卷 --&gt;
&lt;volume type='file'&gt;
    &lt;name&gt;fedora-10.qcow2&lt;/name&gt;
    &lt;capacity unit='GiB'&gt;16&lt;/capacity&gt;
    &lt;target&gt;
        &lt;path&gt;/home/alex/Vmware/libvirt/images/fedora-10.qcow2&lt;/path&gt;
        &lt;format type='qcow2'/&gt;
        &lt;permissions&gt;
            &lt;mode&gt;0666&lt;/mode&gt;
        &lt;/permissions&gt;
    &lt;/target&gt;
    &lt;backingStore&gt;
        &lt;path&gt;/home/alex/Vmware/libvirt/images/fedora-base.qcow2&lt;/path&gt;
        &lt;format type='qcow2'/&gt;
        &lt;permissions&gt;
            &lt;mode&gt;0666&lt;/mode&gt;
        &lt;/permissions&gt;
    &lt;/backingStore&gt;
&lt;/volume&gt;
&lt;!-- 使用LUKS加密的卷--&gt;
&lt;volume&gt;
    &lt;name&gt;MyLuks.img&lt;/name&gt;
    &lt;capacity unit="G"&gt;5&lt;/capacity&gt;
    &lt;target&gt;
        &lt;path&gt;/var/lib/virt/images/MyLuks.img&lt;/path&gt;
        &lt;format type='raw'/&gt;
        &lt;encryption format='luks'&gt;
            &lt;secret type='passphrase' uuid='f52a81b2-424e-490c-823d-6bd4235bc572'/&gt;
        &lt;/encryption&gt;
    &lt;/target&gt;
&lt;/volume&gt;</pre>
<div class="blog_h3"><span class="graybg">基本配置</span></div>
<p>一个卷配置的根元素是volume，最基础的配置项如下：</p>
<pre class="crayon-plain-tag">&lt;volume type='file'&gt;
    &lt;name&gt;sparse.img&lt;/name&gt;
    &lt;key&gt;/var/lib/xen/images/sparse.img&lt;/key&gt;
    &lt;allocation&gt;0&lt;/allocation&gt;
    &lt;capacity unit="T"&gt;1&lt;/capacity&gt;
&lt;/volume&gt;</pre>
<p> 属性和子元素说明如下：</p>
<table class=" fixed-word-wrap full-width">
<thead>
<tr>
<td style="width: 20%; text-align: center;">属性/子元素</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>@type</td>
<td>该属性仅能读取，显示卷的实际类型</td>
</tr>
<tr>
<td>name</td>
<td>存储池范围内唯一的卷名称</td>
</tr>
<tr>
<td>key</td>
<td>用于唯一性的识别卷，该子元素不能在创建卷的时候指定，它总是自动生成的</td>
</tr>
<tr>
<td>allocation</td>
<td>指定分配给卷的存储空间，如果卷是稀疏分配的，该值可以小与卷的逻辑大小。可用属性：<br />unit  计量单位</td>
</tr>
<tr>
<td>capacity</td>
<td>指定卷的逻辑大小。可用属性：<br />unit 计量单位</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">target子元素</span></div>
<p>volume包含一个target子元素，指定卷如何映射到宿主机的文件系统中：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">target的子元素</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>path</td>
<td>卷在本地文件系统的绝对路径。该元素为只读，在创建时不能指定，libvirt会根据卷的name元素自动生成此元素</td>
</tr>
<tr>
<td>format</td>
<td>
<p>指定卷的格式：</p>
<p>对于dir,fs,nfs池后端，支持的卷格式包括：<br />raw  原始文件<br />bochs  Bochs磁盘镜像格式<br />cloop   压缩的loopback磁盘镜像格式<br />cow   用户模式Linux磁盘镜像格式<br />dmg   MAC的磁盘镜像格式<br />iso    光盘镜像格式<br />qcow  QEMU v1磁盘镜像格式<br />qcow2  QEMU v2磁盘镜像格式<br />qed  QEMU增强磁盘镜像格式<br />vmdk  Vmware磁盘镜像格式<br />vpc    VirtualPC磁盘镜像格式</p>
<p>对于disk池后端，支持的卷格式包括：<br />none  linux  fat16  fat32 linux-swap  linux-lvm  linux-raid  extended</p>
<p>对于logical,iscsi,scsi,mpath,rbd,sheepdog,gluster,zfs池后端，不支持该元素</p>
</td>
</tr>
<tr>
<td>permissions</td>
<td>指定卷的文件模式</td>
</tr>
<tr>
<td>compat</td>
<td>兼容性级别，仅仅用于qcow2格式的卷</td>
</tr>
<tr>
<td>nocow</td>
<td>关闭COW，仅仅支持btrfs文件系统中的、文件形式的卷</td>
</tr>
<tr>
<td>features</td>
<td>特定于format的配置项</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">backingStore子元素</span></div>
<p>volume可能包含一个backingStore子元素，用来描述卷的copy-on-write后端存储，配置示例：</p>
<pre class="crayon-plain-tag">&lt;backingStore&gt;
    &lt;path&gt;/var/lib/virt/images/master.img&lt;/path&gt;
    &lt;format type='raw'/&gt;
    &lt;permissions&gt;
        &lt;owner&gt;107&lt;/owner&gt;
        &lt;group&gt;107&lt;/group&gt;
        &lt;mode&gt;0744&lt;/mode&gt;
        &lt;label&gt;virt_image_t&lt;/label&gt;
    &lt;/permissions&gt;
&lt;/backingStore&gt; </pre>
<div class="blog_h1"><span class="graybg">迁移</span></div>
<p>在不同宿主机之间迁移客户机是比较复杂的问题，为了应对不同的需求，libvirt实现了多种解决方案。</p>
<div class="blog_h2"><span class="graybg">网络数据传输</span></div>
<p>从数据传输的角度来看，libvirt支持两种类型的迁移：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">传输类型</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>native</td>
<td>
<p>使用Hypervisor本身的数据传输机制，直接在源、目标宿主机之前进行迁移。该方式可能不支持加密，但是需要最少的数据传输量</p>
<p>可能需要针对Hypervisor进行手工的网络相关的配置，某些Hypervisor需要打开大量的端口才能支持并发的迁移操作</p>
</td>
</tr>
<tr>
<td>tunnelled</td>
<td>
<p>利用libvirt内置此RPC通信协议进行数据传输，总是支持数据加密。数据先从源客户机拷贝到源宿主机上的libvirt，再发送到目标主机的libvrit，最终发送给目标客户机，因此需要额外的数据传输量</p>
<p>不需要针对Hypervisor进行网络配置，仅仅需要开放一个端口</p>
<p>如果客户机内存很大，因而脏页变化频繁，额外的数据传输可能导致性能问题</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">通信控制路径</span></div>
<p>从通信控制路径角度来看，libvirt支持三种类型的迁移：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">通信控制路径</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>受管直接迁移</td>
<td>
<p>由libvirt客户端进程控制迁移的各个阶段。libvirt客户端必须能够连接到源、目的客户机的libvirt守护进程，并通过身份验证。源、目的客户机上的libvirt守护进程不需要相互通信</p>
<p>如果libvirt客户端崩溃，或者丢失到libvirt守护进程的连接，则源宿主机上的迁移会取消，并在源宿主机上重启客户机的CPU</p>
</td>
</tr>
<tr>
<td>受管点对点迁移</td>
<td>
<p>libvirt客户端仅仅与源宿主机上的libvirt守护进程通信，由后者控制迁移的整个过程。源宿主机的libvirt守护进程会连接到目的宿主机的libvirt守护进程执行迁移操作，libvirt客户端崩溃或者断开不会影响迁移过程的推进</p>
<p>注意，源宿主机上的libvirt守护进程使用自己的身份（通常是root）而不是客户端的身份连接到目标宿主机</p>
</td>
</tr>
<tr>
<td>非受管直接迁移</td>
<td>
<p>libvirt客户端、守护进程都不控制迁移过程，迁移由底层的Hypervisor负责。libvirt仅仅在Hypervisor的管理层次上触发迁移请求</p>
<p>即使libvirt客户端、守护进程都崩溃，迁移过程还会继续推进</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">数据安全的考虑</span></div>
<p>迁移时，客户机的内存需要完整的在网络上传输，这可能导致客户机中的敏感信息被嗅探。</p>
<p>如果宿主机有多个网络接口，或者交换机支持Tagged虚拟局域网，最好将客户机的网络流量和迁移/管理网络流量隔离开来。</p>
<p>某些场景下，仅仅依靠网络隔离不能满足安全性需要，这时，需要使用libvirt的tunnelled传输，启用数据加密。</p>
<div class="blog_h2"><span class="graybg">离线迁移</span></div>
<p>所谓离线迁移，就是把源主机的Domain状态设置为inactive，并且在迁移完毕后，Domain在源主机上继续运行，在目标主机上保持inactive状态。目前离线迁移不支持拷贝非共享存储。</p>
<div class="blog_h1"><span class="graybg">PCI直通</span></div>
<div class="blog_h2"><span class="graybg">简介</span></div>
<p>开发虚拟机固件（Open Virtual Machine Firmware）是一个为虚拟机提供UEFI支持的项目。从Linux 3.9开始，配合最近版本的QEMU，能够支持PCI设备直通，例如将显卡直接分配给虚拟机，这样虚拟机就获得接近原生显卡的性能。</p>
<p>对于Intel CPU，除了CPU需要支持VT-x之外，还需要支持VT-d（Intel Virtualization Technology for Directed I/O ）。此技术改善系统的安全性、可靠性，同时支持提升虚拟化环境下的IO性能。</p>
<p>Intel的VT-d和AMD的AMD-Vi，通称IOMMU。IOMMU特性不但要求CPU支持，主板、BIOS也需要配合。<span style="background-color: #c0c0c0;">开启IOMMU能够实现PCI直通、内存保护（免于被恶意、缺陷设备错误修改）</span>。</p>
<div class="blog_h2"><span class="graybg">启用IOMMU</span></div>
<div class="blog_h3"><span class="graybg">内核参数</span></div>
<pre class="crayon-plain-tag">intel_iommu=on iommu=pt</pre>
<p>设置iommu=pt能够防止内核触碰不能直通的设备。</p>
<p>重启后，使用下面的命令确认IOMMU已经正确启用： </p>
<pre class="crayon-plain-tag">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</pre>
<div class="blog_h3"><span class="graybg">PCI设备分组</span></div>
<p>使用下面的脚本可以更加清楚的看到PCI设备是如何并映射到IOMMU分组的：</p>
<pre class="crayon-plain-tag">#!/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;</pre>
<p>输出如下： </p>
<pre class="crayon-plain-tag">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] </pre>
<p>需要注意，<span style="background-color: #c0c0c0;">IOMMU组是直通的最小调度单元</span>。也就是说，你可能被迫将多个设备一起直通给虚拟机，或者将设备放到其他PCI插槽。</p>
<div class="blog_h2"><span class="graybg">隔离PCI设备</span></div>
<p>为了将设备分配给虚拟机，它和它所在分组的所有其他设备的驱动，<span style="background-color: #c0c0c0;">必须更换为一个占位符驱动（stub 或 VFIO驱动）</span>。这样<span style="background-color: #c0c0c0;">，宿主机就不会和该设备进行交互</span>。大部分设备，可以在启动虚拟机之前，即席的完成驱动更换操作。</p>
<p>对于显卡，没有这么方便。GPU驱动通常对动态rebinding支持的不好，不能将宿主机上正在使用的GPU透明的直通给虚拟机。因此，我们需要<span style="background-color: #c0c0c0;">在宿主机启动时，尽早将占位符驱动bind给GPU</span>。这样，<span style="background-color: #c0c0c0;">除非驱动被换回，或者虚拟机claim该GPU，它会保持inactive状态</span>。</p>
<p>之所以要求尽早，是因为Linux现有的驱动机制是<span style="background-color: #c0c0c0;">每个驱动在初始化时，自行去寻找没有初始化的，自己关注的硬件。因此驱动的初始化顺序非常关键</span>。如果慢了，物理驱动就会抢先绑定设备。</p>
<p>从内核4.1开始，<span style="background-color: #c0c0c0;">VFIO驱动vfio-pci被包含其中</span>。可以用来代替pci-stub。</p>
<div class="blog_h3"><span class="graybg">基于pci-stub</span></div>
<p>修改内核参数：</p>
<pre class="crayon-plain-tag">pci-stub.ids=8086:9bf6</pre>
<p>pci-stub根据ID来识别设备，也就是说，我们需要提供用于直通的PCI设备的ID。对于上面的输出中，Intel显卡的ID是8086:9bf6。</p>
<p>如果要绑定<span style="background-color: #c0c0c0;">多个设备，ID需要用逗号分隔</span>。 </p>
<p>随后我们需要执行下面的命令并重启：</p>
<pre class="crayon-plain-tag">sudo update-grub</pre>
<div class="blog_h3"><span class="graybg">基于vfio-pci</span></div>
<p>内核总是先初始化所有内建的驱动，然后才逐个初始化外部模块。 在<span style="background-color: #c0c0c0;">外部模块</span>中，我们可以通过softdep来增加模块间的依赖关键： 例如我们可以把vfio-pci列为nvidia的依赖，那么vfio-pci总会被在nvidia之前初始化，而确保能抢占到硬件。</p>
<p>我们首先需要弄清楚，需要直通的硬件，当前是被什么驱动所绑定：</p>
<pre class="crayon-plain-tag">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: &lt;access denied&gt;
    # 当前使用的驱动
	Kernel driver in use: i915
	Kernel modules: i915</pre>
<p>i915是内核builtin的驱动，无法通过修改softdep，改变驱动加载顺序。解决此问题，有两种方案：</p>
<ol>
<li>将i915从内核剥离出来，编译为模块</li>
<li>将vfio-pci编译进内核</li>
</ol>
<p>如果选择方案2，则使用内核参数：<pre class="crayon-plain-tag">vfio-pci.ids=8086:9bf6</pre> 即可完成设备隔离。对于Ubuntu 20.04，vfio-pci已经默认编译到内核中。</p>
<div class="blog_h1"><span class="graybg">Hooks</span></div>
<p>libvirt提供钩子（Hooks）功能，你可以添加自定义的脚本，存放在/etc/libvirt/hooks目录下，以便在：</p>
<ol>
<li>libvirt守护程序启动、停止、reload时（对应daemon子目录）</li>
<li>QEMU客户机启动、停止时（对应qemu子目录）</li>
<li>一个虚拟网络启动、停止时，或者一个网络接口接入、断开网络时（对应network子目录）</li>
</ol>
<p>执行特定的逻辑。Hooks可以基于Bash或者Python编写。</p>
<div class="blog_h1"><span class="graybg">常见问题</span></div>
<div class="blog_h2"><span class="graybg">Vmware中运行的libvirt</span></div>
<div class="blog_h3"><span class="graybg">无法使用macvtap直连的虚拟网络</span></div>
<p>报错：The virtual machine's operating system has attempted to enable promiscuous mode on adapter 'Ethernet0'.</p>
<p>要解决此问题，需要在宿主机上执行：</p>
<pre class="crayon-plain-tag"># vmnet0改为实际使用的Vmware虚拟网络
sudo chmod a+rw /dev/vmnet0</pre>
<div class="blog_h3"><span class="graybg">无法启动基于KVM的嵌套虚拟机</span></div>
<p>报错信息：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.</p>
<p>解决办法：VM ⇨ Settings，弹出的对话框中，选择Processors，在面板右侧勾选Virtualize Intel VT-X/EPT or AMD-V/RVI</p>
<div class="blog_h2"><span class="graybg">零散问题</span></div>
<div class="blog_h3"><span class="graybg">AppArmor导致无法启动QEMU虚拟机</span></div>
<p>报错信息：error: internal error: cannot load AppArmor profile 'libvirt-cf9a1d56-306d-4a6f-8b0e-79ac070aa8fe'</p>
<p>这个错误与安全模块AppArmor有关，你可以简单的禁用libvirt的QEMU驱动的安全驱动来规避：</p>
<pre class="crayon-plain-tag"># 添加
security_driver = "none"</pre>
<p>并执行：</p>
<pre class="crayon-plain-tag">sudo touch /etc/apparmor.d/disable/usr.sbin.libvirtd</pre>
<p>然后重启libvirtd：<pre class="crayon-plain-tag">sudo service libvirt-bin restart</pre>  </p>
<div class="blog_h3"><span class="graybg">无法启动桥接的QEMU虚拟机</span></div>
<p>报错信息：<br />error: internal error: process exited while connecting to monitor: failed to open /dev/net/tun: Operation not permitted<br />failed to launch bridge helper<br />qemu-system-x86_64: -netdev bridge,id=tap0,br=br0: Device 'bridge' could not be initialized</p>
<p>解决办法：</p>
<pre class="crayon-plain-tag"># 添加如下几行 

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</pre>
<div class="blog_h3"><span class="graybg">-daemonize选项导致启动QEMU虚拟机报错</span></div>
<p>报错信息：error: internal error: process exited while connecting to monitor</p>
<p>解决办法：从Domain配置文件中去掉<pre class="crayon-plain-tag">&lt;qemu:arg value='-daemonize'/&gt;</pre> 即可。</p>
<div class="blog_h3"><span class="graybg">Ubuntu 16.04 Server无法启动</span></div>
<p>症状：无法启动成功，配置SDL则可以启动，但是需要Headless的服务器。</p>
<p>解决办法：配置一个graphics，类型为VNC。</p>
<div class="blog_h3"><span class="graybg">显示相关报错：SDL无法初始化</span></div>
<p>这导致虚拟机无法启动，QEMU的SDL窗口不出现。</p>
<p>报错信息：error: internal error: process exited while connecting to monitor: Could not initialize SDL(No available video device) - exiting</p>
<p>解决办法：</p>
<ol>
<li>首先在宿主机执行<pre class="crayon-plain-tag">env | egrep "DISPLAY|XAUTH"</pre> ，获取两个环境变量的值</li>
<li>然后修改Domain配置文件，把type=sdl的graphics元素的display和xauth属性修改为上面命令输出的环境变量值：<br />
<pre class="crayon-plain-tag">&lt;graphics type='sdl' display=':0' xauth='/home/alex/.Xauthority'/&gt;</pre>
</li>
</ol>
<div class="blog_h3"><span class="graybg">找不到可启动硬盘</span></div>
<p>报错信息：</p>
<p>Booting from Hard Disk...<br />Boot failed: not abootable disk<br />No bootable device.</p>
<p>报错原因：可能是Domain配置文件搞错了镜像文件的格式。</p>
<p>解决办法：修改为正确的镜像格式，例如：<pre class="crayon-plain-tag">&lt;driver name='qemu' type='qcow2'/&gt;</pre> </p>
<div class="blog_h3"><span class="graybg">无法连接到客户机Console</span></div>
<p>报错信息：error: internal error: cannot find character device &lt;null&gt;</p>
<p>解决办法：可能是你的Domain配置中缺少Console和串口的配置，添加：</p>
<pre class="crayon-plain-tag">&lt;serial type='pty'&gt;
    &lt;target port='0'/&gt;
&lt;/serial&gt;
&lt;console type='pty'&gt;
    &lt;target type='serial' port='0'/&gt;
&lt;/console&gt;</pre>
<div class="blog_h3"><span class="graybg">如何退出客户机Console</span></div>
<p>要退出从virsh console进入的控制台，可以按组合键<pre class="crayon-plain-tag">Ctrl+]</pre> 。</p>
<div class="blog_h3"><span class="graybg">能连接到客户机Console但卡死</span></div>
<p>问题现象：光标在Escape character is ^]下面一行闪烁，但是按任何键都没有反应。</p>
<p>问题原因：客户机没有进行适当的配置。</p>
<p>解决办法： 通过其它方式登录到客户机，修改配置。</p>
<p><a id="console-to-ttys0"></a>以Fedora为例：</p>
<ol>
<li>修改/etc/sysconfig/grub（该文件是指向/etc/default/grub的符号链接）：<br />
<pre class="crayon-plain-tag"># 修改这一行，在命令行尾部添加console=...这个内核参数
GRUB_CMDLINE_LINUX="... console=ttyS0,115200"</pre>
</li>
<li>更新GRUB：<pre class="crayon-plain-tag">grub2-mkconfig &gt; /boot/grub2/grub.cfg</pre>  </li>
<li>执行命令<pre class="crayon-plain-tag">echo ttyS0 &gt;&gt; /etc/securetty</pre> 并重启客户机</li>
</ol>
<p>现在你可以使用virsh console连接到客户机，并执行Shell命令了。</p>
<div class="blog_h3"><span class="graybg">Console窗口太小</span></div>
<p>当通过串口（上面的方式）连接到Linux时，缺乏协商机制（Negotiate About Window Size ，NAWS），Linux也不知道你的（通过串口连接的）控制台屏幕屏幕大小。</p>
<p>在这种情况下，通常会使用<pre class="crayon-plain-tag">stty -a</pre>命令显示的结果，来设置窗口尺寸，往往不靠谱，偏小。</p>
<p>你可以在.bashrc中增加<pre class="crayon-plain-tag">/usr/bin/resize &gt; /dev/null</pre>，resize是来自xterm的工具，它可以使用VT100-style的光标位置来判断屏幕尺寸。resize会改变stty输出结果。</p>
<div class="blog_h3"><span class="graybg">无法删除外部快照</span></div>
<p>报错信息：unsupported configuration: deletion of 1 external disk snapshots not supported yet</p>
<p>解决办法：目前libvirt无法删除外部快照的磁盘文件，因此我们仅仅需要删除它的元数据，调用snapshot-delete时添加--metadata参数。磁盘文件可以手工删除。</p>
<div class="blog_h3"><span class="graybg">虚拟局域网（nat方式）无法连通</span></div>
<p>问题现象：无法ping通网桥，无法ping通外网，Vmware无法桥接到libvirt创建的网桥</p>
<p>原因分析：</p>
<p>在我的机器上，外网是通过Wifi连接的。如果设置虚拟局域网为net-autostart，则虚拟局域网启动时Wifi可能尚未连接，这会导致客户机无法连通外网的情况，原因未知。</p>
<p>将Vmware workstation中的虚拟机也桥接到virbr0时，提示无法连接。出现这种现象的原因也未知，但是先启动一个libvirt管理的客户机“激活”一下VLAN，然后启动Vmware就没事了。</p>
<p>解决办法：在Wifi连接后，启动VLAN、一台libvirt管理的虚拟机，然后再启动Vmware。</p>
<div class="blog_h3"><span class="graybg">使用QEMU驱动访问NFS报错：Permission denied</span></div>
<p>设置NFS导出时，要启用no_root_squash选项，让NFS客户端（QEMU宿主机）以root权限访问NFS：</p>
<pre class="crayon-plain-tag">/home/alex/Vmware/libvirt/images/default  10.0.0.0/8(rw,no_root_squash) </pre>
<p>然后，参考本文针对问题《AppArmor导致无法启动QEMU虚拟机》、 《无法启动桥接的QEMU虚拟机》的解决方案。</p>
<div class="blog_h3"><span class="graybg">启动或迁移时报错：Unable to get index for interface  ***: No such device</span></div>
<p>可能是因为虚拟局域网配置错误：</p>
<pre class="crayon-plain-tag">&lt;network&gt;
  &lt;name&gt;default&lt;/name&gt;
  &lt;forward mode="bridge"&gt;
    &lt;interface dev="eth0"/&gt; &lt;!-- 必须和宿主机的以太网设备名称一致 --&gt;
  &lt;/forward&gt;
&lt;/network&gt; </pre>
<div class="blog_h3"><span class="graybg">迁移时报错：Unsupported machine type</span></div>
<p>在Domain配置文件中指定宿主机集群中通用的机器类型，避免此问题：</p>
<pre class="crayon-plain-tag">&lt;os&gt;
    &lt;type arch='x86_64' machine='pc-i440fx-2.0'&gt;hvm&lt;/type&gt;
&lt;/os&gt;</pre>
<div class="blog_h3"><span class="graybg">迁移时报错，无法连接到49152端口</span></div>
<p>报错信息：</p>
<p style="padding-left: 30px;">unable to connect to server at 'Zircon:49152': Connection refused <br />unable to connect to server at 'centos.local:49152': No route to host</p>
<p>报错原因：默认情况下，迁移的源使用目的主机的主机名来建立迁移连接，出现此错误说明源无法正确连接到目标主机——Zircon，可以ping验证一下</p>
<p>解决方案：</p>
<ol>
<li>修改目的主机libvirt的QEMU配置：<br />
<pre class="crayon-plain-tag"># 指向目的主机可被迁移源访问的IP地址或者DNS名称
migration_host = "zircon.local"</pre>
</li>
<li>
<p> 老版本的libvirt可能不支持上述配置，这时可以配置DNS服务器或者迁移源的/etc/hosts文件</p>
</li>
<li>No route to host错误可能是因为目标主机的防火墙导致，注意检查设置</li>
</ol>
<div class="blog_h3"><span class="graybg">迁移时报错，Unable to read from monitor: Connection reset by peer</span></div>
<p>问题现象：迁移进度到达100%报如上错误。这个报错很笼统，详细的错误信息可以在目的宿主机的客户机运行日志（<pre class="crayon-plain-tag">/var/log/libvirt/qemu/$VM.log</pre>  ）中看到：</p>
<pre class="crayon-plain-tag">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</pre>
<div class="blog_h3"><span class="graybg">启动后很快进入pause状态</span></div>
<p>可能是因为卷所在的物理磁盘空间不足。</p>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/libvirt-study-note">libvirt学习笔记</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/libvirt-study-note/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
