1. 總體流程
翻譯自原文(英文):https://ilearnstack.com/2013/04/26/request-flow-for-provisioning-instance-in-openstack/
創建虛機的請求流如下:
- Dashboard 或者 CLI 獲取用戶的登錄信息,調用 Keystone 的 REST API 去做用戶身份驗證。
- Keystone 對用戶登錄信息進行校驗,然后產生驗證token並發回。它會被用於后續 REST 調用請求。
- Dashboard 或者 CLI 將創建虛機的 REST請求中的‘launch instance’ 或‘nova-boot’ 部分進行轉換,然后調用 nova-api 的 REST 接口。
- nova-api 接到請求,向 keystone 發送 auth-token 校驗和權限認證請求。
- Keystone 校驗 token,並將 auth headers 發回,它包括了 roles 和 permissions。
- nova-api 和 nova-database 進行交互。
- nova-database 為新實例創建一個數據庫條目。
- nova-api 向 nova-scheduler 發送 rpc.call 請求,期望它能通過附帶的 host ID 獲取到數據庫條目。
- nova-scheduler 從 queue 中獲取到請求。
- nova-scheduler 和 nova-database 交互,獲取集群中計算節點的信息和狀態。
- nova-scheuler 通過過濾(filtering)和稱重(weighting)找到一個合適的計算節點(host)。
- nova-scheduler 向找到的那個host上的 nova-compute 發送 rpc.cast 請求去啟動虛機。
- 目標 host 上的 nova-compute 從 queue 中獲取到請求。
- nova-compute 向 nova-condutor 發送 rpc.call 請求去獲取待創建虛機的信息比如 host ID 和 flavor 等。
- nova-conductor 從queue 中獲取到請求。
- nova-conductor 和 nova-database 交互。
- nova-database 向 nova-conductor 返回虛機的信息。
- nova-conductor 向 nova-compute 發送 rpc.call,附帶所請求的信息。圖中應該是遺漏了一個步驟,就是 nova-compute 從queue 中獲取返回的數據。
- nova-compute 調用 glance-api 的 REST API,傳入 auth-token,去根據鏡像 ID 獲取鏡像 URI,從鏡像存儲中下載(原文為upload)鏡像。
- glance-api 向 keystone 校驗 auth-token。
- nova-compute 獲取 image 的元數據。
- nova-compute 調用 Neutron API ,傳入 auth-token,去分配和配置網絡,比如虛機的IP地址。
- neutron-server 通過 keystone 校驗 auth-token。
- nova-compute 獲得網絡信息。
- nova-compute 調用 Cinder API,傳入 auth-token,去將 volume 掛接到實例。
- cinder-api 通過 keystone 校驗 auth-token。
- nova-compute 獲得塊存儲信息。
- nova-compute 為 hypervisor driver 產生數據,並調用 Hypersior 執行請求(通過 libvirt 或者 api)。
下表列出了每個步驟中實例的狀態:
Status | Task | Power state | Steps |
Build | scheduling | None | 3-12 |
Build | networking | None | 22-24 |
Build | block_device_mapping | None | 25-27 |
Build | spawing | None | 28 |
Active | none | Running |
2. nova compute 接到創建虛機指令后開始創建虛機的代碼流程分析 (第代碼在 https://github.com/openstack/nova/blob/master/nova/compute/manager.py 中的 def _build_and_run_instance 函數中
_build_resources // 准備網絡和磁盤 //Start building networks asynchronously for instance self._build_networks_for_instance //為 instance 准備網絡資源,實際上是創建一個 neutron port macs = self.driver.macs_for_instance(instance) //分配mac地址。多絕大多數hypersivor,返回None,也就是不預先分配 network_info =self._allocate_network //開始異步網絡分配 nwinfo = self.network_api.allocate_for_instance //Allocate network resources for the instance _validate_requested_port_ids //校驗 port ids _validate_requested_network_ids //校驗 network ids _clean_security_groups //刪除 default 安全組 _process_security_groups //Processes and validates requested security groups for allocation _create_ports_for_instance //Create port for network_requests that don't have a port_id _create_port_minimal //如果port 沒有的話,則Attempts to create a port for the instance on the given network. port_client.create_port //調用 port api 來創建 port,包括創建 port,分配MAC及IP地址,更新數據庫
_generate_mac //生成MAC地址
_create_port_with_mac //創建 port
//DHCP 相關操作:port 創建完成后會通知 neutron-dhcp-agent去執行port_create_end函數,它會將port的ip和mac信息加載到dnsmasq所需的配置文件中
_allocate_ips_for_port //為 port 分配 IP,要么用戶有指定,要么從subnets 中選擇一個
_allocate_specific_ip //如果指定了IP
_generate_ip //如果沒指定IP
return requests_and_created_ports _update_ports_for_instance //為特殊case 更新 port _populate_neutron_extension_values _populate_pci_mac_address //只用於處理 SRIOV_PF _populate_mac_address _update_port port_client.update_port //將上述修改通過調用 port api 得以更新port _update_port_dns_name neutron.update_port(port_id, port_req_body) //將 port 的 dns_name 設置為 hostname nw_info = self.get_instance_nw_info _build_network_info_model //Return list of ordered VIFs attached to instance _gather_port_ids_and_networks //Return an instance's complete list of port_ids and networks ifaces = instance.get_network_info() return nw_info
//Start building block device mappings for instance self._prep_block_device //處理掛接到虛機的磁盤,可能有多個,目標可能是 volume,snapshot 和 image。 當是 image 時,其實就是 boot from volume
block_device_info = driver.get_block_device_info(instance, bdms) //Converts block device mappings for instance to driver format mapping = driver.block_device_info_get_mapping(block_device_info) return block_device_mapping driver_block_device.attach_block_devices //nova/virt/block_device.py _log_and_attach //寫日志關於instance 將從哪里啟動,可能從 volume,snapshot,image 上啟動 bdm.attach //這里會調用 cinder api 去創建 volume,並且掛接到 nova compute host 上
//為每個磁盤調用 _log_and_attach _block_device_info_to_legacy return block_device_info
self.driver.spawn //調用 nova/virt/libvirt/driver.py 中的 spawn 函數,首先創建 image,然后創建domain disk_info = blockinfo.get_disk_info _create_configdrive _create_image //創建鏡像
fileutils.ensure_tree(libvirt_utils.get_instance_path(instance)) //在計算節點本地創建目錄 /var/lib/nova/instances/${uuid}/
_create_and_inject_local_root
if not booted_from_volume //判斷是否從 volume 啟動
backend = self.image_backend.by_name //根據配置的 image type 獲取 image backend,現在支持 rbd(使用 rbd volume),lvm(使用 lvm volume),qcow2(使用本地文件),flat 和 Ploop 等幾種。
if backend.SUPPORTS_CLONE: //判斷 backend 是否支持 clone
backend.clone(context, disk_images['image_id']) //調用 backend 的 clone 方法
RbdProxy().clone //RBD 的clone 方法,從 image 的 snapshot 上 clone
else
fetch_func = libvirt_utils.fetch_image //從 Glance 下載鏡像
_try_fetch_image_cache
self._inject_data(backend, instance, injection_info) //處理數據注入
else
//對boot volume 不允許注入 _create_ephemeral
libvirt_utils.create_image('raw', target, '%dG' % ephemeral_size)
utils.execute('qemu-img', 'create', '-f', disk_format, path, size) _create_swap
libvirt_utils.create_image('raw', target, '%dM' % swap_mb)
nova.privsep.fs.unprivileged_mkfs('swap', target) xml = self._get_guest_xml //生成 domain xml 字符串
conf = self._get_guest_config
_get_guest_numa_config
_get_guest_memory_backing_config
_get_guest_config_meta
_update_guest_cputune
_cpu_config_to_vcpu_model
_configure_guest_by_virt_type
_set_features
_set_clock
_get_guest_storage_config
self.vif_driver.get_config
_create_consoles
_guest_add_spice_channel
_add_video_driver
_set_qemu_guest_agent
_guest_add_pci_devices
_guest_add_watchdog_action
_guest_add_memory_balloon
_guest_add_mdevs
return guest
xml = conf.to_xml()
return xml
self._create_domain_and_network
//nova側去等待neutron側發送network-vif-pluggend事件。neutron-linuxbridge-agent服務檢測tap設備,neutron-server發送event事件給nova-api
self.virtapi.wait_for_instance_event
self.plug_vifs(instance, network_info)
self.firewall_driver.setup_basic_filtering
self.firewall_driver.prepare_instance_filter
self._create_domain(xml) libvirt_guest.Guest.create(xml, self._host) write_instance_config // nova/virt/libvirt/host.py 中 domain = self.get_connection().defineXML(xml) return libvirt_guest.Guest(domain) return guest _wait_for_boot //每隔 0.5秒檢查虛機是否啟動 _update_instance_after_spawn _update_scheduler_instance_info scheduler_client.update_instance_info _notify_about_instance_usage
補充:
- port 創建成功后的 dhcp 相關操作(參考 https://blog.csdn.net/gj19890923/article/details/51558598):
-
1. 創建VM時,nova-compute與neutron的plugin交互,在neutron的數據庫中創建VM所需的port信息。
-
2. neutron數據庫中的port信息創建完成后,通知neutron-dhcp-agent去執行port_create_end函數。該函數將數據庫中的port中的ip和mac信息加載到dnsmasq所需的配置文件中(包括host和addn_hosts文件)。
-
[root@nova 43c0e274-28e3-482e-a32b-d783980fc3ed]# cat addn_hosts 1.1.1.1 host-1-1-1-1.openstacklocal host-1-1-1-1 1.1.1.2 host-1-1-1-2.openstacklocal host-1-1-1-2 1.1.1.10 host-1-1-1-10.openstacklocal host-1-1-1-10 [root@nova 43c0e274-28e3-482e-a32b-d783980fc3ed]# cat host fa:16:3e:d1:d7:72,host-1-1-1-1.openstacklocal,1.1.1.1 fa:16:3e:da:42:50,host-1-1-1-2.openstacklocal,1.1.1.2 fa:16:3e:3c:a3:3e,host-1-1-1-10.openstacklocal,1.1.1.10
[root@nova 43c0e274-28e3-482e-a32b-d783980fc3ed]# cat leases
1464599134 fa:16:3e:3c:a3:3e 1.1.1.10 host-1-1-1-10 01:fa:16:3e:3c:a3:3e
1464598886 fa:16:3e:da:42:50 1.1.1.2 host-1-1-1-2 *
1464598886 fa:16:3e:d1:d7:72 1.1.1.1 host-1-1-1-1 *
-
3. 在VM啟動時,廣播dhcp discover請求,當dnsmasq進程的監聽接口ns-xxx監聽到這種請求時,dnsmasq進程將根據配置文件(host和leases文件)中的內容去判定是否有未分配的ip和mac為請求者進行提供。
-
4. 最終VM便真實的獲取到與保存在數據庫中的ip和mac信息。neutron-dhcp-agent只是將所創建VM的ip和mac信息從數據庫中獲取到自己的配置文件中,然后等到VM啟動時,為它提供。因此neutron-dhcp-agent相當於在VM和數據庫之間起了個中間橋梁的作用。
-
- nova 在 domain 被創建后等待 neutron event 的過程(請參考 http://www.aichengxu.com/linux/9307663.htm)
-
- 創建VM時,nova-compute 服務調用 wait_for_instance_event 函數等待 neutron 側發送來的event。
- neutron的neutron-linuxbridge-agent定時檢測tap設備的增加或刪除,當創建VM時,將創建新的tap設備,此時將更新neutron數據庫中的ports表,而neutron-server服務創建core_plugin時,將利用sqlalchemy自帶的event對neutron數據庫中的ports表進行監視,當ports表發生變化時,neutron-server將通過HTTP請求的方式發送event事件給nova。
- nova 收到 neutron 發送來的 event 時間,結束等待,繼續創建VM下面的操作。
3. 虛機被創建后的L2網絡操作
虛機被創建后,nova-compute 節點上的 neutron-linuxbridge-agent 會檢測到新建的 tap 設備(通過輪詢 /sys/class/net/ 里面的tap 設備),找到后則執行一系列網絡方面的操作,包括設置安全組,
tap設備示例:
[root@test net]# ls
brq8165bc3d-40 eth0 eth1 eth1.120 eth2 lo tap712a2c63-e6 tap83e7c095-f0 tap8f4fcfbb-2b
tap 設備信息:
Port tap93121330-58 updated. Details: {u'profile': {}, u'allowed_address_pairs': [], u'admin_state_up': True, u'network_id': u'8165bc3d-400a-48a0-9186-bf59f7f94b05', u'segmentation_id': 120,u'device_owner': u'compute:nova',
u'physical_network': u'physnet1', u'mac_address': u'fa:16:3e:9f:6f:c5', u'device': u'tap93121330-58', u'port_security_enabled': True, u'port_id': u'93121330-58', u'fixed_ips': [{u'subnet_id': u'ec1028b2-7cb0-4feb-b974-6b8ea7e7f08f', u'ip_address': u'172.16.0.7'}], u'network_type': u'vlan'}
參考文檔: