1. 程式人生 > >KVM(六)Nova 通過 libvirt 管理 QEMU/KVM 虛機

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
不支援 image 設定 hw_disk_discard 屬性,具體參考 BluePrint
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 到虛機

 至於其他的虛機操作,會在另一篇文章中描述。