文章出處

openstack之虛擬機的創建流程。本文試圖詳細地描述openstack創建虛擬機的完整過程,從用戶發起請求到虛擬機成功運行,包括客戶端請求的發出、keystone身份驗證、nova-api接收請求、nova-scheduler調度、nova-computer創建、nova-network分配網絡。對于每一個模塊在創建虛擬機的過程中所負責的功能和執行的操作,進行較為詳細描述和討論。

為了方便描述,本文假設所有的服務ip地址為localhost,端口使用服務的默認設置端口。openstack為F版。下圖為創建虛擬機的一個大概流程圖,粗糙地表示下。接下來將對每一個模塊進行詳細介紹,如有錯誤,歡迎拍磚!

客戶端

創建虛擬機的第一步是需要客戶端調用nova-api,發送創建虛擬機請求。目前openstack提供兩種客戶端:

1 命令行指令nova,通過指定命令行參數,就可以調用nova-api創建虛擬機,一個最簡單的創建虛擬機指令如下:

nova boot vm_name --flavor flavor_id --image image_uuid

2 網頁交互頁面horizon,這是通過web操作頁面來調用nova-api創建虛擬機,比較簡單易用。選定相關參數后,點擊create就可以了。

這兩種客戶端除了UI不一樣以外,功能方面基本都是一樣。就創建虛擬機來講,它們需要完成:

用戶身份驗證。客戶端構造一個body格式如下:

{"auth": {

"passwordCredentials": {"username": self.user,

"password": self.password}}

"tenantName": "admin" }

向keystone發送HTTP請求(keystone的服務地址:nova命令一般通過環境變量OS_AUTH_URL設置,horizon則通過openstack_dashboard.local.local_settings.OPENSTACK_KEYSTONE_URL設置),url為http://localhost:5000/v2.0/tokens。如果驗證通過,keystone則返回token_id和serviceCatalog。身份驗證通過后,客戶端才可對nova-api發送創建請求。因為客戶端只配置了keystone的服務地址和端口,對于nova-api的服務地址它是不知道的,所以需要先通過keystone驗證,然后由keystone將nova-api的服務地址放在serviceCatalog中返回給它。其實serviceCatalog中不僅僅包含nova-api的服務地址,還有glance-api、cinder-api等服務的地址,這些地址在以后的鏡像下載、塊存儲請求時會用到。token_id被放在請求nova-api的headers中,用作nova-api驗證。

對傳入flavor和image參數進行驗證,以確定資源是否有效(對于一些非必須參數,此處省略討論)。對于nova boot,flavor_id和image_uuid需要通過命令行參數指定,novaclient.v1_1.shell._boot()分別向nova-api的"http://localhost:8774/v2/project_id/flavors/flavor_id"和"http://localhost:8774/v2/project_id/images/image_id"發送HTTP請求,驗證資源是否存在。對于horizon,它首先執行horizon.api.nova.flavor_list()和horizon.api.glance.image_list_detailed(),獲取所有可用的flavor ids和image ids,創建虛擬機時只能從這些可用的id中選擇。注意這里nova boot和horizon對于image的驗證有些不同,nova boot是請求nova-api,nova-api再請求glance-api以驗證image uuid,而horizon是直接請求glance-api獲取所有的image ids。

最后設置好請求參數后(除name、flavor和image外,其它可使用默認值),通過novaclient.v1_1.ServerManager.create()發送創建虛擬機請求。

可以看出,無論nova boot還是horizon,最后都是通過novaclient向nova-api發送請求的。novaclient是針對nova-api做了一層封裝,如獲取驗證token-id,以特定的格式構造HTTP請求headers和body,解析請求的結果等。其實可以不用nova boot命令行和horizon,甚至novaclient都不需要,我們完全可以設定好HTTP請求的headers和body,直接請求nova-api。不過這三個工具將這些繁瑣的工作替我們做了,我們只需要填寫參數就可以了。最后注意下,nova命令其實只是novaclient的一個entry point:novaclient.shell.main(),而nova boot實際上調用的是novaclient.v1_1.shell.do_boot()。

keystone

從上面可知,在客戶端發起創建虛擬機請求時,keystone需要對客戶端的用戶名和密碼進行驗證。keystone-all與nova-api一樣,api的發布沒有采用任何框架,而是使用router、paste類庫,從頭寫的,實現風格上又與nova-api有點差異。keystone-all服務會監聽兩個端口:localhost:5000,即publicURL,一般用戶使用密碼可以訪問;localhost:35357,即adminURL,只能使用admin賬戶和密碼訪問。在創建虛擬機流程中,調用的keystone的api有兩個(不過,每次客戶端請求基本都會調用這兩個api):

1 http://localhost:5000/v2.0/tokens,客戶端請求該api來獲取token_id和serviceCatalog。keystone會將該api的請求交給keystone.TokenController.authenticate()處理。該函數主要完成:

對于客戶端傳過來的name、password、tenant_id進行驗證。在keystone的數據庫user表中保存了user相關信息,包括password加密后的hash值。password的加密使用了sha512算法,由passlib庫提供。密碼驗證如下:passlib.hash.sha512_crypt.verify(password, hashed)。

根據CONF.signing.token_format配置項,為客戶端的每一次請求生成一個token_id。目前支持的選擇有兩個UUID、PKI,默認為UUID。所以在默認情況下,一個token_id就是一個隨機產生的uuid。token_id有一個有效時間,從token_id的生成的時刻開始算起。有效時間可通過CONF.token.expiration配置項設置,默認值為86400s。

構建serviceCatalog,這里面就是一堆endpoints,包括nova-api、glance-api、cinder-api等。根據配置項CONF.catalog.driver,可以從數據庫的endpoint表中獲取,也可以從模板文件中獲取。

2 http://localhost:35357/v2.0/tokens/token_id,對請求的token_id進行驗證。nova-api接收到請求后,首先使用請求攜帶的token_id來訪問該api,以驗證請求是否有效,當然nova-api需要在body里加上admin的賬戶和密碼信息,這些信息需要在api-paste.ini中配置。glance-api、cinder-api等在接收到客戶端請求后,都會調用該api對客戶端的token_id進行驗證。該api的請求交給keystone.TokenController.validate_token()處理,其實就是使用請求的token_id進行一次數據庫查詢。

nova-api

nova-api是一個統稱,它是一類服務的集合。如openstack之nova-api服務流程分析所說,在默認配置下,它包含ec2(與EC2兼容的API),osapi_compute(openstack compute自己風格的API),osapi_volume(openstack volume服務API),metadata(metadata 服務API)等api服務。每個服務提供不同的api,不過雖然ec2和osapi_compute的api不同,但功能是相同的。這里,創建虛擬機請求的api是:

http://localhost:8774/v2/project_id/servers,由osapi_compute服務發布。該api是RESTFUL的,以POST方法請求該api,經過幾層middleware處理后,最終交給nova.api.openstack.compute.servers.Controller.create()處理。它們主要完成以下功能:

驗證客戶端的token_id。通過keystone.middleware.auth_token.AuthProtocol.__call__()進行驗證,它是一個middleware,通過api-paste.ini配置。如上面keystone所講,它是通過請求http://localhost:35357/v2.0/tokens/token_id實現的。

檢查創建虛擬機參數是否有效與合法,由nova.api.openstack.compute.servers.Controller.create()實現。如,檢查虛擬機name是否符合命名規范,flavor_id是否在數據庫中存在,image_uuid是否是正確的uuid格式等。

檢查instance、vcpu、ram的數量是否超過限制,并預留所需資源,由nova.quota.QUOTAS管理。每個project擁有的資源都是有限的,如創建虛擬機的個數,vcpu個數,內存大小,volume個數等。默認情況下,所有project的擁有的資源數量相等,由quota_instances、quota_cores、quota_ram、quota_volumes等配置項指定。使用admin賬戶,以PUT方法請求http://localhost:8774/v2/admin_project/os-quota-sets/project_id,可為特定的project設置配額。

檢查metadata長度是否超過限制,inject file的個數是否超過限制,由nova.quota.QUOTAS管理檢測。一般情況下,這些參數都為空,都能通過。

檢查指定的network和fixed ip是否有效,如network是否存在,fixed ip是否屬于該network,fixed ip有沒有被分配等。一般情況下,該參數也為空,由network自動分配。

檢查image、disk、ramdisk是否存在且可用,這個是向glance-api(http://localhost:9292/v1/images/image_id)請求,獲取返回數據進行判斷的。并判斷flavor是否滿足該image的最小配置需求,如內存,虛擬磁盤是否滿足image的最小值。

當所有資源充足,并且所有傳參都有效時,更新數據庫,新建一條instance記錄,vm_states設為BUILDING,task_state設為SCHEDULING。

通過rpc call,將所有參數傳給nova-scheduler的nova.scheduler.manager.SchedulerManager.run_instance(),由它執行虛擬機調度。

在token_id驗證通過的情況下,nova-api的主要任務是資源配額和參數檢查,并創建數據庫。如果你使用dashboard,此時你在頁面上將會看到虛擬機處于scheduling狀態。不過該狀態持續時間很短,幾乎察覺不到,除非nova-scheduler出問題了。

nova-scheduler

與nova-api提供外部服務不同,nova各組件之間相互調用,使用的是以rabbitmq作為消息隊列的RPC調用。這里忽略RPC的實現細節,只需知道rcp call調用哪個的節點的哪個服務就可以了。nova-scheduler的run_instance()從nova-api接收到參數中只使用到了request_spec和filter_properties,其余參數將直接傳遞給nova-compute去創建虛擬機。request_spec包含虛擬機的type、number、uuids等信息,在調度中需要用這些信息作為參考。filter_properties包含指定調度規則信息,如force_hosts指定調度到特定的節點,ignore_hosts不調度到某些節點等。nova-scheduler在接收到nova-api的請求后,由nova.scheduler.filter_scheduler.FilterScheduler.scheduler_run_instances(),它主要完成以下任務:

獲取現存所有計算節點及其信息,調用nova.scheduler.host_manager.HostManager.get_all_host_states()獲取。這些信息包括compute_nodes表中的信息,及由每個nova-compute通過定時任務_publish_service_capabilitites上傳的capabilities信息,包括cpu、vcpu、內存、磁盤等大小和使用情況。

使用指定的filter對上面返回的節點進行過濾,調用nova.scheduler.filters.HostFilterHander.get_filtered_hosts()實現。這些filter可通過配置項scheduler_default_filters指定,它可以包含nova.scheduler.filters中任何已經實現的filter。如RamFilter,它用來過濾內存不足以創建虛擬機的host;ComputeFilter用來過濾一些service無效的host。也可以在這里添加自定義的filter,然后添加到scheduler_default_filters配置項中就行了。不過這里需要注意一點,通過force_hosts和forces_nodes指定的hosts,將不經過filter過濾,直接返回。

使用weighers對經過過濾的host進行排序,調用nova.scheduler.weights.HostWeightHander.get_weighed_objects()實現,在F版中只實現了一個RamWeigher,只以內存大小對hosts做了一個排序。后面的版本可能覺得使用Weigher,又使用WeigherHander,僅僅是對內存大小做排序,有點小題大做了,在2012.2.4中用一個函數nova.scheduler.least_cost.weighted_sum()就實現排序。但在2013.2中又回到原來的結構了。

從排名前n個hosts中,隨機選取一個用于創建虛擬機。n可通過配置項scheduler_host_subset_size設置,默認值為1,即選取最優節點進行創建。并更新scheduler保存host的資源信息,如內存、磁盤、vcpu等。使得調度下一個虛擬機時,使用的資源信息及時更新。

更新數據庫設置instance的host=NULL,scheduled_at=now(),然后通過RPC調用上面選取host的nova-compute進行創建虛擬機。

nova-scheduler相對而言,邏輯比較簡單,代碼也不難。不過需要注意在node-scheduler執行過程中,不會改變虛擬機的vm_state和tast_state。所以在horizon上也不會有變化。

nova-compute

nova-scheduler選定host后,隨即通過rpc調用,調用host的nova-compute服務的nova.compute.manager.ComputeManager.run_instance()。由它執行真正的創建虛擬機操作。不過在介紹之前,需要簡單提及一下nova-compute的兩個定時任務:

update_available_resource。該任務首先獲取當前計算節點的cpu個數、總內存大小、總磁盤大小,調用nova.virt.libvirt..driver.LibvirtDriver.get_available_resource()。然后從數據庫中查找到運行在該節點上的所有虛擬機信息,統計出這些虛擬機所使用的vcpu個數、內存大小、磁盤大小。并將計算節點的總資源數量減去虛擬機使用的數量,得到空閑內存大小和空閑磁盤大小。然后更新數據庫compute_node信息。這里需要注意從數據庫中獲取的虛擬機使用資源數量并不是一定是計算節點消耗的資源數量,如1)虛擬機使用磁盤鏡像為qcow2格式時,它的大小要小于或等于實際分配的大小;2)也有可能因為數據不同步,使得統計的數據與實際的資源使用不一致。

_report_driver_status。該任務是用于定時更新當前計算節點的capabilities信息,同樣也是通過LibvirtDriver來獲取資源數據的,不過在計算已使用資源方面,是直接使用通過調用multiprocessing、libvirt、os等返回的cpu個數、內存、磁盤使用情況。并附加上host_ip信息。并由定時任務_publish_service_capabilities通過rpc call轉發到nova.scheduler.host_manager.HostManager.service_states中。

這兩個定時任務為nova-scheduler提供了實時的host信息,這樣才能實現準確調度。下面介紹下run_instance()主要完成什么功能:

檢查instance的image大小是否超過root的大小,超過則報錯。

更新數據庫,將instance的vm_state=BUILDING,task_state=NULL狀態,horizon上面會有反應,但時間很短。

給虛擬機分配資源,包括cpu、內存、磁盤等。更新數據庫,設置instance的host字段。

分配網絡,首先將instance的tast_state=NETWORKING,然后通過rpc調用nova-network的nova.network.manager.NetworkManager.allocate_for_instance(),返回網絡信息。nova-network處理的詳細將在下面的nova-network模塊討論。

建立塊設備,將task_state=BLOCK_DEVICE_MAPPING。因為未給分配塊設備,這步將不進行操作,有待后面討論。

將task_state=SPAWNING,如果該類型的instance第一次在該計算節點上創建,該狀態要持續幾分鐘,因為需要下載鏡像。

根據上面配置的虛擬機信息,生成xml,寫入libvirt,xml文件,生成console.log文件。

下載鏡像,kernel、ramdisk、image,通過glance api下載。它們首先會被放在_base目錄下,然后copy一份到instance的目錄下面。這樣,如果這個計算節點上創建相同虛擬機時,首先查找_base中是否已經下載,如果已經下載過了,則直接copy就可以了。對于image,一般采用qcow2格式,作為qemu-img的backing_file新建一個image使用,這樣可以節約空間。

最后使用上面生成的xml,調用libvirt創建虛擬機。

nova-network

nova-network是nova-compute在創建虛擬機之前,被其調用nova.network.manager.FlatDHCPManger.allocate_for_instance(),為虛擬機分配fixed ip和floating ip(如果auto_assign_floating_ip為True),并返回網絡信息。這里network采用FlatDHCP模式,multihost=True。查看代碼可知FlatDHCPManager繼承RPCAllocateFixedIP, FloatingIP, NetworkManager三個class,根據python屬性訪問流程(參考python對象之屬性訪問流程)可知,調用FlatDHCPManger.allocate_for_instance()首先執行FloatingIP.allocate_for_instance(),再由它調用NetworkManger.allocate_for_instance()。主要完成任務如下:

獲取網絡信息,用戶可指定network_uuid,否則將直接獲取networks表中所有network。

給每一個network,在數據庫創建一個virtual interface,給instance uuid和network id置為相應的值。注意virtual interface表與其它的表不太一樣,當刪除一個virtual interface時,將直接刪除該記錄,而不是將deleted=1。

從數據庫中找出一個network id等于該network或為NULL的fixed ip,設置instance uuid、host、allocated=True、virtual_interface_id。更新dnsmasq的conf文件(默認在nova源碼目錄下),同時給dnsmasq發送HUP信號,重讀配置文件,當虛擬機動態獲取IP時,dnsmasq根據接收到的mac,查詢該配置文件,返回分配給該虛擬機的ip。這里需要注意下,dnsmasq的啟動參數--dhcp-script=/usr/bin/nova-dhcpbridge,在虛擬機請求和釋放ip時該腳本會被調用,用來設置fixed ip的leased字段。

如果auto_assign_floating_ip為True,則給虛擬機分配floating ip。這里分配的floating ip原本不屬于任何project,分配過后才設置它的project_id和auto_assigned字段。并將fixed_ip_id字段設為上面分配到fixed ip。

在對外出口網卡上綁定floating ip,設置iptables nat的nova-network-PREREOUTING、nova-network-OUTPUT和nova-network-float-snat表,做SNAT和DNAT轉換。這樣就可以通過floating ip從外部訪問虛擬機了。

通過instance uuid查詢數據庫,獲取它的vif、network、floating ip、fixed ip等信息,以nova.network.model.NetworkInfo結構構造,返回給nova-compute。

看文倉www.kanwencang.com網友整理上傳,為您提供最全的知識大全,期待您的分享,轉載請注明出處。
歡迎轉載:http://www.kanwencang.com/bangong/20170111/85712.html

文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()