Nova-scheduler浅析




原文地址:http://aspirer2004.blog.163.com/blog/static/1067647201281925258417/

skydrive共享地址:https://skydrive.live.com/redir?resid=B71FFD238B71E836!511

 
本文基于Folsom版本进行分析。
  1. 简介

Nova-scheduler是openstack核心组件nova的核心组件之一,由此可见其在openstack中的地位。

顾名思义,Nova-scheduer是nova的调度器,目前是负责为创建/启动虚拟机实例寻找合适的计算节点,迁移虚拟机的时候也负责检查目的端的物理资源是否足够,其重要性随着计算节点数量的增加而增加。

下图是openstack架构图,从中我们可以看出nova-scheduer是通过与message queue和nova database交互来完成调度任务的。

 

 

Nova-scheduer的简化工作流程可以用下图表示:

也即过滤不可用节点,并对可用节点进行权重排序,根据用户配置的策略选出最优节点。

下面是nova-scheduler的类图:

  1. 计算节点可用资源通知

要想选出策略最优的节点,就需要首先知道各个节点当前的可用资源状况以及请求需要的资源信息,而后者是用户通过API请求主动传递过来的,所以不是nova-scheduer关注的重点。

下图描述了nova-compute服务如何更新其节点资源信息并发布到message queue,以及nova-scheduer如何收集并保存这些信息以备后续使用:

上述图示可以分为三个相互关联的部分:信息收集、信息发布、信息存储。信息收集(上图1.X部分)由nova-compute服务完成,当然它要依赖hypervisor适配层提供的接口,比如libvirt接口或者XenAPI接口等(实际执行收集动作的函数为nova/virt/libvirt/connection.py:HostState.update_status()),收集到的信息供信息发布函数使用;信息发布(上图2.X部分)是nova-compute服务通过RPC机制完成的,因为发布是跨服务的(nova-compute到nova-scheduer),所以使用message queue也是顺理成章的事情,如果信息收集函数没能把最新的信息传递过来,那么发布上次更新到的信息;信息存储(上图message queue下面的部分)是nova-scheduer服务完成的,它从message queue中获取相关信息并存储起来,就可以用来作为相关决策的依据。

这里有个问题就是当上一个资源占用请求还没有处理的时候,又来了一个新的请求,并且被调度到的计算节点的资源只能满足一个请求,那么第二个请求就会失败。

 

  1. 从API到manager

这里的API指的是scheduler的API(当然除了nova-api之外的其他服务的API到manager的流程也是类似的,这里仅讨论nova-scheduer)。我们知道API到manager的消息传递是通过message queue完成的,nova里面的message queue采用的是AMQP,与AMQP建立连接的过程是在服务启动的时候完成的,所以我们首先要看下nova-scheduer服务的启动流程。

Nova-scheduler服务脚本:

 

创建服务实例和最后的阻塞等待就不用多说了,我们直接看启动服务流程,nova/service.py:serve->launch_server->eventlet用greenthread启动主线程执行run_server->Service.start():

我们继续看创建消费者的过程:

这样就走到了ProxyCallback类里面:

继续

我们看到获取method是用getattr()方法实现的,在Service类里面定义了__getattr__方法,

另外在nova-scheduer的manager.py的SchedulerManager也定义了__getattr__方法,用来处理manager中未实现的被调用到的方法,这些方法都被定向到了_schedule方法。

至此API到manager的流程结束。

 

这部分要感谢田田高同学的鼎力基情支持!

  1. 从manager到driver

这部分的流程比较简单,在SchedulerManager类初始化的时候可以看到driver的初始化过程:

比如我们在resize虚拟机的时候,nova的compute-api的resize方法会通过message queue调用scheduler的prep_resize方法:self._cast_scheduler_message(context, {“method”: “prep_resize”, “args”: args}),这样就会调用到SchedulerManager里面的prep_resize方法,进而调用到MultiScheduler里面的schedule_prep_resize方法,

这里我们又看到了一个dict类型的self.drivers,可以在MultiScheduler的__init__方法中找到它的初始化过程,

这里我们看到了两个子driver,compute_driver和volume_driver,也都是可以在配置文件里面配的,默认值如下:

volume_driver用到的地方不多,在nova/volume/api.py:API.create()方法里面有用到,目的是为了选择一个nova-volume节点创建卷。

compute_driver用到的地方就比较多了,几乎所有涉及到启动虚拟机类的动作都跟它有关,比如创建虚拟机、start虚拟机、resize虚拟机、迁移虚拟机等等,因为启动虚拟机涉及到选择在哪个节点上启动的策略问题。我们继续以resize为例进行说明,从MultiScheduler的schedule_prep_resize方法调用到了FilterScheduler里的schedule_prep_resize方法:

这样就把prep_resize动作定向到了特定的nova-compute节点,以便让resize的目的端节点做好相关准备工作。

除了FilterScheduler调度器之外还有其他的调度器,比如ChanceScheduler、SimpleScheduler,ChanceScheduler是随机选择一个运行中的host,SimpleScheduler则是根据已经占用的核数来选择host,当前占用核数最少的host将被选中。这里我们重点讨论默认的FilterScheduler,这个调度器负责host的选择以及选中后host的资源预占,选择host的依据是使用过滤器过滤host和对host计算权重费用,成功通过所有的过滤器并且权重费用最低的host将被选中。

  1. 从Driver到filter

FilterScheduler(目前只支持compute服务调度)有很多的filter用来选择host,可以用的filter通过配置项’scheduler_available_filters’来确定,默认值为’scheduler_available_filters’,根据注释可以知道默认是遍历所有可用的filter,也就是只要在nova/scheduler/filters目录下的*_filter.py都可以使用。

另外还有一个配置项是’scheduler_default_filters’,这个参数可以配置默认使用的filter,也就是说虽然可用的filter很多,但并未全部使用,其默认配置有三个filter:’AvailabilityZoneFilter’, ‘RamFilter’, ‘ComputeFilter’。

我们先看下_scheduler这个方法,它负责筛选可用的Host列表,我们知道如果要做出决策,一定要有数据支持,否则就是瞎蒙了,所以下面我们先看下筛选Host所需要的数据:

从代码里面可以看出需要两个参数作为决策依据:Host和filter_properties,既然是筛选Hosts,那以Host作为输入也是理所当然的,Host信息的获取方式是:首先查询数据库获取到所有的compute 服务节点,然后查询自己之前保存的service_states属性获取相关信息,最后封装成HostState对象返回。

剩下的就是filter_properties了,这个参数保存了虚拟机的规格、以及特定的Host选择要求等信息,这些信息都是由用户在启动虚拟机的时候指定的,比如我们可以在创建虚拟机的API的data里面设置如下参数:

我们还可以指定特定的Host来启动虚拟机或者指定不在特定的Host上启动:

‘force_hosts’: [‘testhost’],ignore_hosts: [‘testhost’]

这两个参数都是在执行过滤动作之前生效的,看下代码实现:

我们甚至还可以传入任意key-value对到scheduler里以备特定用途使用。

filter_properties里面整合了request_spec这个参数,这也是filter过滤Host的主要依据,通常request_spec包含的内容如下,其中黄色高亮部分是对filter或者costfunction比较重要的项:

上面提到的passes_filters方法,是_schedule方法中执行过滤过程filter_hosts用到的,它属于HostState类,也就是说每个需要过滤的Host都要通过这个方法进行过滤,成功通过的返回True,否则返回False。

我们再看_choose_host_filters是如何选择过滤器的,以及返回的是什么东东:

默认的过滤器肯定是可以用的,因为他们都是从可用过滤器里挑选出来的,_choose_host_filters选出的好用的过滤器的host_passes方法最后都作为参数传给了passes_filters,之后被调用用来过滤Host,我们再来看下这三个过滤器的host_passes方法:

 

  1. 从filter到cost function

用预定义的三个过滤器过滤完之后,会得到一个Hosts列表,保存了所有通过三个过滤器的Host,列表中可能不止一个Host,所以还要在这些Host当中选择一个最优的节点,以起到均衡资源分配的目的,选择最优节点的方式就是利用costfunction计算出各个Host启动虚拟机所需的”费用”,消耗”费用”最低的节点将被选中用来执行启动或者迁入虚拟机之类的操作。

上一节中提到过costfunction是在FilterScheduler类的_scheduler方法中通过调用get_cost_functions获取的,代码如下:

我们再看下nova.scheduler.least_cost.compute_fill_first_cost_fn的定义:

现在我们来看最后的”费用”计算过程(也是在FilterScheduler类的_scheduler方法中调用的):

 

  1. 资源预占

还有一个问题就是,如果一次启动多个instance的话,nova-scheduer是一个一个的选择Host的,而且中间不会更新节点的可用资源,这里就有一个可用资源数量不准确的问题,比如Host1被选中启动了一个instance,对nova-scheduer来说Host1的资源并没有变化,第二个instance还是可能被启动到Host1上,这是个非常明显的问题,所以社区肯定是考虑到了的,社区的解决方法就是资源预占,简单来说就是先在nova-scheduer的进程内部把被选中的Host的可用资源中把已经启动到这个Host上的instance的相关资源信息给减掉,这样就可以避免资源数量不准确的问题了。

  1. 其他问题

 

Nova-scheduer多节点布置??–essex版本不支持,folsom正在开发。

问题参考:

https://answers.launchpad.net/nova/+question/199303

社区蓝图:

https://blueprints.launchpad.net/nova/+spec/scheduler-resource-race

Work items:

Added scheduling retries when build errors occur: DONE

Added resource tracking in the compute host to more gracefully control resource usage and provide up-to-date information to the scheduler: INPROGRESS

已经在F-3版本中实现的失败后重新调度代码:

https://review.openstack.org/#/c/9540/