转载自:http://blog.lightcloud.cn/?p=123#comment-124

Scheduler调度器
Scheduler是调度服务器,为虚拟机分配节点。
Scheduler.api提供了其他服务调用scheduler的接口。调用会通过rabbit-MQ传递到SchedulerManger。
一、调度器的启动

1.    Scheduler由脚本  nova/bin/nova-scheduler启动。

怎么创建并启动一个服务:

?
1
2
3
4
5
6
7
8
     utils.default_flagfile()                #检查是否带有配置文件,如果没有则用默认配置文件
/ etc / nova / nova.conf
     flags.FLAGS(sys.argv)                #把启动脚本时带的配置参数加到flags中,flags保存了所有配置信息
     logging.setup()                            #日志模块
     utils.monkey_patch()
     server = service.Service.create(binary = 'nova-scheduler' )                #创建一个服务
     service.serve(server)                #启动服务
     service.wait()                              #等到请求

可以看到启动一个服务先用service.Service.create()来创建一个Server对象,然后调用server和wait就可以了。

创建Server对象时只传了一个binary参数, binary是一个字符串 ”nova-scheduler”,创建什么服务就是根据这个字符串识别的。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@classmethod
def create( cls , host = None , binary = None , topic = None , manager = None ,
                    report_interval = None , periodic_interval = None ):
     if not host:
         host = FLAGS.host
     if not binary:
         binary = os.path.basename(inspect.stack()[ - 1 ][ 1 ])
     if not topic:
         topic = binary.rpartition( 'nova-' )[ 2 ]
     if not manager:
         manager = FLAGS.get( '%s_manager' % topic, None )
     if not report_interval:
         report_interval = FLAGS.report_interval
     if not periodic_interval:
         periodic_interval = FLAGS.periodic_interval
     service_obj = cls (host, binary, topic, manager,
                              report_interval, periodic_interval)
 
     return service_obj

如果没有指定manager,则manager由topic连接上”_manager”来指定manager的名字,这个manager就是一个服务接收请求,处理请求的部分,是服务的关键部分。

未指定topic时,由binary去掉开头的”nova-“ 生成。所有创建scheduler服务器时manager 为 scheduler_manager,这时就会从flags中搜索scheduler_manager所对应的类名,来生成server对象。

创建的server有个manager属性,指向服务对应的manager,由前面的很容易知道server.manager指向SchedulerManager对象。

?
1
2
3
4
5
6
7
8
def __init__( self , host, binary, topic, manager, report_interval = None ,
                     periodic_interval = None , * args, * * kwargs):
     self .host = host
     self .binary = binary
     self .topic = topic
     self .manager_class_name = manager
     manager_class = utils.import_class( self .manager_class_name)
     self .manager = manager_class(host = self .host, * args, * * kwargs)

再来看看service.serve(server)是怎么执行的

?
1
2
3
4
5
6
def serve( * servers):
     global _launcher
     if not _launcher:
         _launcher = Launcher()
     for server in servers:
         _launcher.launch_server(server)

就是调用Launcher().launch_server(server)

?
1
2
3
def launch_server( self , server):
     gt = eventlet.spawn( self .run_server, server)
     self ._services.append(gt)

二、SchedulerManager类

?
1
2
3
4
5
def __init__( self , scheduler_driver = None , * args, * * kwargs):
     if not scheduler_driver:
         scheduler_driver = FLAGS.scheduler_driver
     self .driver = utils.import_object(scheduler_driver)
     super (SchedulerManager, self ).__init__( * args, * * kwargs)

driver属性默认指向scheduler.multi.MultiScheduler对象。

在SchedulerManager中有方法

?
1
2
3
4
def __getattr__( self , key):
     """Converts all method calls to use the schedule method
     调用未显式定义的方法时转成偏函数"""
     return functools.partial( self ._schedule, key)

对于未在SchedulerManager中显式定义的方法,返回_schedule方法并绑定调用的方法名。

显示定义的方法会直接调用。

现以run_instance请求为例。

在接收到启动虚拟机请求时,SchedulerManager.run_instance被调用

?
1
2
3
def run_instance( self , context, topic, * args, * * kwargs):
     args = (context,) + args
     return self .driver.schedule_run_instance( * args, * * kwargs)

转到MultiScheduler.scheduler_run_instance方法

MultiScheduler.scheduler_run_instance定义如下:

?
1
2
def schedule_run_instance( self , * args, * * kwargs):
     return self .drivers[ 'compute' ].schedule_run_instance( * args, * * kwargs)

其中MultiScheduler.drivers为

?
1
2
self .drivers = { 'compute' : filter_scheduler.FilterScheduler(),
                          'volume' : chance.ChanceScheduler() }

所对计算任务请求是转到FilterScheduler类来处理,对于卷的请求是由ChanceScheduler类处理。

最后对启动虚拟机的任务由FilterScheduler.schedule_run_instance来处理。

三、调度算法
1.得到权重和计算成本的函数列表:(FilterScheduler. get_cost_functions)
i. 每个topic对应一个权重和计算成本的列表,如果之前已经缓存过,就直接返回
ii.  获得所有权重和计算成本的函数列表,缓存下来,然后返回。(计算成本的函数是” least_cost_functions”对应的,默认是只有compute_fill_first_cost_fn,定义如下:

?
1
2
def compute_fill_first_cost_fn(host_state, weighing_properties):
     return host_state.free_ram_mb

就是返回剩余的内在大小
每个计算成本的函数对就一个权重因子,是在flag中定义,名字为函数名加”_weight”。
compute_fill_first_cost_fn 对应的权重因子为compute_fill_first_cost_fn_weight。
在D版中为 1.0,在Essex版中为-1.0。

即选择节点的优先级仅是按剩余内存大小来处理的,在D版是剩余内存越小,优先级越高;而在Essex版是剩余内存越大,优先级越高,这应该是D版的一个bug,在Essex版得到修改。

计算成本的函数和对应的权重因子可以在/etc/nova/nova.conf中配置:
可以用逗号分隔多个计算函数

?
1
2
3
least_cost_functions=nova.scheduler.least_cost.compute_fill_first_cost_fn,nova.scheduler.least_cost.my_cost_fn
compute_fill_first_cost_fn_weight=1.0
my_cost_fn_weight=0.5

2.    得到所有可用的未过滤的节点信息(HostManager. get_all_host_states)
i.  首先会从数据库中选出所有可用的未过滤的节点组成的一个字典host_state_map。
ii. 所有的计算节点都保存在nova库的coupute_nodes表中。
iii. 从表中取出所有节点必要的信息(如节点的内存容量,虚拟CPU个数,硬盘容量等),放到字典host_state_map中
iv. 从数据库表nova.instances中取出所有实例instance的信息。
v. 根据instance所在的主机,从host_state_map中对应主机减去实例所占用的资源,得到主机的剩余资源

3.    根据定义的过滤方法过滤掉不合适的主机(HostManager. filter_hosts)
i. 默认过滤方法为
‘AvailabilityZoneFilter’,
‘RamFilter’,
‘ComputeFilter’

三个类,在flag中scheduler_default_filters定义

过滤方法对应的类都是继承自scheduler.filters.BaseHostFilter类,只要实现了host_passes方法就可以实现对主机的过滤,返回真就是这个主机符合条件,返回假就是不符合条件,被过滤掉。

以RamFilter为例:

?
1
2
3
4
5
6
7
8
9
class RamFilter(filters.BaseHostFilter):
     """Ram Filter with over subscription flag"""
 
     def host_passes( self , host_state, filter_properties):
         """Only return hosts with sufficient available RAM."""
         instance_type = filter_properties.get( 'instance_type' )
         requested_ram = instance_type[ 'memory_mb' ]
         free_ram_mb = host_state.free_ram_mb
         return free_ram_mb * FLAGS.ram_allocation_ratio > = requested_ram

只是判断了主机剩余内存乘以内存分配比率大于申请内在,就通过本次过滤,至于能不能申请还要看其他过滤条件,有一个条件不成立就会被过滤掉。
也可以在nova.conf中定义过滤方法:

?
1
2
3
4
scheduler_available_filters定义可用的过滤器,可以多次定义,每个scheduler_driver=nova.scheduler.distributed_scheduler.FilterScheduler
scheduler_available_filters=nova.scheduler.filters.standard_filters
scheduler_available_filters=myfilter.MyFilter
scheduler_default_filters=RamFilter,ComputeFilter,MyFilter

4.    根据计算成本函数和对应权重计算最适合主机:
i.  在least_cost.weight_sum计算
ii. 根据成本计算函数列表生成一个矩阵(网格),
iii. 每行为一个计算函数对所有主机的计算结果,即每列对应一个主机通过所有函数的成本计算结果(此处和代码中英文注释不同,疑注释有误);
iv. 每列为一个主机通过各函数成本计算结果,加起来为主机总的成本,得到所有主机总的成本。
v. 成本最低的为最优主机,被选出来


Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐