代码调用流程:

1. nova.scheduler.client.query.SchedulerQueryClient#select_destinations
2. nova.scheduler.rpcapi.SchedulerAPI#select_destinations
3. nova.scheduler.manager.SchedulerManager#select_destinations
4. nova.scheduler.filter_scheduler.FilterScheduler#select_destinations

scheduler的rpcapi和manager属于同步调用。

在第三步中scheduler会调用placement提供的API,对所有的`compute node`进行初步的筛选,placement的API会返回一个字典,格式如下:

{
"provider_summaries": {
"4cae2ef8-30eb-4571-80c3-3289e86bd65c": {
"resources": {
"VCPU": {
"used": 2,
"capacity": 64
},
"MEMORY_MB": {
"used": 1024,
"capacity": 11374
},
"DISK_GB": {
"used": 2,
"capacity": 49
}
}
}
},
"allocation_requests": [
{
"allocations": [
{
"resource_provider": {
"uuid": "4cae2ef8-30eb-4571-80c3-3289e86bd65c"
},
"resources": {
"VCPU": 1,
"MEMORY_MB": 512,
"DISK_GB": 1
}
}
]
}
]
}

对于placement API筛选出的节点,scheduler会再度进行筛选,大概的筛选过程:all hosts => filtering => weighting => random
1. get all hosts:这里的all host当然不是指环境中所有的host,而是在通过placement API,返回的所有host的详细信息;
2. filtering:首先过滤ignore host和force host,如果force host或者force node直接返回即可。然后结合nova的配置文件中available_filters和enabled_filters参数,依次执行所有的filter。下面我们举几个filter的例子,执行filter的入口:

nova.filters.BaseFilterHandler#get_filtered_objects

    def get_filtered_objects(self, filters, objs, spec_obj, index=0):
list_objs = list(objs)
LOG.debug("Starting with %d host(s)", len(list_objs))
part_filter_results = []
full_filter_results = []
log_msg = "%(cls_name)s: (start: %(start)s, end: %(end)s)"
# 循环遍历配置文件中指定的filters
for filter_ in filters:
if filter_.run_filter_for_index(index):
cls_name = filter_.__class__.__name__
# 记录开始该执行filter之前的host的个数
start_count = len(list_objs)
# 对所有的host执行该filter,返回只有经过该filter的host
objs = filter_.filter_all(list_objs, spec_obj)
if objs is None:
LOG.debug("Filter %s says to stop filtering", cls_name)
return
list_objs = list(objs)
end_count = len(list_objs)
part_filter_results.append(log_msg % {"cls_name": cls_name,
"start": start_count, "end": end_count})
if list_objs:
remaining = [(getattr(obj, "host", obj),
getattr(obj, "nodename", ""))
for obj in list_objs]
full_filter_results.append((cls_name, remaining))
else:
LOG.info(_LI("Filter %s returned 0 hosts"), cls_name)
full_filter_results.append((cls_name, None))
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

接下来介绍几个filter。

class AvailabilityZoneFilter(filters.BaseHostFilter):

    # 如果是一次创建多个虚机,则AvailabilityZoneFilter指执行一次
run_filter_once_per_request = True
# 所有的filter都需要实现该方法
def host_passes(self, host_state, spec_obj):
# 获取request_spec中指定的availability_zone,这里需要强调一下,如果创建时,没有指定--availability-zone 参数,request_sepc中的availability_zone就是空的。
availability_zone = spec_obj.availability_zone
# 如果request_spec中availability_zone值为空,那么也就是这个操作是允许跨AZ操作的。
if not availability_zone:
return True
# 获取host的availability_zone信息,首先获取该host所属的aggregate信息,aggregate信息中有availability_zone相关的信息
metadata = utils.aggregate_metadata_get_by_host(
host_state, key='availability_zone') if 'availability_zone' in metadata:
# 判断request_spec中指定的availability_zone是否在该host所属的availability_zone中。
hosts_passes = availability_zone in metadata['availability_zone']
host_az = metadata['availability_zone']
else:
hosts_passes = availability_zone == CONF.default_availability_zone
host_az = CONF.default_availability_zone if not hosts_passes:
LOG.debug("Availability Zone '%(az)s' requested. "
"%(host_state)s has AZs: %(host_az)s",
{'host_state': host_state,
'az': availability_zone,
'host_az': host_az}) return hosts_passes
nova.scheduler.filters.image_props_filter.ImagePropertiesFilter#host_passes

    # 主要是根据镜像中的property的值进行过滤,在ironic的调度中会使用到。
def host_passes(self, host_state, spec_obj):
image_props = spec_obj.image.properties if spec_obj.image else {}
# 判断该compute_node是否支持image的property属性中指定的参数值。
if not self._instance_supported(host_state, image_props,
host_state.hypervisor_version):
LOG.debug("%(host_state)s does not support requested "
"instance_properties", {'host_state': host_state})
return False
return True def _instance_supported(self, host_state, image_props,
hypervisor_version):
img_arch = image_props.get('hw_architecture') # 架构,i686或x86_64
img_h_type = image_props.get('img_hv_type') # hypervisor 类型
img_vm_mode = image_props.get('hw_vm_mode') # 虚拟化类型
…………
# 获取该compute_node支持的instance类型,返回值为列表。比如:
[["x86_64", "baremetal", "hvm"]]
[["i686", "qemu", "hvm"], ["i686", "kvm", "hvm"], ["x86_64", "qemu", "hvm"], ["x86_64", "kvm", "hvm"]]
supp_instances = host_state.supported_instances
…………
比较规则
def _compare_props(props, other_props):
# 对image的property指定的所有值进行遍历
for i in props:
查看该property是否是该compute_node支持的
if i and i not in other_props:
return False
return True
# 对该compute_node支持的所有类型进行遍历
for supp_inst in supp_instances:
if _compare_props(checked_img_props, supp_inst)

对于Ironic的调度需要我们着重使用到ImagePropertiesFilter,虚机使用的镜像和裸机使用的镜像中的property的值是不同的,再结合相关的placement的调度,实现虚机不会调度到ironic node,同时创建裸机不会调度到qemu的node。

3. 把过滤后的hosts计算权重并且进行最优排序,下面我们举几个weight的例子:

class BaseWeightHandler(loadables.BaseLoader):
object_class = WeighedObject def get_weighed_objects(self, weighers, obj_list, weighing_properties):
"""Return a sorted (descending), normalized list of WeighedObjects."""
# obj_list 表示filter筛选出的所有hosts
# weighing_properties 表示request_sepc信息
weighed_objs = [self.object_class(obj, 0.0) for obj in obj_list]
# 如果经过filter筛选只剩一个host,则无需进行权重的比较,直接返回该host即可
if len(weighed_objs) <= 1:
return weighed_objs
# 根据配置文件中指定的weigher_classes,逐个计算权重
for weigher in weighers:
# 以RAMWeigher为例进行说明
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]
# 将计算后的权重值,保存到host信息中,并且将所有类型的权重加到一块,如果我们想要增加某种类型的权重比例,我们可以修改配置文件中*_weight_multiplier的值,比如我们想要在权重的计算中有关内存的权重占更大的作用,那么我们可以通过调节ram_weight_multiplier的值达到效果。
obj.weight += weigher.weight_multiplier() * weight
# 按照权重进行性排序(倒序)
return sorted(weighed_objs, key=lambda x: x.weight, reverse=True) class RAMWeigher(weights.BaseHostWeigher):
minval = 0 def weight_multiplier(self):
"""Override the weight multiplier."""
return CONF.filter_scheduler.ram_weight_multiplier def _weigh_object(self, host_state, weight_properties):
"""Higher weights win. We want spreading to be the default."""
# 直接返回该节点的剩余内存,也就是剩余内存越多的节点,有关内存的权重越大。
return host_state.free_ram_mb

4. random,这个过程我们通过代码进行详细的分析。

host_subset_size = CONF.filter_scheduler.host_subset_size
if host_subset_size < len(weighed_hosts):
weighed_subset = weighed_hosts[0:host_subset_size]
else:
weighed_subset = weighed_hosts
# 使用随机算法,从N个中抽取1个
chosen_host = random.choice(weighed_subset)
weighed_hosts.remove(chosen_host)
return [chosen_host] + weighed_hosts

对于host_subset_size参数,默认值为1。官方是这样解释的:如果设置大于1的正整数,当有多个scheduler进程处理相同的请求是会减少调度到同一台host的可能性,创造了一种竞争机制。从N个host中挑选最适合请求的一个host,会减少冲突。然而,如果该值设置的越大,对于给定的请求,选择的主机可能不太优化。

boot,rebuild,resize,migrate有关的scheduler流程的更多相关文章

  1. Migrate Instance 操作详解 - 每天5分钟玩转 OpenStack(40)

    Migrate 操作的作用是将 instance 从当前的计算节点迁移到其他节点上. Migrate 不要求源和目标节点必须共享存储,当然共享存储也是可以的. Migrate 前必须满足一个条件:计算 ...

  2. Resize Instance 操作详解 - 每天5分钟玩转 OpenStack(41)

    Resize 的作用是调整 instance 的 vCPU.内存和磁盘资源. Instance 需要多少资源是定义在 flavor 中的,resize 操作是通过为 instance 选择新的 fla ...

  3. 最近做的一个Spring Boot小项目,欢迎大家访问 http://39.97.115.152/

    最近做的一个Spring Boot小项目,欢迎大家访问 http://39.97.115.152/,帮忙找找bug,网站里有源码地址 网站说明 甲壳虫社区(Beetle Community) 一个开源 ...

  4. 关于DSP的boot mode / boot loader /上电顺序 /在线升级等问题的总结

    使用器件 ti dsp c2000 2837x 1.dsp的上电过程和boot mode以及boot loader 1)dsp的上电顺序, 对于双核系统而言 , 他的上电启动顺序如下所示: 系统复位或 ...

  5. CentOS系统启动流程你懂否

    一.Linux内核的组成 相关概念: Linux系统的组成部分:内核+根文件系统 内核:进程管理.内存管理.网络协议栈.文件系统.驱动程序. IPC(Inter-Process Communicati ...

  6. Linux启动流程与模块管理(15)

    系统的启动其实是一项非常复杂的过程,因为内核得要检测硬件并加载适当的驱动程序,接下来则必须要调用程序来准备好系统运行的环境,以让用户能够顺利的操作整台主机系统,如果你能够理解系统启动的原理,那么将有助 ...

  7. 【docker】docker部署spring boot项目在服务器上

    IDE:idea 工具:docker spring boot:2.0.1 ======================================== 简单记录一下流程,以供参考: 第一步:首先得 ...

  8. Spring Boot Security And JSON Web Token

    Spring Boot Security And JSON Web Token 说明 流程说明 何时生成和使用jwt,其实我们主要是token更有意义并携带一些信息 https://github.co ...

  9. docker部署spring boot项目在服务器上

    IDE:idea 工具:docker spring boot:2.0.1 ======================================== 简单记录一下流程,以供参考: 第一步:首先得 ...

随机推荐

  1. 串行通讯协议--起止式异步通讯协议(UART)

    起止式异步通讯协议: 特点与格式: 起止式异步协议的特点是一个字符一个字符传输,并且传送一个字符总是以起始位开始,以停止位结束,字符之间没有固定的时间间隔要求.其格式如图3 所示.每一个字符的前面都有 ...

  2. LeetCode41.缺失的第一个正数 JavaScript

    给定一个未排序的整数数组,找出其中没有出现的最小的正整数. 示例 1: 输入: [1,2,0] 输出: 3 示例 2: 输入: [3,4,-1,1] 输出: 2 示例 3: 输入: [7,8,9,11 ...

  3. ZooKeeper系列(2)--基于ZooKeeper实现简单的配置中心

    ZooKeeper节点的类型分为以下几类:  1. 持久节点:节点创建后就一直存在,直到有删除操作来主动删除该节点 2. 临时节点:临时节点的生命周期和创建该节点的客户端会话绑定,即如果客户端会话失效 ...

  4. Unity 游戏框架搭建 2018 (一) 架构、框架与 QFramework 简介

    约定 还记得上版本的第二十四篇的约定嘛?现在出来履行啦~ 为什么要重制? 之前写的专栏都是按照心情写的,在最初的时候笔者什么都不懂,而且文章的发布是按照很随性的一个顺序.结果就是说,大家都看完了,都还 ...

  5. Xcode 控制台打印Unicode字符串转换为中文

    在Xcode的控制台里直接打印一个数组或者字典,输出的都是一些Unicode的编码,不方便调试.    要想看到中文,则要去获取对应的key或者数组下标.得到具体某一个对象才能看到中文,给我们调试起来 ...

  6. P1015 回文数解题思路(非原创)

    测试 #include<bits/stdc++.h> using namespace std; int n,m,step; string nn; int len,nex; bool dfs ...

  7. Font Awesome图标字体

    1.unicode unicode是字体在网页端最原始的应用方式,特点是: 兼容性最好,支持ie6+,及所有现代浏览器. 支持按字体的方式去动态调整图标大小,颜色等等. 但是因为是字体,所以不支持多色 ...

  8. 第一章 程序设计和C语言(笔记)

    一.程序和程序语言 程序:完成某项事务所预设的活动方式和活动过程. 程序设计:人们描述计算机要做的工作. 对于工作过程的细节动作描述就是一个“程序”. 在一个程序描述中,总有一批预先假定的“基本动作” ...

  9. 1、win10下的Docker+Redis 的下载及简单使用

    一.下载Docker: 因为始终注册docker账号不成功,所以在这里点击下载. 选中docker-for-windows/ 选中beta/ 下载这个.msi文件 二.安装 1.安装.msi文件,桌面 ...

  10. 学习tp5的第三天(模型)

    一.模型 1.定义基础模型 <?php namespace app\index\model; use think\Model; class User extends Model{ // 设置完整 ...