虚拟化技术:了解libvirt(三)
1. libvirt简介
libvirt是目前使用最为广泛的对KVM虚拟机进行管理的工具和应用程序接口,而且一些常用的虚拟机管理工具(如virsh、virt-install、virt-manager等)和云计算框架平台(如OpenStack、ZStack、OpenNebula、Eucalyptus等)都在底层使用libvirt的应用程序接口。
libvirt是为了更方便地管理平台虚拟化技术而设计的开放源代码的应用程序接口、守护进程和管理工具,它不仅提供了对虚拟化客户机的管理,也提供了对虚拟化网络和存储的管理。libvirt支持多种虚拟化方案,既支持包括KVM、QEMU、Xen、VMware、VirtualBox、Hyper-V等在内的平台虚拟化方案,也支持OpenVZ、LXC等Linux容器虚拟化系统,还支持用户态Linux(UML)的虚拟化。
Tips:在Python、Perl、Java、Ruby、PHP、OCaml等高级编程语言中已经有libvirt的程序库可以直接使用。
libvirt作为中间适配层,可以让底层Hypervisor对上层用户空间的管理工具是完全透明的,因为libvirt屏蔽了底层各种Hypervisor的细节,为上层管理工具提供了一个统一的、较稳定的接口(API)。通过libvirt,一些用户空间管理工具可以管理各种不同的Hypervisor和上面运行的客户机。
2. libvirt重要概念
2.1 域的管理
包括对节点上的域的各个生命周期的管理,如启动、停止、暂停、保存、恢复和动态迁移。还包括对多种设备类型的热插拔操作,包括磁盘、网卡、内存和CPU。当然不同的Hypervisor上对这些热插拔的支持程度有所不同。
2.2 远程节点的管理
只要物理节点上运行了libvirtd这个守护进程,远程的管理程序就可以连接到该节点进程管理操作,经过认证和授权之后,所有的libvirt功能都可以被访问和使用。libvirt支持多种网络远程传输类型,如SSH、TCP套接字、Unix domainsocket、TLS的加密传输等。
2.3 存储的管理
任何运行了libvirtd守护进程的主机,都可以通过libvirt来管理不同类型的存储,如创建不同格式的客户机镜像(qcow2、raw、qde、vmdk等)、挂载NFS共享存储系统、查看现有的LVM卷组、创建新的LVM卷组和逻辑卷、对磁盘设备分区、挂载iSCSI共享存储、使用Ceph系统支持的RBD远程存储,等等。当然在libvirt中,对存储的管理也是支持远程的。
2.4 网络的管理
任何运行了libvirtd守护进程的主机,都可以通过libvirt来管理物理的和逻辑的网络接口。包括列出现有的网络接口卡,配置网络接口,创建虚拟网络接口,网络接口的桥接,VLAN管理,NAT网络设置,为客户机分配虚拟网络接口,等等。
3. libvirt的组成
libvirt主要由3个部分组成,提供一个稳定、可靠、高效的应用程序接口,以便可以完成前面的4个管理功能。
3.1 应用程序接口
是为其他虚拟机管理工具(如virsh、virt-manager等)提供虚拟机管理的程序库支持。
3.2 libvirtd守护进程
负责执行对节点上的域的管理工作,在用各种工具对虚拟机进行管理时,这个守护进程一定要处于运行状态中。而且这个守护进程可以分为两种:一种是root权限的libvirtd,其权限较大,可以完成所有支持的管理工作;一种是普通用户权限的libvirtd,只能完成比较受限的管理工作。
3.3 virsh工具
virsh是libvirt项目中默认的对虚拟机管理的一个命令行工具。
4. libvirt的目录文件
[root@localhost libvirt]# tree
.
├── libvirt-admin.conf
├── libvirt.conf #针对virsh命令主要是关于客户端的配置文件,一般默认的就满足要求了
├── libvirtd.conf #主要是针对服务器端的配置文件,它提供了不同的安全选项,请求限制和日志选项。
├── lxc.conf
├── nwfilter #网络管理
│ ├── allow-arp.xml
│ ├── allow-dhcp-server.xml
│ ├── allow-dhcp.xml
│ ├── allow-incoming-ipv4.xml
│ ├── allow-ipv4.xml
│ ├── clean-traffic-gateway.xml
│ ├── clean-traffic.xml
│ ├── no-arp-ip-spoofing.xml
│ ├── no-arp-mac-spoofing.xml
│ ├── no-arp-spoofing.xml
│ ├── no-ip-multicast.xml
│ ├── no-ip-spoofing.xml
│ ├── no-mac-broadcast.xml
│ ├── no-mac-spoofing.xml
│ ├── no-other-l2-traffic.xml
│ ├── no-other-rarp-traffic.xml
│ ├── qemu-announce-self-rarp.xml
│ └── qemu-announce-self.xml
├── qemu #默认会将虚拟机的描述文件(XML)保存到/etc/libvirt/qemu/目录下
│ ├── linux-01.xml
│ ├── networks #虚拟机网络相关配置文件
│ │ ├── autostart
│ │ │ └── default.xml -> ../default.xml
│ │ └── default.xml
│ └── window7.xml
├── qemu.conf #libvirt对QEMU的驱动的配置文件,包括VNC、SPICE等
├── qemu-lockd.conf
├── secrets
├── storage #虚拟机存储域
│ ├── autostart
│ │ ├── default.xml -> /etc/libvirt/storage/default.xml
│ │ ├── image.xml -> /etc/libvirt/storage/image.xml
│ │ ├── iso.xml -> /etc/libvirt/storage/iso.xml
│ │ └── storage.xml -> /etc/libvirt/storage/storage.xml
│ ├── default.xml
│ ├── image.xml
│ ├── iso.xml
│ └── storage.xml
├── virtlockd.conf
└── virtlogd.conf
/etc/libvirt/libvirt.conf
libvirt.conf文件用于配置一些常用libvirt连接(通常是远程连接)的别名
/etc/libvirt/libvirtd.conf
libvirtd.conf是libvirt的守护进程libvirtd的配置文件,被修改后需要让libvirtd重新加载配置文件(或重启libvirtd)才会生效。
在libvirtd.conf文件中,用井号(#)开头的行是注释内容,真正有用的配置在文件的每一行中使用“配置项=值”(如tcp_port="16509")这样配对的格式来设置。
在libvirtd.conf中配置了libvirtd启动时的许多设置,包括是否建立TCP、UNIX domain socket等连接方式及其最大连接数,以及这些连接的认证机制,设置libvirtd的日志级别等。
/etc/libvirt/qemu.conf
qemu.conf是libvirt对QEMU的驱动的配置文件,包括VNC、SPICE等,以及连接它们时采用的权限认证方式的配置,也包括内存大页、SELinux、Cgroups等相关配置。
/etc/libvirt/qemu/目录
这就是用virt-manager工具创建的一个域(centos7,也就是虚拟机名),默认会将其配置文件保存到/etc/libvirt/qemu/目录下。而其中的networks目录保存了创建一个域时默认使用的网络配置。
在qemu目录下存放的是使用QEMU驱动的域的配置文件。
5. libvirt的使用
systemctl start libvirtd
systemctl reload libvirtd
systemctl status libvirtd
libvirtd守护进程的启动或停止,并不会直接影响正在运行中的客户机。libvirtd在启动或重启完成时,只要客户机的XML配置文件是存在的,libvirtd会自动加载这些客户的配置,获取它们的信息。当然,如果客户机没有基于libvirt格式的XML文件来运行(例如直接使用qemu命令行来启动的客户机),libvirtd则不能自动发现它。
6. 虚拟机配置详解
6.1 libvirt域的XML配置文件
在使用libvirt对虚拟化系统进行管理时,很多地方都是以XML文件作为配置文件的,包括客户机(域)的配置、宿主机网络接口配置、网络过滤、各个客户机的磁盘存储配置、磁盘加密、宿主机和客户机的CPU特性,等等。
基本格式
虚拟机XML配置文件以domain为根元素,domain根元素中包含多个其他元素。XML配置文件中的部分元素可以包含对应属性和属性值,用以详细地描述虚拟机信息,同一元素的不同属性使用空格分开。
XML配置文件的基本格式如下,其中label代表具体标签名,attribute代表属性,value代表属性值,需要根据实际情况修改。
<domain type='kvm'>
<name>centos7</name>
<memory attribute='value'>8</memory>
<vcpu>4</vcpu>
<os>
<label attribute='value' attribute='value'>
...
</label>
</os>
<label attribute='value' attribute='value'>
...
</label>
</domain>
6.2 libvirt的本质
通过libvirt启动客户机,经过文件解析和命令参数的转换,最终也会调用qemu命令行工具来实际完成客户机的创建。用这个XML配置(系统中默认的QEMU工具为/usr/libexec/qemu-kvm)
6.3 libvirt配置流程
- 创建一个根元素为domain的XML配置文件。
- 使用标签name,根据命名规则指定唯一的虚拟机名称。
- 配置虚拟CPU和虚拟内存等系统资源。
- 配置虚拟设备。
- 配置存储设备。
- 配置网络设备。
- 配置外部总线结构。
- 配置鼠标等外部设备。
- 保存XML配置文件。
6.3.1 虚拟机描述
-
domain:虚拟机XML配置文件的根元素,用于配置运行此虚拟机的hypervisor的类型。
属性type:虚拟化中domain的类型。虚拟化中属性值为kvm,比如还有xen等属性。
-
name:虚拟机名称。
虚拟机名称为一个字符串,同一个主机上的虚拟机名称不能重复,虚拟机名称必须由数字、字母、“_”、“-”、“:”组成,但不支持全数字的字符串,且虚拟机名称不超过64个字符。
虚拟机名称为centos7的配置
<domain type='kvm'>
<name>centos7</name>
...
</domain>
6.3.2 虚拟CPU和虚拟内存
-
vcpu:虚拟处理器的个数。
-
memory:虚拟内存的大小。
属性unit:指定内存单位,属性值支持KiB,MiB,GiB,TiB等。
-
cpu:虚拟处理器模式。
属性mode:表示虚拟CPU的模式。
- host-passthrough:表示虚拟CPU的架构和特性与主机保持一致。
- custom:表示虚拟CPU的架构和特性由此cpu元素控制。
子元素topology:元素cpu的子元素,用于描述虚拟CPU模式的拓扑结构。
- 子元素topology的属性socket、cores、threads分别描述了虚拟机具有多少个cpu socket,每个cpu socket中包含多少个处理核心(core),每个处理器核心具有多少个超线程(threads),属性值为正整数且三者的乘积等于虚拟CPU的个数。
<domain type='kvm'>
...
<vcpu>4</vcpu>
<memory unit='GiB'>8</memory>
<cpu mode='host-passthrough'>
<topology sockets='2' cores='2' threads='1'/>
</cpu>
...
</domain>
6.3.3 虚拟设备
虚拟机XML配置文件使用devices元素配置虚拟设备,包括存储设备、网络设备、总线、鼠标等。XML配置文件使用disk元素配置存储设备,disk常见的属性如表1所示,子元素及子元素属性如表2所示。
配置示例
按照“准备虚拟机镜像”操作完成虚拟机镜像准备后,可以使用如下XML配置文件示例,为虚拟机配置虚拟磁盘。
例如,该示例为虚拟机配置了两个IO线程,一个块磁盘设备,一个光盘设备和一个rbd磁盘,第一个IO线程分配给块磁盘设备使用。该块磁盘设备的后端介质为qcow2格式,且被作为优先启动盘。 在使用rbd磁盘前请确保已经安装qemu-block-rbd驱动,如未安装,请在root下使用如下命令进行安装:
yum install qemu-block-rbd
<domain type='kvm'>
...
<iothreads>2</iothreads>
<devices>
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2' cache='none' io='native' iothread="1"/>
<source file='/mnt/openEuler-image.qcow2'/>
<target dev='vda' bus='virtio'/>
<boot order='1'/>
</disk>
<disk type='file' device='cdrom'>
<driver name='qemu' type='raw' cache='none' io='native'/>
<source file='/mnt/openEuler-22.03-LTS-aarch64-dvd.iso'/>
<target dev='sdb' bus='scsi'/>
<readonly/>
<boot order='2'/>
</disk>
<disk type='network' device='disk'>
<driver name='qemu' type='raw' cache='none' />
<source protocol="rbd" name="rbd/vol2"/>
<host name="192.168.0.2" port="6789" />
<target dev='sdc' bus='scsi'/>
<boot order='3'/>
</disk>
...
</devices>
</domain>
上面的配置表示,使用qcow2格式的openEuler-image.qcow2镜像文件作为客户机的磁盘,其在客户机中使用virtio总线(使用virtio-blk驱动),设备名称为/dev/vda。
6.3.4 网络设备
XML配置文件可以配置虚拟网络设备,包括ethernet模式、bridge模式、vhostuser模式等。XML配置文件中使用元素“interface”,其属性“type”表示虚拟网卡的模式,可选的值有“ethernet”、“bridge”、“vhostuser”等,下面以“bridge”模式虚拟网卡为例介绍其子元素以及对应的属性。
配置示例
按照“准备虚拟机网络”创建了Linux网桥br0后,配置一个桥接在br0网桥上的virtio类型的虚拟网卡设备,对应的XML配置如下:
<domain type='kvm'>
...
<devices>
<interface type='bridge'>
<source bridge='br0'/>
<model type='virtio'/>
</interface>
...
</devices>
</domain>
type='bridge'表示使用桥接方式使客户机获得网络address用于配置客户机中网卡的MAC地址,source bridge='br0'表示使用宿主机中的br0网络接口来建立网桥model type='virtio'表示在客户机中使用virtio-net驱动的网卡设备。
如果按照“准备虚拟机网络”创建了OVS网桥,配置一个后端使用vhost驱动,且具有四个队列的virtio虚拟网卡设备。
<domain type='kvm'>
...
<devices>
<interface type='bridge'>
<source bridge='br0'/>
<virtualport type='openvswitch'/>
<model type='virtio'/>
<driver name='vhost' queues='4'/>
</interface>
...
</devices>
</domain>
NAT方式的虚拟网络配置
这里type='network'和source network='default'表示使用NAT的方式,并使用默认的网络配置,客户机将会分配到192.168.122.0/24网段中的一个IP地址。
由于配置使用了默认的NAT网络配置,可以在libvirt相关的网络配置中看到一个default.xml文件(/etc/libvirt/qemu/networks/default.xml),它具体配置了默认的连接方式,如下:
当然,使用NAT必须保证宿主机中运行着DHCP和DNS服务器一般默认使用dnsmasq软件查询。查询DHCP和
DNS服务的运行的命令行如下:
在使用NAT时,查看宿主机中网桥的使用情况如下:
其中vnet0这个网络接口就是客户机和宿主机网络连接的纽带。
说明:还有两种网络模式,用户模式网络由QEMU模拟实现整个TCP/IP协议栈,并且使用此协议栈提供一个虚拟的NAT网络。还有网卡设备直接分配(VT-d),可以采用PCI/PCI-e网卡将设备直接分配给客户机使用。
6.3.5 总线配置
总线是计算机各个部件之间进行信息通信的通道。外部设备需要挂载到对应的总线上,每个设备都会被分配一个唯一地址(由子元素address指定),通过总线网络完成与其他设备或中央处理器的信息交换。常见的设备总线有ISA总线、PCI总线、USB总线、SCSI总线、PCIe总线。
PCIe总线是一种典型的树结构,具有比较好的扩展性,总线之间通过控制器关联,这里以PCIe总线为例介绍如何为虚拟机配置总线拓扑。
说明:总线的配置相对比较繁琐,若不需要精确控制设备拓扑结构,可以使用libvirt自动生成的缺省总线配置。
在libvirt的XML配置中,每个控制器元素(使用controller元素表示)可以表示一个总线,根据虚拟机架构的不同,一个控制器上通常可以挂载一个或多个控制器或设备。这里介绍常用属性和子元素。
controller:控制器元素,表示一个总线。
- 属性type:控制器必选属性,表示总线类型。常用取值有“pci”、“usb”、“scsi”、“virtio-serial”、“fdc”、“ccid”。
- 属性index:控制器必选属性,表示控制器的总线“bus”编号(编号从0开始),可以在地址元素“address”元素中使用。
- 属性model:控制器必选属性,表示控制器的具体型号,其可选择的值与控制器类型“type”的值相关,对应关系及含义请参见表4。
- 子元素address:为设备或控制器指定其在总线网络中的挂载位置。
- 属性type:设备地址类型。常用取值有“pci”、“usb”、“drive”。address的type类型不同, 对应的属性也不同,常用type属性值及其该取值下address的属性请参见表5。
- 子元素model:控制器具体型号的名称。
- 属性name:指定控制器具体型号的名称,和父元素controller中的属性model对应。
配置示例
该示例给出一个PCIe总线的拓扑结构。PCIe根节点(BUS 0)下挂载了三个PCIe-Root-Port控制器。第一个PCIe-Root-Port控制器(BUS 1)开启了multifunction功能,并在其下挂载一个PCIe-to-PCI-bridge控制器,形成了一个PCI总线(BUS 3),该PCI总线上挂载了一个virtio-serial设备和一个USB 2.0控制器。第二个PCIe-Root-Port控制器(BUS 2)下挂载了一个SCSI控制器。第三个PCIe-Root-Port控制器(BUS 0)下无挂载设备。配置内容如下:
<domain type='kvm'>
...
<devices>
<controller type='pci' index='0' model='pcie-root'/>
<controller type='pci' index='1' model='pcie-root-port'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0' multifunction='on'/>
</controller>
<controller type='pci' index='2' model='pcie-root-port'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
</controller>
<controller type='pci' index='3' model='pcie-to-pci-bridge'>
<model name='pcie-pci-bridge'/>
<address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
</controller>
<controller type='pci' index='4' model='pcie-root-port'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
</controller>
<controller type='scsi' index='0' model='virtio-scsi'>
<address type='pci' domain='0x0000' bus='0x02' slot='0x00' function='0x0'/>
</controller>
<controller type='virtio-serial' index='0'>
<address type='pci' domain='0x0000' bus='0x03' slot='0x02' function='0x0'/>
</controller>
<controller type='usb' index='0' model='ehci'>
<address type='pci' domain='0x0000' bus='0x03' slot='0x01' function='0x0'/>
</controller>
...
</devices>
</domain>
6.3.6 其他常用设备
除存储设备、网络设备外,XML配置文件中还需要指定一些其他外部设备。
-
serial:串口设备
属性type:用于指定串口类型。常用属性值为pty、tcp、pipe、file。
-
video:媒体设备
属性type:媒体设备类型。AArch架构常用属性值为virtio,x86_64架构通常使用属性值为vga或cirrus。
子元素model:video的子元素,用于指定媒体设备类型。
在model元素中,type属性为vga表示配置VGA类型显卡,vram属性代表显存大小,单位默认为KB。
例如:给x86_64架构虚拟机配置16MB的VGA类型的显卡,XML示例如下,其中vram属性代表显存大小,单位默认为KB:
<video> <model type='vga' vram='16384' heads='1' primary='yes'/> </video>
-
input:输出设备
属性type:指定输出设备类型。常用属性值为tabe、keyboard,分别表示输出设备为写字板、键盘。
属性bus:指定挂载的总线。常用属性值为USB。
-
emulator:模拟器应用路径
-
graphics:图形设备
属性type:指定图形设备类型。常用属性值为vnc。
属性listen:指定侦听的IP地址。
配置示例
例如,在下面的示例中,配置了虚拟机的模拟器路径,pty串口、virtio媒体设备、USB写字板、USB键盘以及VNC图形设备。
说明:graphics的type配置为VNC时,建议配置属性passwd,即使用VNC登录时的密码。
<domain type='kvm'>
...
<devices>
<emulator>/usr/libexec/qemu-kvm</emulator>
<console type='pty'/>
<video>
<model type='virtio'/>
</video>
<input type='tablet' bus='usb'/>
<input type='keyboard' bus='usb'/>
<graphics type='vnc' listen='0.0.0.0' passwd='n8VfjbFK'/>
...
</devices>
</domain>
6.3.7 其他常见配置项
除系统资源和虚拟设备外,XML配置文件还需要配置一些其他元素。
-
iothreads:指定iothread数量,可以用于加速存储设备性能。
-
on_poweroff:虚拟机关闭时采取的动作。
-
on_reboot:虚拟机重启时采取的动作。
-
on_crash:虚拟机崩溃时采取的动作。
-
clock:采用的时钟类型。
属性offset:设置虚拟机时钟的同步类型,可选的值有“localtime”、“utc”、“timezone”、“variable”等。
配置示例
为虚拟机配置两个iothread,用于加速存储设备性能。
<iothreads>2</iothreads>
虚拟机关闭时,销毁虚拟机。
<on_poweroff>destroy</on_poweroff>
虚拟机重启时,重新启动虚拟机。
<on_reboot>restart</on_reboot>
虚拟机崩溃时,重新启动虚拟机。
<on_crash>restart</on_crash>
时钟采用“utc”的同步方式。
<clock offset='utc'/>
7.libvirt API
此部分为进阶内容,可以查阅《KVM实战:原理、进阶与性能调优》这本书