• nova分析(7)—— nova-scheduler


    Nova-Scheduler主要完成虚拟机实例的调度分配任务,创建虚拟机时,虚拟机该调度到哪台物理机上,迁移时若没有指定主机,也需要经过scheduler。资源调度是云平台中的一个很关键问题,如何做到资源的有效分配,如何满足不同情况的分配方式,这些都需要nova-scheduler来掌控,并且能够很方便的扩展更多的调度方法,可能我需要虚拟机调度到空闲的机器,可能还需要将某类型的虚拟机调度到固定的机架等等,现在就来看看Nova-Scheduler是如何完成调度任务的。

    Scheduler启动后,相应的MQ结构如下:

    Exchange(nova,topic) <--- <routing_key:scheduler>---Queue(scheduler) ---> Consumer(scheduler)
    
    Exchange(nova,topic) <---<routing_key:scheduler.controller>---Queue(scheduler.controller) ---> Consumer(scheduler.controller)
    
    Exchange(scheduler_fanout,fanout) <---<routing_key:scheduler>---Queue(scheduler_fanout_xxx)--->Consumer(scheduler_fanout_xxx)

    一、服务启动

    Nova-scheduler 服务的启动入口脚本是 cmd 包下的 scheduler.py ,其主要监听来自于消息队列中 topic=scheduler( 可配置 ) 的消息。在服务启动过程中,其将初始化一个 SchedulerManager 实例作为该服务的 Handler ,来处理接受到的消息请求。

    同时, Nova-scheduler 服务在启动的过程中,会将自己注册到 DB 中,即将自己的 host 、 binary 、 topic 、 report_count 信息添加到 services 表中,并将自己同样注册到 ServiceGroup 服务中,默认是 DBServiceGroup 服务,并定时向ServiceGroup 服务发送心跳,其本质上就是定时地更新 services 表中的report_count 字段。另, ServiceGroup 服务还有基于 MC 、 ZK 两种实现方式。

    二、主要源码说明

    、 manager.py 

    SchedulerManager 类主要来处理服务接收的消息,目前主要的操作就是live_migration 

    run_instance 、 prep_resize 、 select_hosts 、 select_destinations ,后两者将要 deprecated 。这些操作本质上还是要依赖于具体的 SchedulerDriver 去完成。

    另外, _SchedulerManagerV3Proxy 类与 SchedulerManager 类主要的区别在于消息版本的不同,

    _SchedulerManagerV3Proxy 消息是 3.0 版本,而 SchedulerManager 是 2.9 版本。

    、 driver.py 、 chance.py 、 filter_scheduler.py 、 caching_scheduler.py 

    Scheduler 类是所有 SchedulerDriver 类的基类,其定义了关键的接口协议。目前主要有 ChanceScheduler 、 FilterScheduler 和 CachingScheduler 三种实现方式,具体说明如下:

    ) ChanceScheduler :随机选择一台物理机,前提是该物理机上的 nova-compute 服务正常且该物理机不在指定的 ignore_hosts 列表中。

    ) FilterScheduler :筛选出能通过整个过滤器链的物理机,然后根据相应指标计算权重,并进行排序,最后返回一个 best host 。具体过滤器详见下文。

    ) CachingScheduler : FilterScheduler 的子类,在 FilterScheduler 的基础上将 host 资源信息缓存在了本地内存中,然后通过后台定时任务定时从 DB 中拉取最新的 host 资源信息。在多节点环境下存在问题。

    、 host_manager.py HostState :表示物理机的资源信息集合,比如当前可用内存、已用内存、虚拟机数量、任务数量、 io 负载、可用磁盘、已用磁盘等等,以及一些更新方法。

    HostManager :该类是最重要的 host 筛选类,其内部主要有三个关键函数:

    get_filtered_hosts :返回经过层层过滤器筛选后的 host 列表,其依赖于FilterHandler

    get_weighed_hosts :返回经过权重计算排序后的 host 列表,其依赖于WeightHandler

    get_all_host_states :获取当前最新的 host 资源列表。

    、 filters 

    这里是全部的各种 filter 的实现方式,主要有:

    ) __init__.py 

    BaseHostFilter :继承自 BaseFilter ,是所有具体 Filter 子类的基类,其中一个较重要的成员变量 run_filter_once_per_request 表示该 Filter 子类是否在在一次 request 中仅执行一次。

    HostFilterHandler :最重要的函数 get_filtered_objects , load 指定的filter_classes ,层层过滤

    host ,最后返回符合所有过滤条件的 host 列表。

    )剩余就是很多个不同的具体 Filter 子类的实现,基于各自不同的策略,具体暂不一一介绍。

    、 weights 

    ) __init__.py BaseHostWeigher :继承自 BaseWeigher ,是所有具体 Weigher 子类的基类,其中主要的函数就是 _weigh_object ,由具体子类实现,完成对目标 host 列表权重的计算与排序,具体策略见下文。

    HostWeightHandler :最重要的函数 get_weighed_objects , load 指定的weigher_classes ,层层依据各自指标对目标 host 列表进行权重计算,最后排序后返回。

    ) ram.py RAMWeigher :基于剩余可用内存进行权重计算排序。

    ) metrics.py MetricsWeigher :支持自定义一些指标进行权重计算排序。

    三、流程与算法说明

    、这里主要是基于 FilterScheduler 来说明下在 Scheduler 服务中创建虚拟机(包括筛选物理机)的流程(其他流程同理):

    )当 nova-scheduler 服务从 MQ 中接收到 run_instance 消息时,则由SchedulerManager 类对其进行处理,执行 run_instance 函数。

    ) SchedulerManager 将具体逻辑交由具体的 SchedulerDriver 执行,这里就是 FilterScheduler 

    ) FilterScheduler 接收到请求后,开始进行 host 筛选,选择出 instance_num个目标 host 

    )首先判断这次 request 的 retry 次数是否已经达到 max_attempts 次,若达到,则停止 retry ,抛出 NoValidHost 异常;若是第一次,则设置 filter_properties 属性 retry={num_attempts:1,hosts:[]} ;否则继续执行。

    )然后从 DB 中获取当前最新的 compute node 节点资源信息,并更新本地内存 缓存 中的 host 表资源数据。这里的好处是当一次批量申请多个虚拟机时,可以避免多次请求DB ,但是一个不容忽视的问题就是如果多个 Scheduler 服务并行部署时,就会因资源信息的延迟不同步而导致虚拟机创建失败(实际后台 host 已不足)。

    )再次开始循环为每个 instance 筛选目标 host :首先根据指定的ignore_hosts 、 force_hosts force_nodes 属性对候选 hosts 列表进行筛选,留下符合这些属性条件的 hosts

    )然后根据配置指定的 filter 来循环层层过滤 6 )中的候选 hosts ,具体代码如下:

    def get_filtered_objects(self, filter_classes, objs,filter_properties, index=0):
            list_objs = list(objs)
            LOG.debug(_("Starting with %d host(s)"), len(list_objs))
            for filter_cls in filter_classes:
            cls_name = filter_cls.__name__
            filter = filter_cls()
    
             if filter.run_filter_for_index(index):
               objs = filter.filter_all(list_objs,filter_properties)
               if objs is None:
                   LOG.debug(_("Filter %(cls_name)s says to stop filtering"),
                       {'cls_name': cls_name})
                return
              list_objs = list(objs)
              if not list_objs:
                LOG.info(_("Filter %s returned 0 hosts"), cls_name)
                break
              LOG.debug(_("Filter %(cls_name)s returned  %(obj_len)d host(s)"),
                 {'cls_name': cls_name, 'obj_len': len(list_objs)})
            return list_objs

    )在 7 )中筛选的这批 hosts 此时可以认为是满足该 instance 的资源要求,然后开始对这批hosts 根据配置的各个 Weigher 子类进行权重计算并排序,具体如下:

    def get_weighed_objects(self, weigher_classes, obj_list,weighing_properties):
       """Return a sorted (descending), normalized list of WeighedObjects."""
    
      if not obj_list:
           return []
    
      weighed_objs = [self.object_class(obj, 0.0) for obj in obj_list]
      for weigher_cls in weigher_classes:
            weigher = weigher_cls()
            weights = weigher.weigh_objects(weighed_objs, weighing_properties)
    
            # Normalize the weights
            weights = normalize(weights,minval=weigher.minval,maxval=weigher.maxval)
    
             for i, weight in enumerate(weights):
                  obj = weighed_objs[i]
                  obj.weight += weigher.weight_multiplier() * weight
    
               return sorted(weighed_objs, key=lambda x: x.weight, reverse=True)

    )从 8 )中根据权重排序后的 hosts 取出 scheduler_host_subset_size (默认是 1 )个 host ,表明未来该 instance 会落在该 host 上,并 预扣掉该 host 的剩余可用资源 (注意这里仅仅是修改的本地缓存中 host 资源信息)。

    10 )在为每个 instance 选择好候选目标 host 后,开始循环创建虚拟机:更新instance ( host,node,scheduled_at ),并向 nova-compute 服务发送 rpc 请求。

    11 ) nova-compute 服务接收到创建虚拟机请求后:首先将 DB 中 instance状态设置为vm_state=BUILDING , task_state=SCHEDULING 

    12 )然后获取该 node 的 ResourceTracker 实例(其主要是维护跟踪该 node 的资源明细),claim 将要预留资源信息,设置 mac 和 network 信息,最后开始构建虚拟机。期间在不同的 task 阶段,会相应更新 DB 中 vm_state 和 task_state 状态。

    13 )若创建虚拟机流程中发生错误,会重新向 MQ 发送创建虚拟机请求,此时nova-scheduler服务会继续处理该消息,则继续回到 1 )步骤。

    、下图展示了整个调度过程的大体流程:

    这里主要说明下各个 host 的权重是如何计算出来的,大体公式如下: 

    host_weight = Weigher1_multiplier * Weigher1_host_weight+……+ WeigherN_multiplier

    * WeigherN_host_weight

    举例说明:

    假若有 6 台候选 Host : H1 , H2 , H3 , H4 , H5 , H6

    个 Weigher : W1 , W2 , W3 ,且其 multiplier 分别为 1 , 2 , 1

    具体一个 Weigher 如何计算出一个 Host 在该 Weigher 的权重,则依赖各个Weigher 自己的

    实现,比如 RAMWeigher 则是以每个 Host  free_ram_mb 作为其原生权重。

    )假若 H1~H6 经过 W1 计算后的原生权重值如下:

    W(multiplier)

    H1

    H2

    H3

    H4

    H5

    H6

    W1 (1)

    50

    10

    20

    10

    90

    110

    然后需要对这些原始权重值进行 normalize 化,具体方式是:找出这些原始权重值中的

    最大值 max_weight 和最小值 min_weight ,然后按照如下公式来计算其比例权重值:

    ratio_weight = ( raw_weight - min_weight ) / ( max_weight - min_weight )

    那么 H1~H6 原生权重 W1 normalize 化之后 (ratio_weight) 

    W(multiplier)

    H1

    H2

    H3

    H4

    H5

    H6

    W1 (1)

    0.4

    0

    0.1

    0

    0.8

    1

    )假若 H1~H6 经过 W2 计算后的原生权重值如下:

    W(multiplier)

    H1

    H2

    H3

    H4

    H5

    H6

    W1 (1)

    0.4

    0

    0.1

    0

    0.8

    1

    W2 (2)

    10

    4

    6

    11

    1

    9

    那么 H1~H6 原生权重 W2 normalize 化之后 (ratio_weight) 

    W(multiplier)

    H1

    H2

    H3

    H4

    H5

    H6

    W1 (1)

    0.4

    0

    0.1

    0

    0.8

    1

    W2 (2)

    0.9

    0.3

    0.5

    1

    0

    0.8

    )假若 H1~H6 经过 W3 计算后的原生权重值如下:

    W(multiplier)

    H1

    H2

    H3

    H4

    H5

    H6

    W1 (1)

    0.4

    0

    0.1

    0

    0.8

    1

    W2 (2)

    0.9

    0.3

    0.5

    1

    0

    0.8

    W3 (1)

    15

    25

    10

    5

    10

    5

    那么 H1~H6 原生权重 W3 normalize 化之后 (ratio_weight) 

    W(multiplier)

    H1

    H2

    H3

    H4

    H5

    H6

    W1 (1)

    0.4

    0

    0.1

    0

    0.8

    1

    W2 (2)

    0.9

    0.3

    0.5

    1

    0

    0.8

    W3 (1)

    0.5

    1

    0.25

    0

    0.25

    0

    )最后得出 H1~H6 的最终权重为:

    W(multiplier)

    H1

    H2

    H3

    H4

    H5

    H6

    W1 (1)

    0.4

    0

    0.1

    0

    0.8

    1

    W2 (2)

    0.9

    0.3

    0.5

    1

    0

    0.8

    W3 (1)

    0.5

    1

    0.25

    0

    0.25

    0

    ratio_weights

    2.7

    1.6

    1.35

    2

    1.05

    2.6

    )最后对 H1~H6 的 ratio_weight 进行降序排序,那么这次 Host 的排序则为H1 , H6 , H4 , H2 , H3 , H5

    、 nova-scheduler 与 nova-compute 之间是如何知道具体 host 资源的详情的?

    )每当 nova-compute 服务起来之后,会自动更新 DB 中对应 compute_node的资源信息,若是第一次,则会将自己添加到 compute_node 中;并同时会将这些资源信息缓存在本地内存中,并由一个 ResourceTracker 实例进行跟踪。

    )每次 nova-compute 服务接收到创建虚拟机的申请时,通过ResourceTracker 进行instance_claim 时,会将这次申请所需要扣除的虚拟机资源大小及时反映到compute node 中,同时更新本地 ResourceTracker 中的缓存信息。

    ) nova-scheduler 每次在接收到创建虚拟机请求时,都会从 compute node中取出最新的 host 源信息,被暂时缓存在本地内存中,当成功选择出一台 host 时,会及时扣除本地缓存相应的 host 资源信息。

    参考文档

    http://www.choudan.net/2013/08/11/Nova-Scheduler分析.html

    http://www.choudan.net/2013/08/09/Nova-Service启动.html

    http://blog.csdn.net/gaoxingnengjisuan/article/details/15615743

    http://blog.csdn.net/cloudresearch/article/details/19051043

    http://blog.csdn.net/gaoxingnengjisuan/article/details/15734437

    http://www.tuicool.com/articles/Vn2iia

    http://docs.openstack.org/developer/nova/devref/filter_scheduler.html

  • 相关阅读:
    浅谈MVC3.0 及其URL路由机制
    了解 NHibernate
    ASP.NET与 .NET MVC的认识
    ASP.NET中的Webservice
    MVC3.0 将网站设为首页和加为收藏的实现(IE/Firefox)
    ASP.NET页面生命周期
    MVC3.0 开发过程中遇到错误及解决方案(不断更新中。。。)
    scienceWord总结
    设计模式——结构型模式(包含7种)
    sql 、linq、lambda 查询语句的区别
  • 原文地址:https://www.cnblogs.com/feisky/p/3875361.html
Copyright © 2020-2023  润新知