scheduler的调度流程
注: 源码可以从~/nava/scheduler/managert.py (select_destinations)开始
scheduler从接受到rpc调用开始,所经历的流程大概如下图所示:
- 对rpc传过来的request_spec参数进行优化(这一步是S版后才有的,为了配合nova-api表中的聚合关系,进行预先的筛选,减少后边查找的范围)
if self.driver.USES_ALLOCATION_CANDIDATES and not is_rebuild:
# Only process the Placement request spec filters when Placement
# is used.
try:
request_filter.process_reqspec(ctxt, spec_obj)
except exception.RequestFilterFailed as e:
raise exception.NoValidHost(reason=e.message)
def process_reqspec(ctxt, request_spec):
"""Process an objects.ReqestSpec before calling placement.
:param ctxt: A RequestContext
:param request_spec: An objects.RequestSpec to be inspected/modified
"""
for filter in ALL_REQUEST_FILTERS:
filter(ctxt, request_spec)
目前包含的4个filter为:
ALL_REQUEST_FILTERS = [
require_tenant_aggregate,
map_az_to_placement_aggregate,
require_image_type_support,
compute_status_filter,
]
- 将request_spec翻译为placement所需的查询格式并查询符合的资源集合
resources = utils.resources_from_request_spec(
ctxt, spec_obj, self.driver.host_manager)
res = self.placement_client.get_allocation_candidates(ctxt,resources)
- 根据资源集合去查找相应的hoststate(查询的表为:compute_node,services,instance,cell_mapping)
selections = self.driver.select_destinations(ctxt, spec_obj,
instance_uuids, alloc_reqs_by_rp_uuid, provider_summaries,
allocation_request_version, return_alternates)
从这一步开始进入到/nova/scheduler/filter_cheduler.py的select_destinations方法
elevated = context.elevated()
hosts = self._get_all_host_states(elevated, spec_obj,
provider_summaries)
- 对hoststate进行过滤和打分,获得符合条件并排好序的hoststate
for num, instance_uuid in enumerate(instance_uuids):
# In a multi-create request, the first request spec from the list
# is passed to the scheduler and that request spec's instance_uuid
# might not be the same as the instance we're processing, so we
# update the instance_uuid in that case before passing the request
# spec to filters since at least one filter
# (ServerGroupAntiAffinityFilter) depends on that information being
# accurate.
spec_obj.instance_uuid = instance_uuid
# Reset the field so it's not persisted accidentally.
spec_obj.obj_reset_changes(['instance_uuid'])
hosts = self._get_sorted_hosts(spec_obj, hosts, num)
这里的instance_uuids是rpc传过来的,相当于创建几个虚机就有几个instance_uuid
并且这里获得的是一个集合,最优的在第一个
5.对hoststate从最优开始进行claim操作,如果成功则跳出,更新hoststate并继续下一个虚拟机的创建(回到第4步重新排序)这里的claim操作只是对placement中的资源进行校验和更新
claimed_host = None
for host in hosts:
cn_uuid = host.uuid
if cn_uuid not in alloc_reqs_by_rp_uuid:
msg = ("A host state with uuid = '%s' that did not have a "
"matching allocation_request was encountered while "
"scheduling. This host was skipped.")
LOG.debug(msg, cn_uuid)
continue
alloc_reqs = alloc_reqs_by_rp_uuid[cn_uuid]
alloc_req = alloc_reqs[0]
if utils.claim_resources(elevated, self.placement_client,
spec_obj, instance_uuid, alloc_req,
allocation_request_version=allocation_request_version):
claimed_host = host
break
if claimed_host is None:
LOG.debug("Unable to successfully claim against any host.")
break
claimed_instance_uuids.append(instance_uuid)
claimed_hosts.append(claimed_host)
# Now consume the resources so the filter/weights will change for
# the next instance.
self._consume_selected_host(claimed_host, spec_obj,
instance_uuid=instance_uuid)
6.验证claim成功的资源数和要求的数量是否相同,不同则回滚对placement的修改,并抛出novalidhost异常
self._ensure_sufficient_hosts(context, claimed_hosts, num_instances,
claimed_instance_uuids)
7.返回选中的selection信息集合
selections_to_return = self._get_alternate_hosts(
claimed_hosts, spec_obj, hosts, num, num_alts,
alloc_reqs_by_rp_uuid, allocation_request_version)
return selections_to_return
selecttion所包含的字段:
fields = {
"compute_node_uuid": fields.UUIDField(),
"service_host": fields.StringField(),
"nodename": fields.StringField(),
"cell_uuid": fields.UUIDField(),
"limits": fields.ObjectField("SchedulerLimits", nullable=True),
# An allocation_request is a non-trivial dict, and so it will be stored
# as an encoded string.
"allocation_request": fields.StringField(nullable=True),
"allocation_request_version": fields.StringField(nullable=True),
}