1. 程式人生 > >OpenStack Nova深入學習 -- 建立instance的過程之原始碼分析

OpenStack Nova深入學習 -- 建立instance的過程之原始碼分析

Nova的核心元件有nova API, nova Conductor,nova Scheduler和nova compute。如下圖所示:

nova主要元件的作用:

1). nova API -- 主要是接收HTTP請求(通常來自nova client),將其轉換成命令,並通過oslo message和nova Conductor或者nova Compute互動。為了完成虛機的處理(比如建立、修改,刪除等)操作,nova API還需要通過HTTP協議向Keystone, Neutron,  Glance發出請求。

2). nova Conductor -- 主要作為一個數據庫代理存在,避免nova Compute直接操作資料庫。

3). nova Compute -- 主要是操縱compute node上的hypervisor對instance進行建立和管理。

4). nova Scheduler -- 主要是用來判斷新建立的instance應該被放在哪一個compute node上,它的配置在/etc/nova/nova.conf裡完成,預設配置如下:

scheduler_available_filters=nova.scheduler.filters.all_filters
scheduler_default_filters=RetryFilter,AvailabilityZoneFilter,RamFilter,ComputeFilter,ComputeCapabilitiesFilter,ImagePropertiesFilter,ServerGroupAntiAffinityFilter,ServerGroupAffinityFilter

scheduler_driver=nova.scheduler.filter_scheduler.FilterScheduler

其中,scheduler_driver決定了nova用什麼樣的scheduler,scheduler_default_filters決定了在一個compute node被選中作為instance的宿主前,都需要過哪些檢查關口,如下是這些檢查關口的說明:

-- RetryFilter:找出還沒有被嘗試filter過的compute node

-- AvailabilityZoneFilter:必須在規定的availability zone裡選compute node

-- RamFilter:compute node必須有足夠的記憶體支援instance的建立

-- ComputeFilter:必須有compute node存在

-- ComputeCapabilitiesFilter:compute node必須滿足instance建立需要的extra specs

-- ImagePropertiesFilter:compute node必須滿足映象裡定義的屬性,比如architecture是否是qcow2,是否支援KVM hyperviosr等。

-- ServerGroupAntiAffinityFilter:如果建立多個instance,需要讓這些instance分佈在不同的compute node上

-- ServerGroupAffinityFilter:如果建立多個instance,需要讓這些instance分佈在相同的compute node上

下面,通過nova的原始碼看一下一個新的instance被建立需要經過哪些過程,程式碼來自Liberty版本:

建立instance的入口程式碼在nova/api/openstack/compute/servers.py,方法是create(),如下:

   def create(self, req, body):
            (instances, resv_id) = self.compute_api.create(context,
                            inst_type,
                            image_uuid,
                            display_name=name,
                            display_description=name,
                            metadata=server_dict.get('metadata', {}),
                            admin_password=password,
                            requested_networks=requested_networks,
                            check_server_group_quota=True,
                            **create_kwargs)


compute_api物件來自compute.API類,如下。而compute.API來自nova/compute/__init__.py。

        self.compute_api = compute.API(skip_policy_check=True)


檢視nova/compute/__init__.py,看到API()返回的就是nova.compute.api.API,如下。所以compute_api就是nova.compute.api.API。

CELL_TYPE_TO_CLS_NAME = {'api': 'nova.compute.cells_api.ComputeCellsAPI',
                         'compute': 'nova.compute.api.API',
                         None: 'nova.compute.api.API',
                        }
def _get_compute_api_class_name():
    """Returns the name of compute API class."""
    cell_type = nova.cells.opts.get_cell_type()
    return CELL_TYPE_TO_CLS_NAME[cell_type]
def API(*args, **kwargs):
    class_name = _get_compute_api_class_name()
    return importutils.import_object(class_name, *args, **kwargs)

檢視nova/compute/api.py,看到create()方法呼叫了_create_instance(),如下:

    def create(self, context, instance_type,
               image_href, kernel_id=None, ramdisk_id=None,
               min_count=None, max_count=None,
               display_name=None, display_description=None,
…
        return self._create_instance(
                       context, instance_type,
                       image_href, kernel_id, ramdisk_id,
                       min_count, max_count,
…

_create_instance()也在nova/compute/api.py中定義,它又呼叫了compute_task_api物件的build_instances()方法,如下:

    def _create_instance(self, context, instance_type,
               image_href, kernel_id, ramdisk_id,
               min_count, max_count,
…
        self.compute_task_api.build_instances(context,
                instances=instances, image=boot_meta,
                filter_properties=filter_properties,
…

compute_task_api物件也在nova/compute/api.py中定義,來自conductor.ComputeTaskAPI物件,如下:

    def compute_task_api(self):
        if self._compute_task_api is None:
            # TODO(alaski): Remove calls into here from conductor manager so
            # that this isn't necessary. #1180540
            from nova import conductor
            self._compute_task_api = conductor.ComputeTaskAPI()
        return self._compute_task_api

ComputeTaskAPI物件在nova/conductor/__init__.py中定義,來自nova.conductor.api.ComputeTaskAPI,如下:

from nova.conductor import api as conductor_api
def ComputeTaskAPI(*args, **kwargs):
    use_local = kwargs.pop('use_local', False)
    if oslo_config.cfg.CONF.conductor.use_local or use_local:
        api = conductor_api.LocalComputeTaskAPI
    else:
        api = conductor_api.ComputeTaskAPI
    return api(*args, **kwargs)

檢視nova/conductor/api.py,build_instances()方法呼叫了conductor_compute_rpcapi物件的build_instances()方法。conductor_compute_rpcapi物件來自nova/conductor/rpcapi.py中定義的ComputeTaskAPI類。

class ComputeTaskAPI(object):
…
    def __init__(self):
        self.conductor_compute_rpcapi = rpcapi.ComputeTaskAPI()
…
    def build_instances(self, context, instances, image, filter_properties,
            admin_password, injected_files, requested_networks,
            security_groups, block_device_mapping, legacy_bdm=True):
        self.conductor_compute_rpcapi.build_instances(context,
                instances=instances, image=image,……



nova/conductor/rpcapi.py是專門處理nova元件之間的RPC呼叫的。因為這裡定義的topic是conductor,根據http://www.cnblogs.com/littlebugfish/p/4090311.html中的描述的訊息佇列原理,這裡將轉到nova-conductor程序中進行處理。

    def build_instances(self, context, instances, image, filter_properties,
            admin_password, injected_files, requested_networks,
…
        cctxt = self.client.prepare(version=version)
        cctxt.cast(context, 'build_instances', **kw)

檢視nova/conductor/manager.py,看到build_instances()方法會呼叫scheduler_client.select_destinations()和compute_rpcapi.build_and_run_instance()。前者會呼叫schedular的RPC API選擇在哪些主機上建立instance,後者會通過RPC請求nova-compute服務去構建和執行instance。

class ComputeTaskManager(base.Base):
…
    def build_instances(self, context, instances, image, filter_properties,
            admin_password, injected_files, requested_networks,…
…
        try:
…
            hosts = self.scheduler_client.select_destinations(context,
                    request_spec, filter_properties)
…
        for (instance, host) in itertools.izip(instances, hosts):
…
            self.compute_rpcapi.build_and_run_instance(context,
                    instance=instance, host=host['host'], image=image,
                    request_spec=request_spec,…

檢視nova/compute/manager.py,整個的呼叫過程如下:

build_and_run_instance() --> _do_build_and_run_instance() --> _build_and_run_instance() --> driver.spawn()

driver物件是在nova/virt/driver.py裡定義,呼叫load_compute_driver()方法獲得並載入compute driver。compute driver的定義是在/etc/nova/nova.conf裡由compute_driver的值確定。

def __init__(self, compute_driver=None, *args, **kwargs):
        self.driver = driver.load_compute_driver(self.virtapi, compute_driver)

def load_compute_driver(virtapi, compute_driver=None):
    if not compute_driver:
        compute_driver = CONF.compute_driver

    if not compute_driver:
        LOG.error(_LE("Compute driver option required, but not specified"))
        sys.exit(1)

    LOG.info(_LI("Loading compute driver '%s'"), compute_driver)
    try:
        driver = importutils.import_object_ns('nova.virt',
                                              compute_driver,
                                              virtapi)
        return utils.check_isinstance(driver, ComputeDriver)