KVM(六)Nova 通過 libvirt 管理 QEMU/KVM 虛機
1. Libvirt 在 OpenStack 架構中的位置
在 Nova Compute 節點上執行的 nova-compute 服務呼叫 Hypervisor API 去管理執行在該 Hypervisor 的虛機。Nova 使用 libvirt 管理 QEMU/KVM 虛機,還使用別的 API 去管理別的虛機。
libvirt 的實現程式碼在 /nova/virt/libvirt/driver.py 檔案中。
這裡是 OpenStack Hypervisor Matrix。
這裡是 每個 Linux 發行版裡面 libvirt, QEMU/KVM 的版本號
請注意Juno 版本 Nova 對 libvirt 和 QEMU 的各種最低版本要求:
功能 | 最低 libvirt 版本 | 最低 QEMU 版本 | 不支援的後果 |
所有 | 0.9.11 | Nova 不能使用 libvirt driver | |
支援 device callback |
1.1.1 | 不支援的話,就無法支援 Detach PCI/SR-IOV 裝置 | |
Live snapshot | 1.3.0 | 1.3.0 | 只能使用 Clod Snapshot |
掛載卷時設定卷的 block 大小(Block IO) | 0.10.2 | 不能使用的話,就不能設定卷的特定 block size,只能使用其預設的 block size。 | |
Block Job Info | 1.1.1 | 不能線上刪除卷的快照 (online deletion of volume snapshots) | |
Discard | 1.0.6 | 1.6.0 | |
NUMA topology | 1.0.4 | 無法獲取 node 的 NUMA topology 資訊,就無法將虛機的 vCPU 指定到特定的 node CPU 上,會影響虛機的效能 |
2. Nova 中 libvirt 的使用
Nova 使用 libvirt 來管理虛機,包括:
- 建立虛機
- 虛機的生命週期管理(參考這篇文件)
- 新增和刪除連線到別的網路的網絡卡 (interface)
- 新增和刪除 Cinder 卷 (volume)
2.1 建立 QEMU/KVM 虛機
建立虛機的配置有幾個來源:
- 使用者的選項,包括虛機的基本資訊,比如 name,flavor,image,network,disk等。
- image 的屬性,比如 hw_vif_model,hw_scsi_model 等。完整的供 libvirt API 使用的屬性列表 在這裡。
- 管理員在 nova.conf 中的配置
(注意:image 的元資料屬性的優先順序高於 nova.conf 中的配置。只有在沒有property的情況下才使用nova.conf中的配置)
建立虛機的過程的幾個主要階段:
(1)訊息由 nova-api 路由到某個 nova compute 節點 (API -> Scheduler -> Compute (manager) -> Libvirt Driver)
(2)呼叫 Neutron REST API 去準備網路。其返回的資料類似:
[VIF({'profile': {}, 'ovs_interfaceid': u'59cfa0b8-2f5c-481a-89a8-7a8711b368a2', 'network': Network({'bridge': 'br-int', 'subnets': [Subnet({'ips': [FixedIP({'meta': {}, 'version': 4, 'type': 'fixed', 'floating_ips': [], 'address': u'10.0.10.14'})], 'version': 4, 'meta': {'dhcp_server': u'10.0.10.11'}, 'dns': [], 'routes': [], 'cidr': u'10.0.10.0/24', 'gateway': IP({'meta': {}, 'version': 4, 'type': 'gateway', 'address': u'10.0.10.1'})})], 'meta': {'injected': False, 'tenant_id': u'74c8ada23a3449f888d9e19b76d13aab'}, 'id': u'a924e87a-826b-4109-bb03-523a8b3f6f9e', 'label': u'demo-net2'}), 'devname': u'tap59cfa0b8-2f', 'vnic_type': u'normal', 'qbh_params': None, 'meta': {}, 'details': {u'port_filter': True, u'ovs_hybrid_plug': True}, 'address': u'fa:16:3e:e0:30:e7', 'active': False, 'type': u'ovs', 'id': u'59cfa0b8-2f5c-481a-89a8-7a8711b368a2', 'qbg_params': None})]
(3)從 image 啟動話,nova 會呼叫 Glane REST API 後者 image metadata 和準備本地啟動盤
image metadata:
{u'status': u'active', u'deleted': False, u'container_format': u'bare', u'min_ram': 0, u'updated_at': u'2015-04-26T04:34:40.000000', u'min_disk': 0, u'owner': u'74c8ada23a3449f888d9e19b76d13aab', u'is_public': False, u'deleted_at': None, u'properties': {}, u'size': 13167616, u'name': u'image', u'checksum': u'64d7c1cd2b6f60c92c14662941cb7913', u'created_at': u'2015-04-26T04:34:39.000000', u'disk_format': u'qcow2', u'id': u'bb9318db-5554-4857-a309-268c6653b9ff'}
本地啟動盤:
{'disk_bus': 'virtio', 'cdrom_bus': 'ide', 'mapping': {'disk': {'bus': 'virtio', 'boot_index': '1', 'type': 'disk', 'dev': u'vda'}, 'root': {'bus': 'virtio', 'boot_index': '1', 'type': 'disk', 'dev': u'vda'}, 'disk.local': {'bus': 'virtio', 'type': 'disk', 'dev': 'vdb'}, 'disk.swap': {'bus': 'virtio', 'type': 'disk', 'dev': 'vdc'}}}
本地啟動盤的檔案資訊:
[email protected]:/home/s1# qemu-img info /var/lib/nova/instances/02699155-940f-4401-bc01-36220db80639/disk.local image: /var/lib/nova/instances/02699155-940f-4401-bc01-36220db80639/disk.local file format: qcow2 virtual size: 1.0G (1073741824 bytes) #由 flavor.ephemeral_disk 指定其 size disk size: 324K cluster_size: 65536 backing file: /var/lib/nova/instances/_base/ephemeral_1_default Format specific information: compat: 1.1 lazy refcounts: false [email protected]:/home/s1# qemu-img info /var/lib/nova/instances/02699155-940f-4401-bc01-36220db80639/disk.swap image: /var/lib/nova/instances/02699155-940f-4401-bc01-36220db80639/disk.swap file format: qcow2 virtual size: 30M (31457280 bytes) # 由 flavor.swap_disk 指定其size disk size: 196K cluster_size: 65536 backing file: /var/lib/nova/instances/_base/swap_30 Format specific information: compat: 1.1 lazy refcounts: false
(4)根據這些資訊,生成 domain xml,然後生成其配置使得它是一個永續性虛機 (呼叫 libvirt Python DefineXML API)。
一個從 image 啟動的 Domain 的配置 XML 例項(藍色部分是註釋說明):
<domain type="qemu"> 8352e969-0a25-4abf-978f-d9d0ec4de0cd instance-0000002f 51200 # guest.memory = flavor.memory_mb * units.Ki 即 50 * 1024 = 51200 <vcpu cpuset="0">1 #flavor.vcpus <nova:instance xmlns:nova="http://openstack.org/xmlns/libvirt/nova/1.0"> <nova:package version="2014.2.2"/> vm11 #input.name 2015-06-09 23:54:04 <nova:flavor name="tiny2"> #input.flavor 50 1 30 1 1 <nova:user uuid="bcd37e6272184f34993b4d7686ca4479">admin <nova:project uuid="74c8ada23a3449f888d9e19b76d13aab">admin <nova:root type="image" uuid="bb9318db-5554-4857-a309-268c6653b9ff"/> #input.source <sysinfo type="smbios"> # Nova 中寫死的 <entry name="manufacturer">OpenStack Foundation <entry name="product">OpenStack Nova <entry name="version">2014.2.2 <entry name="serial">03bb1a0f-ae04-4765-9f3c-d200a2540675 <entry name="uuid">8352e969-0a25-4abf-978f-d9d0ec4de0cd hvm #表示 Guest OS 需要 full virtualiaiton 支援 <boot dev="hd"/> #指定啟動盤 <smbios mode="sysinfo"/> #去讀取 的定義 # Soft Reboot 需要 ACPI 的支援,否則只能使用 Hard reboot。 https://bugs.launchpad.net/horizon/+bug/1346741 # 沒 APIC 的話,Windows Guest 會在 Xen 或者 KVM 上崩潰。 https://bugs.launchpad.net/nova/+bug/1086352 <clock offset="utc"/> #如果Guest OS 是 MS,則是 localtime,否則都是 utc <cpu mode="host-model" match="exact"> # 對於 KVM,如果 CONF.libvirt.cpu_mode 是 none,mode 則設為 "host-model"。具體可參考 https://wiki.openstack.org/wiki/LibvirtXMLCPUModel <topology sockets="1" cores="1" threads="1"/> #預設的時候,sockets 數目設為 vcpu 的數目,cores 和 threads 都設為 1. 可以通過設定 image 的 hw_cpu_topology 屬性來改變這裡的設定,具體請參考 https://blueprints.launchpad.net/nova/+spec/support-libvirt-vcpu-topology 以及 https://wiki.openstack.org/wiki/VirtDriverGuestCPUMemoryPlacement <disk type="file" device="disk"> # 從 image 啟動時候的啟動盤(flavor.root_disk) <driver name="qemu" type="qcow2" cache="none"/> <source file="/var/lib/nova/instances/8352e969-0a25-4abf-978f-d9d0ec4de0cd/disk"/> <target bus="virtio" dev="vda"/> #對於 KVM,disk 的 bus 為 "virtio",cdrom 的 bus 為 "ide",floppy 的 bus 為 "fdc" <disk type="file" device="disk"> #臨時分割槽 (falvor.ephemeral_disk) <driver name="qemu" type="qcow2" cache="none"/> <source file="/var/lib/nova/instances/8352e969-0a25-4abf-978f-d9d0ec4de0cd/disk.local"/> <target bus="virtio" dev="vdb"/> <disk type="file" device="disk"> #swap 分割槽 (flavor.swap_disk) <driver name="qemu" type="qcow2" cache="none"/> <source file="/var/lib/nova/instances/8352e969-0a25-4abf-978f-d9d0ec4de0cd/disk.swap"/> <target bus="virtio" dev="vdc"/> <interface type="bridge"> # 虛機通過網橋連線到 OVS <mac address="fa:16:3e:e0:30:e7"/> <model type="virtio"/> #該 type 可以由 image metadata hw_vif_type 指定。未指定的話,如果配置了 conf.libvirt.use_virtio_for_bridges = true (預設就是 true)的話,QEMU/KVM 會使用 virtio 型別。 <driver name="qemu"/> <source bridge="qbr59cfa0b8-2f"/> #qbr59cfa0b8-2f 連線虛機的 vNIC tap59cfa0b8-2f 和 qvb59cfa0b8-2f ,而 qvb59cfa0b8-2f 練到 OVS 的 br-int 上。 <target dev="tap59cfa0b8-2f"/> </interface> <serial type="file"> 當 CONF.serial_console.enabled = true 時,type 為 "tcp",使用 config 配置,其 XML 為 ;當為 false 時,使用 console.log 檔案,type 為 file。 <source path="/var/lib/nova/instances/8352e969-0a25-4abf-978f-d9d0ec4de0cd/console.log"/> <serial type="pty"/> #每個domain都有 type 為 "pty" 的 serial 配置。 <input type="tablet" bus="usb"/> #當 CONF.vnc_enabled = true 或者 CONF.spice.enabled = true 並且 CONF.spice.agent_enabled = false 時新增 tablet,type 和 bus 都是固定的。 <graphics type="vnc" autoport="yes" keymap="en-us" listen="0.0.0.0"/> #如果 CONF.vnc_enabled = true,那麼 keymap=CONF.vnc_keymap;listen=CONF.vncserver_listen #如果 CONF.vnc_enabled 或者 CONF.spice.enabled,則新增該 video 配置 <model type="cirrus"/> #如果 CONF.spice.enabled,則 type 為 qxl;否則為 cirrus。 <memballoon model="virtio"> #如果 CONF.libvirt.mem_stats_period_seconds >0 則新增 memballoon;對 KVM,model 固定為 "virtio" <stats period="10"/>
從 bootable volume 啟動的話,disk 部分為:
<disk type="file" device="disk"> <driver name="qemu" type="qcow2" cache="none"/> <source file="/var/lib/nova/instances/02699155-940f-4401-bc01-36220db80639/disk.local"/> <target bus="virtio" dev="vdb"/> <disk type="file" device="disk"> <driver name="qemu" type="qcow2" cache="none"/> <source file="/var/lib/nova/instances/02699155-940f-4401-bc01-36220db80639/disk.swap"/> <target bus="virtio" dev="vdc"/> 26446902-5a56-4c79-b839-a8e13a66dc7a
(5). 啟動 domain (呼叫 libvirt Python createWithFlags API)
2.2 新增 volume 到虛機 (nova volume-attach)
(1)使用 volume id 通過 volume driver 找到指定的 volume
(2)呼叫 volume driver 來建立主機和 Volume 之間的連線
主機資訊為:
{'ip': '192.168.1.15', 'host': 'compute2', 'initiator': 'iqn.1993-08.org.debian:01:a9f2b45c24f9'}
建立的 iSCSI 連線資訊為:
{u'driver_volume_type': u'iscsi', u'data': {u'access_mode': u'rw', u'target_discovered': False, u'encrypted': False, u'qos_specs': None, u'target_iqn': u'iqn.2010-10.org.openstack:volume-51da0d1f-0a17-4e7f-aeff-27438963348a', u'target_portal': u'10.0.2.41:3260', u'volume_id': u'51da0d1f-0a17-4e7f-aeff-27438963348a', u'target_lun': 1, u'auth_password': u'hXG64qrzEjNt8MDKnERA', u'auth_username': u'fKSAe6vhgyeG88U9kcBV', u'auth_method': u'CHAP'}}
volume 在主機上的磁碟為:
[email protected]:/home/s1# ls /dev/disk/by-path/ -ls total 0 0 lrwxrwxrwx 1 root root 9 Jun 10 12:18 ip-10.0.2.41:3260-iscsi-iqn.2010-10.org.openstack:volume-51da0d1f-0a17-4e7f-aeff-27438963348a-lun-1 -> ../../sdc Disk /dev/sdc: 1073 MB, 1073741824 bytes 34 heads, 61 sectors/track, 1011 cylinders, total 2097152 sectors Units = sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disk identifier: 0x00000000 Disk /dev/sdc doesn't contain a valid partition table
(3)通過 domain name 來找到指定 domain 物件 (通過呼叫 lookupByName API)
(4)生成 volume 連線的配置 xml,比如:
51da0d1f-0a17-4e7f-aeff-27438963348a
(5)呼叫 attachDeviceFlags API 將 volume 掛載到該虛機
2.3 新增連線到新的網路的 interface 給虛機 (nova interface-attach)
(1)執行 nova interface-attach,傳入 network-id,Neutron 會分配如下network info 給 Nova
VIF({'profile': {}, 'ovs_interfaceid': u'0142efee-7382-43ef-96e8-d0084ecc893c', 'network': Network({'bridge': u'br-int', 'subnets': [Subnet({'ips': [FixedIP({'meta': {}, 'version': 4, 'type': u'fixed', 'floating_ips': [], 'address': u'10.0.0.40'})], 'version': 4, 'meta': {u'dhcp_server': u'10.0.0.3'}, 'dns': [], 'routes': [], 'cidr': u'10.0.0.0/24', 'gateway': IP({'meta': {}, 'version': 4, 'type': u'gateway', 'address': u'10.0.0.1'})})], 'meta': {u'injected': False, u'tenant_id': u'74c8ada23a3449f888d9e19b76d13aab'}, 'id': u'01630966-b21f-4a6d-95ff-10c4575f1fe2', 'label': u'demo-net'}), 'devname': u'tap0142efee-73', 'vnic_type': u'normal', 'qbh_params': None, 'meta': {}, 'details': {u'port_filter': True, u'ovs_hybrid_plug': True}, 'address': u'fa:16:3e:14:32:d9', 'active': True, 'type': u'ovs', 'id': u'0142efee-7382-43ef-96e8-d0084ecc893c', 'qbg_params': None})
(2)執行下面的命令,將 Neutron 分配的 port 連線到 OVS
#新增 linux bridge brctl addbr qbr0142efee-73 #名字是 devname 的後半部分 brctl setfd qbr0142efee-73 0 brctl stp qbr0142efee-73 off tee /sys/class/net/qbr0142efee-73/bridge/multicast_snooping ip link add qvb0142efee-73 type veth peer name qvo0142efee-73 ip link set qvb0142efee-73 ip link set qvb0142efee-73 promisc on #在 OVS 上新增埠 ovs-vsctl --timeout=120 -- --if-exists del-port qvo0142efee-73 -- add-port br-int qvo0142efee-73 -- set Interface qvo0142efee-73 external-ids:iface-id=0142efee-7382-43ef-96e8-d0084ecc893c external-ids:iface-status=active external-ids:attached-mac=fa:16:3e:14:32:d9 external-ids:vm-uuid=8352e969-0a25-4abf-978f-d9d0ec4de0cd
(3)生成 interface 配置的xml,比如:
(4)呼叫 attachDeviceFlags API 來掛載該 interface 到虛機
至於其他的虛機操作,會在另一篇文章中描述。