1. 程式人生 > >一個OpenStack訪問請求在各元件之間的呼叫過程

一個OpenStack訪問請求在各元件之間的呼叫過程

OpenStack 是一整套資源管理軟體的集合,也是當前最熱的開源虛擬化管理軟體之一,有一個全球139個國家將近兩萬開發者參與的開源社群(www.openstack.org)作為支援。OpenStack專案的目的是快速建設一個穩定可靠的公有云或私有云系統。整個專案涵蓋了計算,儲存,網路以及前端展現等關於雲管理的全部方面,包含了眾多子專案,其中主要的子專案有:

  • OpenStack Compute (code-name Nova) 計算服務
  • OpenStack Networking (code-name Neutron) 網路服務
  • OpenStack Object Storage (code-name Swift) 物件儲存服務
  • OpenStack Block Storage (code-name Cinder) 塊裝置儲存服務
  • OpenStack Identity (code-name Keystone) 認證服務
  • OpenStack Image Service (code-name Glance) 映象檔案服務
  • OpenStack Dashboard (code-name Horizon) 儀表盤服務
  • OpenStack Telemetry (code-name Ceilometer) 告警服務
  • OpenStack Orchestration (code-name Heat) 流程服務
  • OpenStack Database (code-name Trove) 資料庫服務

OpenStack的各個服務之間通過統一的REST風格的API呼叫,實現系統的鬆耦合。下圖是OpenStack各個服務之間API呼叫的概覽,其中實線代表client 的API呼叫,虛線代表各個元件之間通過rpc 呼叫進行通訊。鬆耦合架構的好處是,各個元件的開發人員可以只關注各自的領域,對各自領域的修改不會影響到其他開發人員。不過從另一方面來講,這種鬆耦合的架構也給整個系統的維護帶來了一定的困難,運維人員要掌握更多的系統相關的知識去調試出了問題的元件。所以無論對於開發還是維護人員,搞清楚各個元件之間的相互呼叫關係是怎樣的都是非常必要的。

從nova-client入手

nova-client是一個命令列的客戶端應用,終端使用者可以從nova-client發起一個api請求到nova-api,nova-api服務會轉發該請求到相應的元件上。同時,nova-api支援對cinder、neutron的請求轉發,也就是你可以在nova-client直接向cinder,neutron傳送請求。

我們可以在呼叫nova-client 增加--debug選項列印更多的debug訊息,通過這些debug資訊可以瞭解到如果我們需要發起一個完整的業務層面上請求,都需要跟那些服務打交道。

以 boot 一個新例項為例子,以下是執行程式碼以及debug輸出:

[[email protected] devstack]$ nova --debug boot t3 --flavor m1.nano --image  
44c37b90-0ec3-460a-bdf2-bd8bb98c9fdf --nic net-id=b745b2c6-db16-40ab-8ad7-af6da0e5e699

…

REQ: curl -i 'http://cloudcontroller:5000/v2.0/tokens'

REQ: curl -i 'http://cloudcontroller:8774/v2/d7beb7f28e0b4f41901215000339361d/images/44c37b90-0ec3-460a-bdf2-bd8bb98c9fdf'

REQ: curl -i 'http://cloudcontroller:8774/v2/d7beb7f28e0b4f41901215000339361d/flavors/m1.nano'

REQ: curl -i 'http://cloudcontroller:8774/v2/d7beb7f28e0b4f41901215000339361d/servers' -X POST 
-H "Accept: application/json" -H "Content-Type: application/json" -H "User-Agent: 
python-novaclient" -H "X-Auth-Project-Id: admin" -H "X-Auth-Token:
 {SHA1}15d9e554b7456f1043732bb8df72d1521c5f6aa1" -d '{"server":
 {"name": "t3", "imageRef": "44c37b90-0ec3-460a-bdf2-bd8bb98c9fdf", 
"flavorRef": "42", "max_count": 1, "min_count":
 1, "networks": [{"uuid": "b745b2c6-db16-40ab-8ad7-af6da0e5e699"}]}}'

從以上debug輸出我們可以清楚看到,執行一個boot新例項的操作需要傳送如下幾個api請求:

  1. 向keystone傳送請求,獲取租戶(d7beb7f28e0b4f41901215000339361d)的認證token
  2. 通過拿到的token,向nova-api服務傳送請求,驗證image是否存在
  3. 通過拿到的token,向nova-api服務傳送請求,驗證建立的favor是否存在
  4. 請求創健一個新的instance,需要的元資料資訊通過包含在請求body中

nova-client幫我們把需要的全部請求放到一起,而最重要的就是4。如果使用者想自己通過rest api 直接傳送http請求的話,可以直接使用4,當然,前提是先通過呼叫keystone服務得到認證token。

下面結合程式碼重點敘述一下4的請求資料流動在整個stack中的過程。

圖1建立新例項時的請求在OpenStack中各元件之間的呼叫

上圖是一個全域性的流程圖,圖中每個服務是一個單獨的程序例項,他們之間通過rpc呼叫(廣播或者呼叫)另一個服務。nova-api服務是一個wsgi服務例項,建立新instance的入口程式碼是在nova /api/openstack/compute/servers.py,處理函式為:

def create(self, req, body):
    """Creates a new server for a given user."""
…   
(instances, resv_id) = self.compute_api.create(context,...

做一些引數驗證之後,呼叫compute api的create 函式(程式碼在nova/compute/api.py中):

@hooks.add_hook("create_instance")
def create(self, context, instance_type,
...
        return self._create_instance(...

建立instance物件例項,_create_instance會呼叫compute_task api 的build_instances 方法對剛建立的instances例項進行構建:

self.compute_task_api.build_instances(context, ...

compute_task api是一個nova-conductor 服務的rpc api請求,處理程式碼在nova/conductor/manager.py中:

def build_instances(self, context, instances, image, filter_properties,
... 
       hosts = self.scheduler_rpcapi.select_destinations(context,
...
       self.compute_rpcapi.build_and_run_instance(context, ...

它做了兩件事情:呼叫scheduler的rpc api選擇在那些主機上建立新例項,並最終通過rpc請求nova-compute 服務去構建和執行新例項。

處理函式在nova/compute/manager.py中:

def build_and_run_instance(self, context, instance, image, request_spec,
                 filter_properties, admin_password=None,
                 injected_files=None, requested_networks=None,
                 security_groups=None, block_device_mapping=None,
                 node=None, limits=None):

最終呼叫配置檔案中配置的hypervisor型別進行虛擬機器的建立和執行,一個例項就這樣構建好了。

以下是上面涉及到的服務的主要功能:

  1. nova-api:接受http請求,並響應請求,當然還包括請求資訊的驗證
  2. nova-conductor:與資料庫互動,提高對資料庫訪問的安全性
  3. nova-scheduler:排程服務,決定最終例項要在哪個服務上建立。遷移,重建等都需要通過這個服務
  4. nova-compute:呼叫虛擬機器管理程式,完成虛擬機器的建立和執行以及控制

以上基本包含nova專案的全部服務,但一個請求有的時候並不需要經過全部的服務。繼續看shelve一個例項的過程。

[[email protected] devstack]$ nova --debug shelve t2

REQ: curl -i 'http://cloudcontroller:5000/v2.0/tokens' …

REQ: curl -i 'http://cloudcontroller:8774/v2/d7beb7f28e0b4f41901215000339361d/servers'…

REQ: curl -i 'http://cloudcontroller:8774/v2/d7beb7f28e0b4f41901215000339361d/servers/r'…

…

REQ: curl -i 'http://cloudcontroller:8774/v2/d7beb7f28e0b4f41901215000339361d/servers/
00be783d-bef5-46b1-bfdc-316618c76e92/action' 
-X POST -H "Accept: application/json" -H "Content-Type: application/json" 
-H "User-Agent: python-novaclient" -H "X-Auth-Project-Id: admin" 
-H "X-Auth-Token: {SHA1}0634ea0ef1c3994e1f496c5d8890d32610cf11e9" 
-d '{"shelve": null}'…
  1. 向keystone傳送請求,獲取租戶(d7beb7f28e0b4f41901215000339361d)的認證token
  2. 通過拿到的token,向nova-api服務傳送請求,顯示該租戶的全部服務例項
  3. 通過拿到的token,向nova-api服務傳送請求,查詢準備shelve的例項uuid的詳細資訊
  4. 請求一個server操作action,執行shelve操作(request body為‘{“shelve”: null}’)

nova-api返回http 202,成功接受請求,轉為後臺進行非同步執行。

RESP: [202] CaseInsensitiveDict({'date': 'Thu, 18 Sep 2014 04:03:09 GMT', 
'content-length': '0', 'content-type': 'text/html; 
charset=UTF-8', 'x-compute-request-id': '
req-4be7dc9a-21da-4050-9310-3ee58ca93569'}) RESP BODY: null

上面4中的shelve 操作程式碼在nova/api/openstack/compute/contrib/shelve.py :

@wsgi.action('shelve')
def _shelve(self, req, id, body):
    """Move an instance into shelved mode."""
    context = req.environ["nova.context"]
    auth_shelve(context)

    instance = self._get_instance(context, id)
    try:
        self.compute_api.shelve(context, instance)
    except exception.InstanceIsLocked as e:
        raise exc.HTTPConflict(explanation=e.format_message())

從程式碼可以看出,nova-api服務直接呼叫了compute_api,程式碼位於nova/compute/api.py

    if not self.is_volume_backed_instance(context, instance):
        name = '%s-shelved' % instance['display_name']
        image_meta = self._create_image(context, instance, name,
                'snapshot')
        image_id = image_meta['id']
        self.compute_rpcapi.shelve_instance(context, instance=instance,
                image_id=image_id)
    else:
        self.compute_rpcapi.shelve_offload_instance(context,
                instance=instance)

comput_api直接呼叫rpc訊息請求,所以,直接將訊息傳送給了nova-compute服務,所以最終各個元件之間的呼叫關係如下:

結論

本文介紹了OpenStack專案的所有元件以及元件之間的呼叫關係,並從nova-client 入手,結合程式碼分析了兩個具體例項,從例項的debug訊息分析得出如果我們需要完成一個完整的業務請求,需要呼叫那些api請求;從程式碼分析,可以得出api呼叫的大致關係。rpc請求用於實現一個元件內部的各個服務,如nova元件中的nova-api、nova-compute、nova-conductor、nova-scheduler等。而不同元件之間的呼叫則是通過rest api請求實現,如nova元件的某一服務需要呼叫cinder服務,則是在nova元件引入改cinder服務的client api,實現rest-api請求。