建立虛擬機流程nova

       這篇博文借鑑於http://www.cnblogs.com/yjbjingcha/p/6977741.html,感謝博友提供。

 

       本文試圖具體地描寫敘述openstack建立虛擬機的完整過程。從用戶發起請求到虛擬機成功執行,包含client請求的發出、keystone身份驗證、nova-api接收請求、nova-scheduler調度、nova-computer建立、nova-network分配網絡。對於每一個模塊在建立虛擬機的過程當中所負責的功能和執行的操做,進行較爲具體描寫敘述和討論。爲了方便描寫敘述,本文若是所有的服務ip地址爲localhost。port使用服務的默認設置port。openstack爲O版。下圖爲建立虛擬機的一個大概流程圖,粗糙地表示下。接下來將對每一個模塊進行具體介紹。若有錯誤,歡迎拍磚!html

 

clientnode

      建立虛擬機的第一步是需要client調用nova-api,發送建立虛擬機請求。眼下openstack提供兩種client:python

1 命令行指令nova,經過指定命令行參數。就可以請求nova-api建立虛擬機,一個最簡單的建立虛擬機指令例如如下:web

 

nova boot vm_name --flavor flavor_id --image image_uuid --nic net-id = network_id

eg:nova boot --flavor m1.nano --image cirros \
--security-groups default --key-name mykey --nic net-id=a31d6a9a-a73f-4a19-a252-10d0b7e64d1d \
test2算法

 

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

 

這兩種client除了UI不同之外,功能方面基本都是同樣。數據庫

就建立虛擬機來說。它們需要完畢:api

 

  • 用戶身份驗證。client構造一個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。身份驗證經過後。client纔可對nova-api發送建立請求。

     

    因爲client只配置了keystone的服務地址和port,對於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()發送建立虛擬機請求,該函數主要是將請求參數構形成規定格式的body。而後向nova-api發送HTTP請求。

     

    關於body的模樣,可看看novaclient.v1_1.BootingManagerWithFind._boot() 。以上面的nova boot命令爲例,body內容例如如下:

    {'server': {'flavorRef': '1',
                'hypervisor_type': 'QEMU',
                'imageRef': 'd3670457-16f5-4c70-913f-6fc7b76706e4',
                'max_count': 1,
                'min_count': 1,
                'name': 'test-ic'}}
    
    這裏的flavorRef相應的是數據庫instance_types的flavorid字段(非id字段),上面命令行傳入的flavor_id也是指數據庫的flavorid字段。向nova-api的http://localhost:8774/v2/project_id/flavors/flavorid請求時,它經過nova.api.openstack.compute.views.flavors.ViewBuilder,將數據庫的id字段做爲instance_type_id。將flavorid做爲id進行返回的。

       可以看出,無論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

       由上可知,在client發起建立虛擬機請求時,keystone需要對client的username和password進行驗證。keystone-all與nova-api同樣,api的公佈沒有採用不論什麼框架,而是使用router、paste類庫。從頭寫的,實現風格上又與nova-api有點差別。

keystone-all服務會監聽兩個port:localhost:5000,即publicURL。通常用戶使用password可以訪問;localhost:35357。即adminURL,僅僅能使用admin帳戶和password訪問。

在建立虛擬機流程中,調用的keystone的api有兩個(事實上,每次請求基本都會調用這兩個api):

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

 

  •  對client傳過來的name、password、tenant_id進行驗證。keystone的數據庫user表中保存了user相關信息,包含password加密後的hash值。password的加密使用了sha512算法。由passlib庫提供。密碼驗證例如如下:passlib.hash.sha512_crypt.verify(password, hashed)。
  • 依據CONF.signing.token_format配置項,爲client的每一次請求生成一個token_id。眼下支持的選擇有兩個UUID、PKI。默以爲UUID。因此在默認狀況下,一個token_id就是一個隨機產生的uuid。token_id有一個有效時間。從token_id的生成的時刻開始算起。有效時間可經過CONF.token.expiration配置項設置,默認值爲86400s。
  • 構建serviceCatalog。這裏面就是一堆endpoints,包含nova-api、glance-api、cinder-api、keystone-api等。依據配置項CONF.catalog.driver。可以從數據庫的endpoint表中獲取,也可以從模板文件裏獲取。

2 http://localhost:35357/v2.0/tokens/token_id,對請求的token_id進行驗證。由nova-api調用。nova-api接收到請求後。首先使用請求攜帶的token_id來訪問該api,以驗證請求是否經過驗證。

固然nova-api需要在body里加上admin的帳戶和password信息,這些信息需要在api-paste.ini中配置。還有keystone的服務地址,因爲在驗證沒經過以前。不能使用client傳過來的endpoints。glance-api、cinder-api等在接收到client請求後。都會調用該api對client的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()處理。

它們主要完畢下面功能:

 

  • 驗證client的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。假設沒有給虛擬機指定security group。那麼將默認使用default。instance與security group的關聯很隱蔽,在db.instance_create()中,注意觀察。

     

     

  • 提交預留資源,完畢資源分配。

     

    經過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的兩個定時任務:

 

  1. update_available_resource。該任務首先獲取當前計算節點的cpu個數、總內存大小、總磁盤大小,數據由nova.virt.libvirt..driver.LibvirtDriver.get_available_resource()獲取。

     

    而後從數據庫中查找到執行在該節點上的所有虛擬機信息,統計出這些虛擬機所使用的vcpu個數、內存大小、磁盤大小。並將計算節點的總資源數量減去虛擬機使用的數量,獲得空暇內存大小和空暇磁盤大小。而後更新數據庫compute_node信息。這裏需要注意從數據庫中獲取的虛擬機使用資源數量並不是必定是計算節點消耗的資源數量,如1)虛擬機使用磁盤鏡像爲qcow2格式時,它的大小要小於或等於實際分配的大小。2)也有可能因爲數據不一樣步,使得統計的數據與實際的資源使用不一致。

  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信息。因此才幹實現準確調度。由於capabilities信息與compute_node表中信息有很是大的類似度,因此調度過程當中很是少用到。

nova-scheduler調度到nova-compute的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下載。它們首先會被放在FLAGS.instances_path的_base文件夾下。而後copy一份到instance的文件夾如下。這樣,假設這個計算節點上建立一樣虛擬機時。首先查找_base中是否已經下載。假設已經下載過了,則直接copy就可以了。

     

    對於image,通常採用qcow2格式,做爲qemu-img的backing_file新建一個image使用,這樣可以節約空間。

  • 向下載事後的磁盤文件。注入指定的內容。如public_key、/etc/network/interfaces、rootpassword、指定的文件路徑和內容、/etc/vmuuid等。

     

    原理比較簡單,將下載的image使用mount命令進行掛載,而後將要寫入的內容下到特定的位置。

  • 最後使用上面生成的xml,調用libvirt建立虛擬機,等待虛擬機正常執行。

     

     

  • 更新數據庫,將instance的vm_state=ACTIVE、task_state=None。

     

     

        nova-compute作的事情仍是挺多的,生成虛擬機xml配置文件、下載鏡像並注入文件、調用libvirt建立虛擬機等。這裏對於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。
相關文章
相關標籤/搜索