本文梳理一下 Nova 主要操作的流程。
0. Nova REST-CLI-Horizon 操作對照表
Nova 基本的 CRUD 操作和 extensions:
# | 類別 | Nova V2 REST API Action | Nova CLI | Horizon | 解釋 |
虛機操作 | POST | boot | Launch Instance | 啟動一個新的虛機 |
|
DELETE | delete | Terminate Instance | 關閉和刪除一個虛機
|
||
confirmResize | resize-confirm | N/A | 確認 resize 操作 |
||
revertResize | resize-revert | N/A | 取消 resize 操作 |
||
reboot | reboot [--hard] | Soft Reboot Instance Hard Reboot Instance |
重啟虛機,具體分析見本文 1. 章節 |
||
changePassword | root-password | libvirt driver 沒有實現 | |||
resize | resize | Resize Instance | 遷移虛機或者改變虛機的flavor: http://www.cnblogs.com/sammyliu/p/4572287.html | ||
rebuild | rebuild | Rebuild Instance | 先調用 driver.destroy (destroy domain,detach volume connections,plug VIF), 然后再調用 driver.spawn | ||
createImage | image-create | N/A | 快照。http://www.cnblogs.com/sammyliu/p/4468757.html | ||
os-start | start | Launch Instance | = hard reboot,見下面 1. 章節 | ||
os-stop | stop | Shut Off Instance | call dom.destroy | ||
admin action | pause | pause | Pause Instance | call dom.suspend | |
unpause | unpause | Resume Instance | call dom.resume | ||
suspend | suspend | N/A |
|
||
resume | resume | N/A |
|
||
migrate | migrate | N/A | http://www.cnblogs.com/sammyliu/p/4572287.html | ||
resetNetwork | N/A | libvirt 沒有實現 | |||
injectNetworkInfo | N/A | Set up basic filtering (MAC, IP, and ARP spoofing protection),調用 _conn.nwfilterDefineXML(xml) | |||
lock | lock | N/A | 直接在數據庫中 instance 上面設置 lock = true | ||
unlock | unlock | N/A | 直接在數據庫中 instance 上面設置 lock = false | ||
createBackup | backup | N/A | 同 createImage,可以指定類型(daily 或者 weekly),和保存的 image 的最大數目,老的 image 會被刪除 {"backup_type": "daily", "rotation": "2", "name": "bk"} |
||
os-migrateLive | live-migration | N/A | http://www.cnblogs.com/sammyliu/p/4572287.html | ||
os-resetState | reset-state | N/A | 傳入 state 參數,直接修改數據庫中 instance 的狀態 | ||
bare metal | add_interface | baremetal-interface-add | TBD | ||
remove_interface | baremetal-interface-remove | TBD | |||
cloudpipe | update | cloudpipe-configure | TBD | ||
console | os-getVNCConsole | get-vnc-console | Console | 見本文第四章節 |
|
os-getSPICEConsole | get-spice-console | TBD | |||
os-getRDPConsole | get-rdp-console | TBD | |||
os-getSerialConsole | TBD | ||||
os-getConsoleOutput | console-log | View Log | 讀取 虛機的 console.log 文件並返回其內容;如果沒有這文件的話,則使用 “pty”,將其內容寫入到 consolue文件並返回其內容。 | ||
delete | restore | restore | Terminate Instance | Restore a previously deleted (but not reclaimed) instance。直接修改數據庫。 | |
forceDelete | force-delete | N/A | 有 snapshot,則全部刪除;然后從 DB 中刪除 instance. |
||
evacuate | evacuate | evacuate | 從 DB 中讀取 instance 數據,在一個新的主機上 rebuild。 | ||
flavor access | addTenantAccess | flavor-access-add | Flavor - Modify Access | 修改 DB 中 flavor 表 | |
removeTenantAccess | flavor-access-remove | Flavor - Modify Access | 修改 DB 中 flavor 表 | ||
flavor manage | delete | flavor-delete | Flavor - Delete Flavor | 直接 DB 操作 | |
create | flavor-create | Flavor - Create Flavor | 直接 DB 操作 | ||
floating ip | addFloatingIp | floating-ip-create | Associate Floating IP | 調用 network_api.associate_floating_ip | |
removeFloatingIp | floating-ip-delete | Disassociate Floating IP | 調用 network_api.disassociate_floating_ip | ||
NIC | addFixedIp | fixed-ip-reserve | 參數 "networkId"。
|
||
removeFixedIp | fixed-ip-unreserve | 參數 "address"。
|
|||
network associate | disassociate_host | Associate or disassociate host or project to network。 call network_api.associate | |||
disassociate_project | network-disassociate | call network_api.associate | |||
associate_host | network-associate-host | call network_api.associate | |||
os network | disassociate | call network_api.associate | |||
rescue | rescue | rescue | 見本文第二章節 | ||
unrescue | unrescue | 見本文第二章節 | |||
security group | addSecurityGroup | add-secgroup | call security_group_rpcapi.refresh_security_group_rules | ||
removeSecurityGroup | secgroup-delete | call security_group_rpcapi.refresh_security_group_rules | |||
shelve | shelve | shelve | 見本文第三章節 | ||
shelveOffload | shelve-offload | 見本文第三章節 | |||
unshelve | unshelve | 見本文第三章節 |
1. Reboot (重啟)
物理機有兩種重啟方式:一種從操作系統中重啟,一種直接先斷電然后再接通電源。虛機的重啟也有類似的兩種方式:Soft Reboot 和 Hard Reboot。nova reboot 命令在不使用 “-hard” 情況下,默認會 Soft Reboot,如果失敗則會 Hard reboot;如果使用了 “-hard”,則直接 Hard reboot。
1.1 Soft reboot
Soft reboot 完全依賴於虛機的操作系統和 ACPI。其過程為:
- Shutdown domain (domain.shutdown)
- Launch domain (domain.createWithFlags)
關於 shutdown API ,它會shutdown一個虛機,但是虛機的各種附件(object)依然可以使用,只是虛機的操作系統被停止了。注意,虛機的操作系統可能會忽略該請求。這時候,shutdown 不會成功(在 CONF.libvirt.wait_soft_reboot_seconds 指定的時間內,nova發現虛機沒有被 shutdown),得轉到 hard reboot 了。(Shutdown a domain, the domain object is still usable thereafter, but the domain OS is being stopped. Note that the guest OS may ignore the request.)
1.2 Hard reboot
Hard reboot 比 Soft reboot 復雜、耗時長。其主要過程為:
- Destroy domain (virt_dom.destroy())。Destroy 和 Shutdown 兩個 API 的區別是:shutdown domain 以后,客戶機的磁盤存儲處於可用狀態,而且該 API 在命令發給 Guest OS 后立即返回,客戶程序需要不斷檢查其狀態來判斷是否shutdown 成功;而 destroy API 執行后,客戶機的所有資源都會被歸還給 Hypervisor (all resources used by it are given back to the hypervisor),它的過程是首先給客戶機發送一個terminate 指令比如SIGTERM,如果在一定的時間內未成功則直接發送 SIGKILL 指令將虛機殺掉。
- 根據 domain 的信息(instance, network_info, disk_info,image_meta, block_device_info)重新生成 domain 的配置 xml
- 根據 image 重新生成各鏡像文件(包括 disk,disk.local 和 disk.swap)
- 連接各個 volume
- 將各個 interface 掛到 OVS 上
- 重新生成和應用 iptales 規則
- Define domain (conn.defineXML)
- Launch domain (domain.createWithFlags)
可見,hard reboot 是真正的 reboot 一個虛機。來比較一下 hard reboot 前后的虛機的磁盤和xml 文件:
#reboot 前
root@compute2:/var/lib/nova/instances/8352e969-0a25-4abf-978f-d9d0ec4de0cd# ls -l total 2608 -rw-rw---- 1 root root 20126 Jun 10 12:38 console.log -rw-r--r-- 1 libvirt-qemu kvm 2162688 Jun 10 12:40 disk -rw-r--r-- 1 nova nova 246 Jun 10 07:54 disk.info -rw-r--r-- 1 libvirt-qemu kvm 393216 Jun 10 12:39 disk.local -rw-r--r-- 1 libvirt-qemu kvm 197120 Jun 10 07:54 disk.swap -rw-r--r-- 1 nova nova 3452 Jun 10 12:39 libvirt.xml
#reboot 后 root@compute2:/var/lib/nova/instances/8352e969-0a25-4abf-978f-d9d0ec4de0cd# ls -l total 2608 -rw-rw---- 1 root root 20126 Jun 10 12:38 console.log -rw-r--r-- 1 libvirt-qemu kvm 2162688 Jun 10 12:57 disk -rw-r--r-- 1 nova nova 246 Jun 10 07:54 disk.info -rw-r--r-- 1 libvirt-qemu kvm 393216 Jun 10 12:57 disk.local -rw-r--r-- 1 libvirt-qemu kvm 197120 Jun 10 07:54 disk.swap -rw-r--r-- 1 nova nova 3452 Jun 10 12:57 libvirt.xml root@compute2:/var/lib/nova/instances/8352e969-0a25-4abf-978f-d9d0ec4de0cd# diff libvirt.xml libvirt.xml.ori 10c10 < <nova:creationTime>2015-06-10 04:57:25</nova:creationTime> #domain 除了創建時間不同別的都相同 --- > <nova:creationTime>2015-06-10 04:39:14</nova:creationTime> 84c84 < <source host="127.0.0.1" mode="bind" service="10002"/> --- > <source host="127.0.0.1" mode="bind" service="10001"/>
2. Rescue (拯救)和 Unrescue
Rescue 是個很有意思的功能。它的一個使用場景是,虛機的啟動盤的一個文件被誤刪除了導致無法再次啟動了,或者 admin 的密碼忘記了。Rescue 功能提供一個解決這類問題的手段。
執行 nova rescue 命令后的主要過程是:
(1)保存目前domain 的 xml 配置到 unrescue.xml 文件
(2)根據 image 重新生成啟動盤 disk.swap (大小不受 falvor.root_disk_size 控制,盡可能小的一個文件)
root@compute2:/var/lib/nova/instances/8352e969-0a25-4abf-978f-d9d0ec4de0cd# qemu-img info disk.rescue image: disk.rescue file format: qcow2 virtual size: 39M (41126400 bytes) #不是 falovr 里面定義的 1G 大小 disk size: 1.6M cluster_size: 65536 backing file: /var/lib/nova/instances/_base/fbad3d96a1727069346073e51d5bbb1824e76e34 Format specific information: compat: 1.1 lazy refcounts: false
(3)構造一個新的 domain 的 xml 配置,使用 disk.rescue 做啟動盤,將原來的 disk 掛載到該 domain,其他的盤和volume不會被掛載。
<disk type="file" device="disk"> <driver name="qemu" type="qcow2" cache="none"/> <source file="/var/lib/nova/instances/8352e969-0a25-4abf-978f-d9d0ec4de0cd/disk.rescue"/> #新構造的啟動盤 <target bus="virtio" dev="vda"/> </disk> <disk type="file" device="disk"> <driver name="qemu" type="qcow2" cache="none"/> <source file="/var/lib/nova/instances/8352e969-0a25-4abf-978f-d9d0ec4de0cd/disk"/> #原來的啟動盤 <target bus="virtio" dev="vdb"/> </disk>
(4)將原來的 domain destroy 掉 (virt_dom.destroy)
(5)定義新的 domain (conn.defineXML(xml))
(6)啟動新的 domain (domain.createWithFlags)
至此,nova rescue 的過程完成。用戶可以 ssh 到新的虛機,修改 “Vdb“分區中的受損害的文件。然后執行 ”nova unrescue“命令。其主要過程是:
(1)讀取之前保存的 unrescue.xml 文件
(2)將 rescued domain destroy 掉
(3)定義和啟動新的domain(同上面5和6)
(4)刪除 unrescue.xml 文件
注意,這時候文件夾中的 libvirt.xml 文件和新運行的 domain 的 xml 不一致,因為代碼中沒有將新的 domain 的xml 寫到該文件。
3. Shelve (擱置)和 unshelve
當一個虛機不需要使用的時候,可以將其 shelve 起來。該操作會創建該虛機的一個快照並傳到 Glance 中,然后在 Hypervisor 上將該虛機刪除,從而釋放其資源。
其主要過程為:
- destroy 虛機 (virt_dom.destroy())
- snapshot 該 domain
- 如果 CONF.shelved_offload_time == 0 的話,將domain 的各種資源(interface,volume,實例文件等),然后將其undefine (virt_dom.undefine())
其只存在於數據庫和 Glance 中。運行 nova list 能看到一條記錄:
| 8352e969-0a25-4abf-978f-d9d0ec4de0cd | vm11 | SHELVED_OFFLOADED | - | Shutdown | demo-net2=10.0.10.14; demo-net=10.0.0.41 |
運行 glance image-list 能看到其image:
| 6ed6eb92-ce42-46d1-ab46-259e3e235304 | vm11-shelved | qcow2 | bare | 19988480 | active |
能看到該 image 的 instance 相關的屬性:
s1@controller:~$ glance image-show 6ed6eb92-ce42-46d1-ab46-259e3e235304 +---------------------------------------+--------------------------------------+ | Property | Value | +---------------------------------------+--------------------------------------+ | Property 'base_image_ref' | bb9318db-5554-4857-a309-268c6653b9ff | | Property 'image_location' | snapshot | | Property 'image_state' | available | | Property 'image_type' | snapshot | | Property 'instance_type_ephemeral_gb' | 1 | | Property 'instance_type_flavorid' | 129f237e-8825-49fa-b489-0e41fb06b70e | | Property 'instance_type_id' | 8 | | Property 'instance_type_memory_mb' | 50 | | Property 'instance_type_name' | tiny2 | | Property 'instance_type_root_gb' | 1 | | Property 'instance_type_rxtx_factor' | 1.0 | | Property 'instance_type_swap' | 30 | | Property 'instance_type_vcpus' | 1 | | Property 'instance_uuid' | 8352e969-0a25-4abf-978f-d9d0ec4de0cd | | Property 'network_allocated' | True | | Property 'owner_id' | 74c8ada23a3449f888d9e19b76d13aab | | Property 'user_id' | bcd37e6272184f34993b4d7686ca4479 |
Unshelve 是 shelve 的反操作。它的主要過程是:
- 從 DB 中獲取 network_info 和 block_device_info
- 從 Glance 中獲取 image
- 象新建一個虛擬那樣 spawn 新的虛機
- 調用 image API 將 image 刪除
4. VNC (VNC 連接)
VNC 操作的過程:(來源)
(1)用戶查詢 VNC 的訪問 URL。
Nova CLI 對應的命令為 “nova get-vnc-console <server> <console-type>”。 REST API 的數據格式為:{"os-getVNCConsole": {"type": "novnc"}}
“type” 參數值可以是 “novnc” 或者 “xvpvnc”。
(2)通過 RPC 調用虛機所在的 node 上的 Nova 生成一個 UUID(長度為 4)格式的 token,以及格式為 ‘<base_url>?token=<token>' 的 Access URL。
- base_url 從 CONF.novncproxy_base_url (比如 http://192.168.1.20:6080/vnc_auto.html)或者 CONF.xvpvncproxy_base_url 讀取。
(3)本地Nova 調用 libvirt driver 從 domain xml 中獲取 port,比如下面例子中的 5900,以及從 confi.vncserver_proxyclient_address 獲取 host。
<graphics type='vnc' port='5900' autoport='yes' listen='0.0.0.0' keymap='en-us'>
<listen type='address' address='0.0.0.0'/>
</graphics>
(4)通過 RPC,調用 consoleauth 的 authorize_console 方法,它將 Access URL, host,port 保存到 memcached。
(5)返回 Access URL 給客戶端,比如 http://192.168.1.20:6080/vnc_auto.html?token=8dc6f7cb-2e2d-4fbe-abab-3334fe3a19dc
在 Contoller 節點上運行着一個service “/usr/bin/python /usr/bin/nova-novncproxy --config-file=/etc/nova/nova.conf”。該 service 默認在 6080 端口(可由 CONF.novncproxy_port 配置)監聽來自所有地址(可由 CONF.novncproxy_host 配置)的請求。
(6)在客戶端瀏覽器上打開 Access URL,由 Controller 上的 nova-novncproxy 接收並處理
(7)nova-novncproxy 通過 RPC 調用 consoleauth.check_token() 方法,獲取 console_type 和 port 等 connect info。期間還會到目的node上去校驗這些信息。
{u'instance_uuid': u'7c53af66-765b-48f3-b8a2-a908feb63968', u'internal_access_path': None, u'last_activity_at': 1434670235.591856, u'console_type': u'novnc', u'host': u'192.168.1.15', u'token': u'10bffd07-e11c-48bb-880a-bc1a49c871d7', u'port': u'5900'}
(8)連接到目的 node,握手,開始 proxying。
與 NOVNC 有關的配置參數:
節點 | 配置文件 | 參數 | 默認配置 | 說明 |
Controller | /etc/nova/nova.conf | novncproxy_port | 6080 | /usr/bin/nova-novncproxy 服務所在的節點(通常是 Controller 節點)上 該 service 所監聽的端口。奇怪的是 nova 文檔中沒說明該配置參數。 |
novncproxy_host | 0.0.0.0 | nova-novncproxy service 所監聽的請求來源地址。0.0.0.0 表示接收所有來源的請求。奇怪的是 nova 文檔中沒說明該配置參數。 | ||
nova-compute | /etc/nova/nova.conf | vncserver_proxyclient_address | 虛機所在的 nova compute 節點的 management 網卡的 IP 地址 | |
vncserver_listen | 127.0.0.1 | 虛機所在的 nova compute 節點的 vnc service 所監聽的端口,需要修改默認配置為其 0.0.0.0 | ||
novncproxy_base_url | http://127.0.0.1:6080/ |
nova 客戶端通過 get_vncconsole 命令獲取到的訪問虛機的 VNC URL。需要修改其值為 http://<nova-novncproxy-node-management-interface-ip>:<nova-novncproxy-node-nova.conf.novncproxy_port>/vnc_auto.html 比如 http://192.168.1.20:6080/ |
||
vnc_enabled | true |
使得虛機能夠對外提供 VNC 服務 |
5. Evacuate (移機)
作用:當一個 node down 掉后,在新的 node 上根據其 DB 中保存的信息重新 build down node 上虛機。這個往往在虛機 HA 方案中用到。它盡可能地將原來的虛機在新的主機上恢復:
- 虛機的配置:從 DB 中獲取,包括 image,block,network 等
- 虛機的數據:如果使用共享存儲,則使用共享存儲上的虛機數據;如果不使用共享存儲,則無法恢復數據
- 內存狀態:無法恢復
因此,HA 方案中,應該盡可能地將虛機的數據文件放在共享存儲上,否則,恢復出來的虛機的價值非常有限。
Nova CLI:usage: nova evacuate [--password <password>] [--on-shared-storage] <server> [<host>]
要求:
(1)必須指定和虛機的 host 不同的 host,否則將報錯“The target host can't be the same one”。
(2)虛機的host 必須處於不可用狀態,否則將報錯 “Compute service of compute2 is still in use.”
(3)可以指定新的 admin password,不指定的話,nova 將生成一個隨機密碼
(4)參數 on-shared-storage 表示虛機的 instance folder 是不是在共享存儲上。
主要步驟:
(1)在做完以上各種參數檢查后,調用 Conductor 的 方法:
return self.compute_task_api.rebuild_instance(context, instance=instance, new_pass=admin_password, injected_files=None, image_ref=None,
orig_image_ref=None, orig_sys_metadata=None, bdms=None, recreate=True, on_shared_storage=on_shared_storage, host=host)
如果 host 為none 的話,conductor 將調用 scheduler 的方法選擇一個 host。
(2)接着調用 nova compute 的 rebuild_instance 方法。該方法從系統(DB)中獲取已有的數據,然后根據這些已有的 metadata 重新構造domain。“A 'rebuild' effectively purges all existing data from the system and remakes the VM with given 'metadata' and 'personalities'.”
- 獲取 image_ref,再獲取 image meta
- 獲取 network_info 並在新的 host 上構造網絡
- 獲取 BlockDeviceMappingList
- 獲取 block_device_info
(3)然后調用 virt driver 的 rebuild 方法,但是 libvirt 沒有實現該方法,轉而調用 _rebuild_default_impl 方法。該方法:
- 從 volume 端斷開其與原來 host 的連接
- 調用 driver.spawn 方法構造新的 domain,依次創建 image(如果 on_shared_storage = false 的話,則重新下載 image, 構造 instance folder;如果 on_shared_storage = true 的話,則直接使用共享存儲上的 instance folder。這也可見使用共享存儲的優勢),network 和 domain,並設置 domain 的狀態和之前一致。
(4)在壞了的 host 被重啟后,nova-compute 服務調用 _destroy_evacuated_instances 方法來找到 evacuated instances 並將它們刪除:
- 調用 libvirt 找到該 host 上所有 domains,然后在 nova db 中一一查找其狀態,過濾出 “deleted” 為 false 的 domains
- 如果instance.host 不是本機,而且 instance.task_state 不是 { MIGRATING,RESIZE_MIGRATING,RESIZE_MIGRATED,RESIZE_FINISH} 之一,則刪除該 domain,以及 network,block device 以及 instance folder。
總之,在使用共享存儲的情況下,evacuate 出來的新的 domain 除了臨時盤上的數據外,它和之前的 domain 的數據是一樣的了,但是內存狀態除外。