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),
}