1、前面已经介绍Nova Scheduler服务的启动流程,我们知道Nova Scheduler服务作为一个调度者,其核心便是调度

法。接下来我们就分析一下Nova Scheduler服务的调度算法吧。

2、在配置文件中,调度算法默认的驱动类是FilterScheduler,该类位于/nova/scheduler/filter_scheduler.py

中。FilterScheduler的schedule_run_instance方法调用了_schedule方法获取执行调度算法,获取加权主机列表。

_schedule方法实现了虚拟机调度算法。


class FilterScheduler(driver.Scheduler):
    def scheduler_run_instance(self, context, request_spec,
                               admin_password, injected_files,
                               requested_networks, is_first_time,
                               filter_properties):
        #获取调度所需参数
        payload = dict(request_spec=request_spec)
        #通知Nova API开始调度
        notifier.notify(context, notifier.publisher_id("scheduler"),
                        'scheduler.run_instance.start', notifier.INFO, notifier.INFO,
                        payload)
        ...
        #<strong><span style="color:#ff0000;">执行调度算法,获取加权主机列表</span></strong>
        weighted_hosts = self._schedule(context, "compute", request_spec,
                                        filter_properties, instance_uuids)
        ...
        #<strong><span style="color:#ff0000;">为每个虚拟机分配计算节点</span></strong>
        for num, instance_uuid in enumerate(instance_uuids):
            ...
            try:
                try:
                    #<strong><span style="color:#ff0000;">选择权值最高的计算节点</span></strong>
                    weighted_host = weighted_hosts.pop(0)
                except IndexError:
                    raise exception.NoValidHost(reason="")
                #<strong><span style="color:#ff0000;">在权值最高的计算节点上创建虚拟机</span></strong>
                self._provision_resource(context, weighted_host,
                     request_spec,
                     filter_properties,
                     requested_networks,
                     injected_files, admin_password,
                     is_first_time,
                    instance_uuid=instance_uuid)
            except Exception as ex:
                ...
        #通知Nova API虚拟机调度完毕
        notifier.notify(context, notifier.publisher_id("scheduler"),
                        'scheduler.run_instance.end', notifier.INFO, payload)

算法的核心实现在FilterScheduler类的_schedule方法。后面的_provision_resource方法实际上是远程调用了

Nova Compute服务的run_instance方法。

3、我们下面来重点看一下包含算法核心的_scheduler方法。


def _schedule(self, context, topic, request_spec, filter_properties,
                  instance_uuids=None):
    #获取用户上下文信息
    elevated = context.elevated()
    #获取虚拟机的信息
    instance_properties = request_spec['instance_properties']
    #获取虚拟机规格
    instance_type = request_spec.get("instance_type", None)
    ...
    #获取配置项
    config_options = self._get_configuration_options()
    properties = instance_properties.copy()
    if instance_uuids:
        properties['uuid'] = instance_uuids[0]
    self._populate_retry(filter_properties, properties)

    #构造主机过滤参数
    filter_properties.update({'context': context,
                              'request_spec': request_spec,
                              'config_options': config_options,
                              'instance_type': instance_type})
    self.populate_filter_properties(request_spec,
                                    filter_properties)

    #获取全部活动的主机列表
    hosts = self.host_manager.get_all_host_states(elevated)
    selected_hosts = []
    #获取需要启动的虚拟机数量
    if instance_uuids:
        num_instances = len(instance_uuids)
    else:
        num_instances = request_spec.get('num_instances', 1)
    #为每个要创建的虚拟机,选择权值最高的主机
    for num in xrange(num_instances):
        #获取所有可用的主机列表
        hosts = self.host_manager.get_filter_hosts(hosts,filter_properties)
        if not hosts:
            break
        #计算可用主机的权值
        weighted_host = self.host_manager.get_weighted_hosts(hosts, filter_properties)
   #这个参数定义了新的实例将会被调度到一个主机上,这个主机是随机的从最好的(分数最高的)N个主机组成的子集中选择出来的
        scheduler_host_subset_size = CONF.scheduler_host_subset_size
        if scheduler_host_subset_size > len(weighed_hosts):
            scheduler_host_subset_size = len(weighed_hosts)
        if scheduler_host_subset_size < 1:
            scheduler_host_subset_size = 1
        #从分数最高的若干主机组成的子集中,随机的选择一个主机出来
        chosen_host = random.choice(weighed_hosts[0:<span style="font-family: SimSun;">scheduler_host_subset_size</span>])
        selected_hosts.append(chosen_host)
        #因为已经选好了一个主机,所以要在下一个实例选择主机前,更新主机资源信息
        chosen_host.obj.consume_from_instance(instance_properties)
        ...
    return selected_hosts



(1)、构造

主机过滤参数filter_properties。filter_properties参数是一个数据结构。(后面介绍)

(2)、hosts = self.host_manager.get_all_host_states(elevated) 调用FilterScheduler对象的

   host_manager成员变量的方法get_all_host_states方法获取所有活动的计算节点列表

HostManager类的get_all_host_states方法后面讨论。

(3)、后面实现虚拟机调度的核心算法。

hosts = self.host_manager.get_filter_hosts(hosts,filter_properties) 获取可用的计算节点列表

weighted_host = self.host_manager.get_weighted_hosts(hosts, filter_properties)计算可用计算节点的权值

(4)、从权值最高的scheduler_host_subset_size个计算节点中随机选择一个计算节点作为创建虚拟机的节点。

(5)、调用chosen_host.obj变量的consume_from_instance方法,更新选择的计算节点的硬件资源信息,为虚拟机预

留资源。chosen_host.obj变量 HostState对象

注意:

1、使用for循环,为每个要创建的虚拟机执行一遍调度算法。因为当上一台虚拟机启动后,计算节点的硬件资源会发

生改变、计算节点的可用性和权值也会变化。因此,需要重新执行调度算法。

2、为什么要从权值最高的scheduler_host_subset_size个计算节点随机选择一个计算节点,而不是选择权值最高

的?如果都是选择权值最高的计算节点,就会导致一台配置很高的计算节点不断接受虚拟机创建请求,而其他节点处

于空闲状态。 负载均衡和系统性能产生影响。

3、执行_schedule方法时,并没有真的创建虚拟机。代码依然修改计算节点的硬件资源信息,目的是为虚拟机预留响

应的资源,也是为了保证下一次虚拟机调度算法能够有效执行。


总结:虚拟机调度算法的步骤:

1、获取可用的计算节点列表

2、计算可用计算节点的权值

3、从权值最高的scheduler_host_subset_size个计算节点中随机选择一个计算节点作为创建虚拟机的节点

4、更新选择的计算节点的硬件资源信息,为虚拟机预留硬件资源。