一 调度器
选择一个虚拟机在哪个主机上运行的方式有很多种,目前Nova中实现的有:
- ChanceScheduler(随机调度器):从所有的nova-compute服务正常运行的节点中随机选择。
- FilterScheduler(过滤调度器):根据指定的过滤条件以及权重挑选最佳节点。
- CachingScheduler:FilterScheduler的一种,在FilterScheduler的基础上将主机资源信息缓存在本地内存中,然后通过后台的定时任务定时从数据库中获取最新的主机资源信息。
为了便于扩展,Nova将一个调度器必须实现的接口提取出来成为nova.scheduler.driver.Scheduler,只要继承类SchedulerDriver并实现其中的接口,我们就可以实现一个自己的调度器。
#nova/scheduler/driver.py
class Scheduler(object):
"""The base class that all Scheduler classes should inherit from."""
不同的调度器并不能共存,需要在/etc/nova/nova.conf中通过scheduler_drvier选项指定,默认使用的是FilterScheduler:
scheduler_drvier =nova.scheduler.filter_scheduler.FilterScheduler
#nova/scheduler/filter_scheduler.py
class FilterScheduler(driver.Scheduler):
"""Scheduler that can be used for filtering and weighing."""
def __init__(self, *args, **kwargs):
super(FilterScheduler, self).__init__(*args, **kwargs)
self.options = scheduler_options.SchedulerOptions()
self.compute_rpcapi = compute_rpcapi.ComputeAPI()
self.notifier = rpc.get_notifier('scheduler')
self._supports_affinity = scheduler_utils.validate_filter(
'ServerGroupAffinityFilter')
self._supports_anti_affinity = scheduler_utils.validate_filter(
'ServerGroupAntiAffinityFilter')
![](https://i-blog.csdnimg.cn/blog_migrate/b2fd25a7dd9df18625f3851ecdbc2c3d.png)
FilterScheduler首先使用指定的过滤器Filters(过滤器)得到符合条件的主机,比如内存使用率小于50%,然后对得到主机列表计算权重并排序,获得最佳的一个。
完整来说这个过程可以分为几个阶段:
1 从nova.scheduler.rpcapi.SchedulerAPI发出RPC请求到nova.scheduler.manager.SchedulerManager;
# nova/scheduler/rpcapi.py
class SchedulerAPI(object):
'''Client side of the scheduler rpc API.
def select_destinations(self, ctxt, request_spec, filter_properties):
cctxt = self.client.prepare()
return cctxt.call(ctxt, 'select_destinations',
request_spec=request_spec, filter_properties=filter_properties)
2 从SchedulerManager到调度器(类SchedulerDriver);
# nova/scheduler/rpcapi.py
class SchedulerManager(manager.Manager):
"""Chooses a host to run instances on."""
@messaging.expected_exceptions(exception.NoValidHost)
def select_destinations(self, context, request_spec, filter_properties):
"""Returns destinations(s) best suited for this request_spec and
filter_properties.
The result should be a list of dicts with 'host', 'nodename' and
'limits' as keys.
"""
dests = self.driver.select_destinations(context, request_spec,
filter_properties)
return jsonutils.to_primitive(dests)
3 从SchedulerDriver到Filters;
#nova/scheduler/filter_scheduler.py
class FilterScheduler(driver.Scheduler):
"""Scheduler that can be used for filtering and weighing."""
def select_destinations(self, context, request_spec, filter_properties):
"""Selects a filtered set of hosts and nodes."""
self.notifier.info(context, 'scheduler.select_destinations.start',
dict(request_spec=request_spec))
num_instances = request_spec['num_instances']
selected_hosts = self._schedule(context, request_spec,
filter_properties)
# Couldn't fulfill the request_spec
if len(selected_hosts) < num_instances:
raise exception.NoValidHost(reason='')
dests = [dict(host=host.obj.host, nodename=host.obj.nodename,
limits=host.obj.limits) for host in selected_hosts]
self.notifier.info(context, 'scheduler.select_destinations.end',
dict(request_spec=request_spec))
return dests
4 从Filters到权重计算与排序。
二 Filtering
Filtering就是使用配置文件指定的各种filters过滤掉不符合条件的主机。但是在调用各个Filters之前,这个阶段首先要做的一件事情是根据各个主机当前的可用资源情况,过滤掉那些不满足虚拟机要求的主机,比如内存的容量等。
这就需要从数据库中得到各个主机的资源数据,这些数据的收集与存储都由nova-compute负责,但是nova-computer对数据更新是周期性的,而nova-scheduler在选择最佳主机时则要求数据必须是最新的,因此nova-scheduler中又维护了一份数据,里面包含了从上次数据库更新到现在的主机资源变化情况,这部分工作由nova.scheduler.host_manager.HostState完成。
#nova/scheduler/host_manager.py
class HostState(object):
def update_from_compute_node(self, compute):
"""Update information about a host from its compute_node info."""
if (self.updated and compute['updated_at']
and self.updated > compute['updated_at']):
return
all_ram_mb = compute['memory_mb']
# Assume virtual size is all consumed by instances if use qcow2 disk.
free_gb = compute['free_disk_gb']
least_gb = compute.get('disk_available_least')
如果数据库中数据的更新时间“updated_at”小于nova-scheduler所维护数据的更新时间,则说明数据库中的数据已经比较过时了,此时不需要从数据库更新信息。
显然,为了保持自己所维护的数据为最新,每创建一个虚拟机,nova-scheduler都要将其更新并从主机可用资源中去掉虚拟机使用的部分。
nova-scheduler所维护的资源数据并不会同步到数据库,它只会从数据库同步数据,如果在两次数据同步之间,数据库的内容发生了改变,则nova-scheduler所维护的资源数据会有一定的误差。比如虚拟机销毁时,nova-computer会去更新数据库,但因为这个过程并不需要nova-scheduler的参与,它并不能感知这一动作并更新自己所维护的资源数据去反映虚拟机删除所带来的资源变化。
经过上述的初步过滤后,各个Filter便粉墨登场了。Juno中的Nova支持的Filter共有28个,下面是常用的一些:
- AllHostsFilter:不进行任何过滤。
- RamFilter:依据内存的可用情况过滤,挑选出拥有足够内存的主机。
- ComputeFilter:挑选出所有处于活动状态(Active)的主机。
- TrustedFilter:挑选出所有可信的主机。
- PciPassthroughFilter:挑选出提供PCI SR-IOV支持的主机。
所有的Filter实现都位于nova/scheduler/filters目录
每个Filter都继承自nova.scheduler.filters.BaseHostFilter
#nova/scheduler/filters/__init__.py
class BaseHostFilter(filters.BaseFilter):
"""Base class for host filters."""
def _filter_one(self, obj, filter_properties):
"""Return True if the object passes the filter, otherwise False."""
return self.host_passes(obj, filter_properties)
def host_passes(self, host_state, filter_properties):
"""Return True if the HostState passes the filter, otherwise False.
Override this in a subclass.
"""
raise NotImplementedError()
我们自己也可以很方便地通过继承类BaseHostFilter来创建一个新的Filter,新建Filter只需要实现一个函数host_passes(),返回的结果只有两种:满足条件返回True,否则返回False。
不同的Filter能够共存,比如配置了Filter1、Filter2、Filter3,并且有3个主机Host1、Host2、Host3,按照Filter的顺序,假如Host1和Host3通过了Filter1,那么调用Filter2时,只考虑Host1和Host3,接下来如果只有Host1通过了Filter2,则调用Filter3时,只用考虑Host1。
具体使用哪些Filter,需要在配置文件中指定:
scheduler_available_filters = nova.scheduler.filters.all_filters
scheduler_default_filters = RetryFilter,AvailabilityZoneFilter,ComputeFilter,ComputeCapabilitiesFilter,ImagePropertiesFilter,ServerGroupAntiAffinityFilter,ServerGroupAffinityFilter
其中scheduler_available_filters用于指定所有可用的Filters,scheduler_default_filters则表示,对于可用的Filter,nova-scheduler默认会使用哪些。
三 Weighting
Weighting是指对所有符合调剂主机计算权重(Weight)并排序从而得出最佳的一个。
经过各种过滤之后,会得到一个Host列表,保存了所有通过指定过滤器的Host,其中可能存在多个Host,所以还需要在它们当中选择最优的一个。类似于Filtering,这个过程需要调用指定的各种Weigher模块,得出每个主机总的权重值。
所有的Weighrt实现都位于nova/scheduler/weights目录,比如RAMWeigher
class RAMWeigher(weights.BaseHostWeigher):
minval = 0
#权重的系数,最终排序时需要将每种Weigher得到的权重分别乘以它对应的这个
#系数,有多个Weigher时才有意义。对于RAMWeigher,这个值可以通过配置选项ram_weight_multiplier 进行指定,默认为1.0
def weight_multiplier(self):
"""Override the weight multiplier."""
return CONF.ram_weight_multiplier
def _weigh_object(self, host_state, weight_properties):
"""Higher weights win. We want spreading to be the default."""
#计算权重值,对于RAMWeigher仅仅返回可用内存的大小
return host_state.free_ram_mb