本文主要讲述cinder scheduler调度服务的调度策略,以及常用到的Filter节点筛选器以及Weigher节点称重器
顺便提一嘴,这里所说的节点,其实就是各个后端,因为在cinder里host其实就对应着配置里enabled_backends的所有后端,所以也可以叫做后端筛选器、后端称重器。
[cinder@node-8 /]$ cinder-manage service list
Binary Host Zone Status State Updated At RPC Version Object Version Cluster
cinder-scheduler cinder-volume-worker nova enabled :-) 2023-08-08 07:02:07 3.12 1.38
cinder-volume cinder-volume-worker@hdd nova enabled :-) 2023-08-08 07:02:11 3.16 1.38
cinder-volume cinder-volume-worker@ssdpool nova enabled :-) 2023-08-08 07:02:10 3.16 1.38
cinder-volume cinder-volume-worker@alcubierre nova enabled :-) 2023-08-08 07:02:10 3.16 1.38
一、常用的调度器
cinder各服务之间大致流程关系如图:
如图,cinder本身就支持多种调度器:
ChanceScheduler: 随机选取cinder-service 创建cinder volume
SimpleScheduler: 根据availability_zone 和 cinder-volume service的capacity进行选择
FilterScheduler: 可以选择具体的filter规则,满足filter规则的cinder-volume service将会通过筛选,创建cinder volume
目前90%都是使用FilterScheduler调度器,且若不手动修改,默认的scheduler_driver就是FilterScheduler
# Default scheduler driver to use (string value)
#scheduler_driver = cinder.scheduler.filter_scheduler.FilterScheduler
二、FilterScheduler调度器简单介绍
FilterScheduler调度器又分为Filter和Weigher两部分。简单来说就是,根据Filter选择出所有满足条件的可用节点,再根据weigher选择最优的节点
2.1 Filter
默认启用的Filter:
- AvailabilityZoneFilter:按可用性区域过滤后端
- CapacityFilter :基于卷后端的容量利用率的容量过滤器
- CapabilitiesFilter:基于volume type中的extra specs(常用volume_backend_name)
本次文档暂时只梳理CapacityWeigher的原理,Filter都很容易理解
2.2 Weigher
默认启用的Weigher:
- CapacityWeigher:可用存储空间最大的节点成为最优节点
CapacityWeigher类源码如下:
class CapacityWeigher(weights.BaseHostWeigher):
def weight_multiplier(self):
"""Override the weight multiplier."""
return CONF.capacity_weight_multiplier
def weigh_objects(self, weighed_obj_list, weight_properties):
"""Override the weigh objects.
This override calls the parent to do the weigh objects and then
replaces any infinite weights with a value that is a multiple of the
delta between the min and max values.
NOTE(jecarey): the infinite weight value is only used when the
smallest value is being favored (negative multiplier). When the
largest weight value is being used a weight of -1 is used instead.
See _weigh_object method.
"""
tmp_weights = super(weights.BaseHostWeigher, self).weigh_objects(
weighed_obj_list, weight_properties)
if math.isinf(self.maxval):
# NOTE(jecarey): if all weights were infinite then parent
# method returns 0 for all of the weights. Thus self.minval
# cannot be infinite at this point
copy_weights = [w for w in tmp_weights if not math.isinf(w)]
self.maxval = max(copy_weights)
offset = (self.maxval - self.minval) * OFFSET_MULT
self.maxval += OFFSET_MIN if offset == 0.0 else offset
tmp_weights = [self.maxval if math.isinf(w) else w
for w in tmp_weights]
return tmp_weights
def _weigh_object(self, host_state, weight_properties):
"""Higher weights win. We want spreading to be the default."""
free_space = host_state.free_capacity_gb
total_space = host_state.total_capacity_gb
if (free_space == 'infinite' or free_space == 'unknown' or
total_space == 'infinite' or total_space == 'unknown'):
# (zhiteng) 'infinite' and 'unknown' are treated the same
# here, for sorting purpose.
# As a partial fix for bug #1350638, 'infinite' and 'unknown' are
# given the lowest weight to discourage driver from report such
# capacity anymore.
free = -1 if CONF.capacity_weight_multiplier > 0 else float('inf')
else:
free = utils.calculate_virtual_free_capacity(
total_space,
free_space,
host_state.provisioned_capacity_gb,
host_state.thin_provisioning_support,
host_state.max_over_subscription_ratio,
host_state.reserved_percentage)
return free
当cinder.conf中capacity_weight_multiplier=1.0时,调用weigh_objects(),为-1.0时直接调用_weigh_object,若这个在cinder里添加后端配置时可以修改,如果不配置则默认1.0
weigh_objects()方法中line 19会调用会调用父类方法,父类源码如下:
def weigh_objects(self, weighed_obj_list, weight_properties):
"""Weigh multiple objects.
Override in a subclass if you need access to all objects in order
to calculate weights. Do not modify the weight of an object here,
just return a list of weights.
"""
# Calculate the weights
weights = []
for obj in weighed_obj_list:
weight = self._weigh_object(obj.obj, weight_properties)
# Record the min and max values if they are None. If they anything
# but none we assume that the weigher has set them
if self.minval is None:
self.minval = weight
if self.maxval is None:
self.maxval = weight
if weight < self.minval:
self.minval = weight
elif weight > self.maxval:
self.maxval = weight
weights.append(weight)
return weights
在此父类weigh_object()中会调用子类CapacityWeigher重写的_weigh_object(),计算出可用容量,然后计算出最大可用容量maxval和最小可用容量minval,并且子类CapacityWeigher中weigh_object()会根据maxval是否为无穷大对最终返回的tmp_weights()进行调整。
具体调整方案如下:
当存在maxval为无穷大时:
当maxval为无穷大时,minval不为无穷大时,将无穷大节点设置为(maxval-minval)*100
当maxval和minval都为无穷大时,将无穷大节点设置为10000
其中CapacityWeigher类中_weigh_object()方法line 49:调用到的计算可用容量的方法calculate_virtual_free_capacity()源代码如下:
def calculate_virtual_free_capacity(total_capacity,
free_capacity,
provisioned_capacity,
thin_provisioning_support,
max_over_subscription_ratio,
reserved_percentage):
"""Calculate the virtual free capacity based on thin provisioning support.
:param total_capacity: total_capacity_gb of a host_state or pool.
:param free_capacity: free_capacity_gb of a host_state or pool.
:param provisioned_capacity: provisioned_capacity_gb of a host_state
or pool.
:param thin_provisioning_support: thin_provisioning_support of
a host_state or a pool.
:param max_over_subscription_ratio: max_over_subscription_ratio of
a host_state or a pool
:param reserved_percentage: reserved_percentage of a host_state or
a pool.
:returns: the calculated virtual free capacity.
"""
total = float(total_capacity)
reserved = float(reserved_percentage) / 100
if thin_provisioning_support:
free = (total * max_over_subscription_ratio
- provisioned_capacity
- math.floor(total * reserved))
else:
# Calculate how much free space is left after taking into
# account the reserved space.
free = free_capacity - math.floor(total * reserved)
return free
经过calculate_virtual_free_capacity()的line 25-33可以知道:
如果后端存储是精简(thin)的, 按照可用容量free=总容量 - 已分配 - 预留,
如果后端存储是厚置备(thick)的,按照可用容量free=总容量 -预留。
这个在对接商业存储时会进行配置,如果不配置则默认为精简
最终会从返回的tmp_weights()各节点的可用容量来选择最优节点