libvirt 簡介
libvirt 是目前使用最為廣泛的對 KVM 虛擬機進行管理的工具和應用程序接口。
libvirt 可以支持多種 hypervisor,包括 Xen,Hyper-V 和 qemu-kvm 等。同時,相比於使用 qemu 通過參數指定創建虛擬機的方式,使用 libvirt 創建虛機更簡單,更人性化。
libvirt 由應用程序接口 API,守護進程 libvirtd 和 libvirt 的默認管理工具組成。其中,libvirt API 為其它虛機管理工具,如 virsh,virt-manager 等提供應用程序接口,使得這些虛機管理工具通過調用 libvirt 的 API 實現虛機的管理。libvirtd 負責執行節點上虛機(domain) 的管理工作。libvirt 的默認管理工具是 virsh,它是 libvirt 默認提供的虛機的管理工具,通過 virsh 提供的一系列接口命令可以實現對虛機的管理。
libvirt XML 配置
libvirt 有幾個重要概念,分別是:
-
節點 Node:節點是虛機(domain) 運行的物理機器,hypervisor 也運行在節點之上。
-
domain: 虛機在 libvirt 中表示為 domain(域),一個 domain 就是一個虛機。
-
hypervisor:虛擬機監控器,KVM 就是使用硬件輔助的全虛擬化方案的 hepervisor。
在 libvirt 中創建 domain 的流程是:通過 XML 文件定義好 domain 的配置,然后,virsh 根據定義好的 XML 文件創建指定 domain。
domain XML 配置
disk(磁盤)
|
任何磁盤設備,包括軟盤(floppy)、硬盤(hard disk)、光驅(cdrom)或者半虛擬化驅動都使用 <disk> 元素來定義。方式:
<disk type='**' device='**'>。其中:
type 用來指定device source 的類型:"file", "block", "dir", "network", 或者 "volume"。具體的 source 由 <source> 標簽定義。
device 用來指定 device target 的類型:"floppy", "disk", "cdrom", and "lun", 默認為 "disk" 。具體的 target 由 <target> 標簽定義。
(1)volume 類型的 disk
<disk type='volume' device='disk'> <driver name='qemu' type='raw'/> <source pool='blk-pool0' volume='blk-pool0-vol0'/> <target dev='hdk' bus='ide'/> </disk>
(2)file 類型的 disk
<disk type='file' snapshot='external'> <driver name="tap" type="aio" cache="default"/> <source file='/var/lib/xen/images/fv0' startupPolicy='optional' /> <target dev='hda' bus='ide'/> </disk>
(3)block 類型的 disk
<disk type='block' device='cdrom'> <driver name='qemu' type='raw'/> <target dev='hdd' bus='ide' tray='open'/> <readonly/> </disk>
(4)network 類型的 disk
<disk type='network' device='cdrom'> <driver name='qemu' type='raw'/> <source protocol="http" name="url_path"> <host name="hostname" port="80"/> </source> <target dev='hde' bus='ide' tray='open'/> <readonly/> </disk> |
Host device assignment (主機設備分配)
|
<hostdev mode='subsystem' type='usb'> #USB 設備直接分配 <source startupPolicy='optional'> <vendor id='0x1234'/> <product id='0xbeef'/> </source> <boot order='2'/> </hostdev> <hostdev mode='subsystem' type='pci' managed='yes'> #PCI 設備直接分配 <source> <address domain='0x0000' bus='0x06' slot='0x02' function='0x0'/> </source> <boot order='1'/> <rom bar='on' file='/etc/fake/boot.bin'/> </hostdev> |
Network interface (網卡)
|
有幾種 interface 類型:
(1)type = ‘network’ 定義一個連接 Virtual network 的 interface
<devices> <interface type='network'> <source network='default'/> #虛擬網絡的名稱為 ‘default’ </interface> ... <interface type='network'> <source network='default' portgroup='engineering'/> <target dev='vnet7'/> <mac address="00:11:22:33:44:55"/> <virtualport> <parameters instanceid='09b11c53-8b5c-4eeb-8f00-d84eaa0aaa4f'/> </virtualport> </interface> </devices>
(2)type=‘birdge’ 定義一個 Bridge to LAN(橋接到物理網絡)的interface:前提是主機上存在一個 bridge,該 bridge 已經連到物理 LAN
<interface type='bridge'> #連接到 br0 <source bridge='br0'/> </interface> <interface type='bridge'> #連接到br1 <source bridge='br1'/> <target dev='vnet7'/> <mac address="00:11:22:33:44:55"/> </interface> <interface type='bridge'> #連接到 Open vSwith bridge ovsbr <source bridge='ovsbr'/> <virtualport type='openvswitch'> <parameters profileid='menial' interfaceid='09b11c53-8b5c-4eeb-8f00-d84eaa0aaa4f'/> </virtualport> </interface>
(3)type=‘ethernet’定義一個使用指定腳本連接到 LAN 的 interface
<devices> <interface type='ethernet'> <target dev='vnet7'/> <script path='/etc/qemu-ifup-mynet'/> </interface> </devices>
(4)type=‘direct’ 定義一個直接連到物理網卡(Direct attachment to physical interface)的 interface:需要 Linux macvtap 驅動支持
<interface type='direct' trustGuestRxFilters='no'> <source dev='eth0' mode='vepa'/> </interface>
(5)type=‘hostdev’ 定義一個由主機 PCI 網卡直接分配(PCI Passthrough)的 interface:分配主機上的網卡給虛機
<devices> <interface type='hostdev' managed='yes'> <driver name='vfio'/> <source> <address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/> </source> <mac address='52:54:00:6d:90:02'/> <virtualport type='802.1Qbh'> <parameters profileid='finance'/> </virtualport> </interface> </devices> |
metadata(元數據)
|
domain 的 metadata,用來表示 domain 的屬性,從而區別於其它 domain:
<metadata> <nova:instance xmlns:nova="http://openstack.org/xmlns/libvirt/nova/1.0"> <nova:package version="20.0.2-0.20191230035951.27bfd0b.el8ost"/> <nova:name>ROBOT-TEST-IPV6-01-OAM-001</nova:name> <nova:creationTime>2020-07-11 17:55:37</nova:creationTime> <nova:flavor name="demo"> <nova:memory>32768</nova:memory> <nova:disk>40</nova:disk> <nova:swap>0</nova:swap> <nova:ephemeral>0</nova:ephemeral> <nova:vcpus>6</nova:vcpus> </nova:flavor> <nova:owner> <nova:user uuid="a511dc27164a476c9b28c4323d76b3a9">admin</nova:user> <nova:project uuid="8acc3ea78b924f77a30d0389303f6818">admin</nova:project> </nova:owner> <nova:root type="image" uuid="2d3a3798-208b-4a61-b2fa-171be08a7a3b"/> </nova:instance> </metadata> |
CPU
|
CPU 配置,cpu mode 有三種模式:host-model,custom 和 host-passthrough。
host-model:根據 node 上的 CPU 特性,在 domain 上選擇一個最接近的標准 CPU 型號,如果不指定 cpu mode,默認就是 host-model。
custom: 表示基於某個基礎 CPU 做個性化定制。
host-passthrough:直接將物理 CPU 特性暴露給虛擬機使用。
<cpu mode='host-model' check='partial'> <model fallback='allow'/> <topology sockets='3' cores='1' threads='2'/> # CPU topology,socker,core and threads <numa> <cell id='0' cpus='0-5' memory='33554432' unit='KiB' memAccess='shared'/> </numa> </cpu> |
cputune
|
cputune 標簽可對 cpu 進行更多調節:
<cputune> <shares>6144</shares> <vcpupin vcpu='0' cpuset='11'/> <vcpupin vcpu='1' cpuset='39'/> <vcpupin vcpu='2' cpuset='41'/> <vcpupin vcpu='3' cpuset='13'/> <vcpupin vcpu='4' cpuset='34'/> <vcpupin vcpu='5' cpuset='6'/> <emulatorpin cpuset='6,11,13,34,39,41'/> # vcpu 和 物理 cpu 的映射 </cputune> |
os
|
os 標簽定義操作系統架構,hvm 表示硬件輔助的虛擬機
<os> <type arch='x86_64' machine='pc-i440fx-rhel7.6.0'>hvm</type> <boot dev='hd'/> <smbios mode='sysinfo'/> </os> |
更多關於 libvirt XML 的配置可看這里。
virsh 創建 domain
virsh 是用於管理 domain 和 hypervisor 的命令行工具。
virsh 通過一系列命令實現對 domain 的管理和監控,宿主機和 hypersivor 的管理,網絡/存儲池和存儲卷的管理等等。可通過 help 命令查看詳細的解釋。
通用的 virsh 命令主要有:
virsh list 查看本地 node 的 domain:
[root@compute-1 admin]# virsh list Id Name State ---------------------------------------------------- 1331 instance-00001c38 running
virsh dominfo 查看本地 node 的 domain 信息:
[root@compute-1 admin]# virsh dominfo 1331 Id: 1331 Name: instance-00001c38 UUID: 50df6317-642b-48f3-9a53-fbc3c440f56f OS Type: hvm State: running CPU(s): 6 CPU time: 73898.6s Max memory: 33554432 KiB Used memory: 33554432 KiB Persistent: yes Autostart: disable Managed save: no Security model: none Security DOI: 0
virsh vcpuinfo 查看本地 node 上 domain 的 vcpu 信息:
[root@compute-1 admin]# virsh vcpuinfo 1331 VCPU: 0 CPU: 11 State: running CPU time: 12235.4s CPU Affinity: -----------y-------------------------------------------- VCPU: 1 CPU: 39 State: running CPU time: 12081.0s CPU Affinity: ---------------------------------------y----------------
virsh edit <domainID> 查看並且編輯 domain 的 XML 信息:
[root@compute-1 admin]# virsh edit 1331
virsh define 根據 XML 文件定義一個 domain,但是不啟動 domain:
[root@compute-1 admin]# virsh edit demo.xml
virsh create 根據 XML 文件創建一個 domain:
[root@compute-1 admin]# virsh create demo.xml
libvirt API
libvirt API 是一套 C 語言實現的管理虛擬機的,穩定高效的程序接口。virsh 就是通過 libvirt API 來實現 domain 的管理監控等操作的。
libvirt API 分為對本地的 API 調用和對遠端 node 的 API 調用。管理工具通過本地 URI 和 遠程 URI 實現這些調用。
本地 URI 的格式是:qemu:///session,連接本地 session 實例,該連接僅能管理當前用戶的 domain 資源;qemu:///system,連接本地 system 實例,管理特權用戶的 domain 資源。遠程 URI 的格式是: qemu+ssh:///root@example.com/system 和 qemu+ssh:///user@example.com/session 等。
[admin@compute-1 ~]$ virsh -c qemu:///system list error: failed to connect to the hypervisor error: Failed to connect socket to '/var/run/libvirt/libvirt-sock': Permission denied [admin@compute-1 ~]$ virsh -c qemu:///session list Id Name State ---------------------------------------------------- [admin@compute-1 ~]$ sudo su [root@compute-1 admin]# virsh -c qemu:///system list Id Name State ---------------------------------------------------- 1331 instance-00001c38 running 1332 instance-00001c3a running [root@compute-1 admin]# virsh list # virsh list 默認 list 當前用戶的 domain Id Name State ---------------------------------------------------- 1331 instance-00001c38 running 1332 instance-00001c3a running
libvirt API 提供了很多接口供管理工具調用。許多語言都提供了對 libvirt 的綁定,python 也實現了對 libvirt 的綁定,用戶可通過導入 libvirt 包實現對 libvirt API 的調用(需要事先安裝好 libvirt-python 軟件包),安裝好之后代碼可在 /usr/lib64/python2.7/site-packages/libvirt* 找到:
[lianhua@controller-0 ~]$ ll /usr/lib64/python2.7/site-packages/libvirt libvirt_lxc.py libvirt_lxc.pyo libvirtmod_qemu.so libvirt.py libvirt.pyo libvirt_qemu.pyc libvirt_lxc.pyc libvirtmod_lxc.so libvirtmod.so libvirt.pyc libvirt_qemu.py libvirt_qemu.pyo
通過一個簡單的 python 代碼實現對 libvirt API 的調用:
#!/usr/bin/python # Get domain info via libvirt python API # Test env: python2.7 and libvirt-python-2.0.0 on KVM host import libvirt import sys def createConnection(): conn = libvirt.openReadOnly(None) if conn == None: print "Failed to open connection to QEMU/KVM" sys.exit(1) else: print "connection successfully" return conn def getDomInfoByName(conn, name): try: localDom = conn.lookupByName(name) except: print 'Failed to get the domain info with name "%s"' % name return 1 print "domain id: %d name: %s " % (localDom.ID(), localDom.name()) print "domain state: %s " % (localDom.state(0)) print "domain info: %s " % (localDom.info()) print "vCPUS: %d " % localDom.maxVcpus() def getDomInfoByID(conn, id): try: localDom = conn.lookupByID(id) except: print 'Failed to get the domain info with id "%d"' % id return 1 print "lookup domain id: %d, name: %s" % (localDom.ID(), localDom.name()) def closeConnection(conn): print "close connection session" try: conn.close() except: print "Failed to close the session" return 1 if __name__ == '__main__': name1 = "instance-00002d2e" name2 = "notExist" id1 = 321 id2 = 999 conn = createConnection() getDomInfoByName(conn, name1) getDomInfoByName(conn, name2) getDomInfoByID(conn, id1) getDomInfoByID(conn, id2) closeConnection(conn)
查看執行結果,檢查是否調用到 libvirt API:
[root@compute-1 qemu-kvm]# virsh list Id Name State ---------------------------------------------------- 321 instance-00002d2e running [root@compute-1 qemu-kvm]# python libvirt-test.py connection successfully domain id: 321 name: instance-00002d2e domain state: [1, 5] domain info: [1, 33554432L, 33554432L, 3, 1546499830000000L] vCPUS: 3 libvirt: QEMU Driver error : Domain not found: no domain with matching name 'notExist' Failed to get the domain info with name "notExist" lookup domain id: 321, name: instance-00002d2e libvirt: QEMU Driver error : Domain not found: no domain with matching id 999 Failed to get the domain info with id "999" close connection session