1、前面已经介绍Nova Scheduler服务的启动流程,我们知道Nova Scheduler服务作为一个调度者,其核心便是调度
算法。接下来我们就分析一下Nova Scheduler服务的调度算法吧。
2、在配置文件中,调度算法默认的驱动类是FilterScheduler,该类位于/nova/scheduler/filter_scheduler.py
中。FilterScheduler的schedule_run_instance方法调用了_schedule方法获取执行调度算法,获取加权主机列表。
_schedule方法实现了虚拟机调度算法。
class FilterScheduler(driver.Scheduler):算法的核心实现在FilterScheduler类的_schedule方法。后面的_provision_resource方法实际上是远程调用了
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)
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方法获取所有活动的计算节点列表。
host_manager变量是一个HostManager对象。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、更新选择的计算节点的硬件资源信息,为虚拟机预留硬件资源。