深挖Openstack Nova - Scheduler排程策略
深挖Openstack Nova - Scheduler排程策略
一. Scheduler的作用就是在建立例項(instance)時,為例項選擇出合適的主機(host)。這個過程分兩步:過濾(Fliter)和計算權值(Weight)
1. 過濾:
過濾掉不符合我們的要求,或映象要求(比如物理節點不支援64bit,物理節點不支援Vmware EXi等)的主機,留下符合過濾演算法的主機集合。
2. 計算權值
通過指定的權值計算演算法,計算在某物理節點上申請這個虛機所必須的消耗cost。物理節點越不適合這個虛機,消耗cost就越大,權值Weight就越大,排程演算法會選擇權值最小的主機。
二. 過濾策略
Filter演算法在nova-scheduler中是通過oslo.config.cfg模組從nova.conf配置檔案中動態獲取的,應用了Python的反射機制,在執行時刻決定初始化所選擇的filter演算法。
OpenStack支援多種過濾策略,均在/nova/scheduler/filters包下:
1. CoreFilter:根據CPU數過濾主機
2. RamFilter:根據指定的RAM值選擇資源足夠的主機
3. AvailabilityZoneFilter:返回建立虛擬機器引數指定的叢集內的主機
4. JsonFilter:根據JSON串指定的規則選擇主機
三. 目錄結構
1. /nova/scheduler/filter_scheduler.py:繼承於類Scheduler,實現基於主機過濾器選取主機節點方式的排程器
2. /nova/scheduler/host_manager.py: 描述了跟排程器操作相關的主機的實現,其中,HostState類描述了從主機獲取相關資料和狀態的一些實現,HostManager類描述了跟排程器操作相關的一些主機管理實現
3. /nova/weights.py:實現了跟計算權值相關的方法
四. 分析排程_schedule方法
該方法對應在/nova/scheduler/filter_scheduler.py中
- # 排程方法,返回一系列滿足要求的主機(host)
- def _schedule(self, context, request_spec, filter_properties)
1. 資訊初始化
- # 返回帶有admin標誌設定的context的版本
- elevated = context.elevated()
- # 獲取例項資訊
- instance_properties = request_spec[ 'instance_properties']
2. 更新過濾器屬性資訊
- filter_properties.update({ 'context': context,
- 'request_spec': request_spec,
- 'config_options': config_options,
- 'instance_type': instance_type})
3. 過濾不可用的host
- # 過濾掉不可用的主機節點
- hosts = self._get_all_host_states(elevated)
深入_get_all_host_states方法,對應的是/nova/scheduler/host_manager.py。
(1)獲取可用的計算節點
- # 獲取可用計算節點的資源使用情況
- # 獲取所有compute_node(計算節點)
- compute_nodes = objects.ComputeNodeList.get_all(context)
(2)設定基本資訊
- # 獲取主機host
- host = compute.host
- # 獲取hypervisor_hostname作為節點名
- node = compute.hypervisor_hostname
- state_key = (host, node)
- # 從host_state_map獲取並更新host狀態
- host_state = self.host_state_map.get(state_key)
- if host_state:
- host_state.update_from_compute_node(compute)
- else:
- host_state = self.host_state_cls(host, node, compute=compute)
- self.host_state_map[state_key] = host_state
(3)更新host狀態
- # 每次請求到來都要更新host狀態
- host_state.aggregates = [self.aggs_by_id[agg_id] for agg_id in
- self.host_aggregates_map[
- host_state.host]]
- host_state.update_service(dict(service))
- self._add_instance_info(context, compute, host_state)
- seen_nodes.add(state_key)
(4)刪除不活躍的計算節點
- # 從host_state_map中刪除不活躍的計算節點
- dead_nodes = set(self.host_state_map.keys()) - seen_nodes
- for state_key in dead_nodes:
- host, node = state_key
- LOG.info(_LI( "Removing dead compute node %(host)s:%(node)s "
- "from scheduler"), {'host':host, 'node': node})
- del self.host_state_map[state_key]
4.迴圈遍歷例項,獲取符合過濾要求的host
- for num in range(num_instances):
- # 基於具體要求過濾本地主機
- hosts = self.host_manager.get_filtered_hosts(hosts,
- filter_properties, index=num)
- # 一個符合要求的host都沒有
- if not hosts:
- break
深入get_filtered_hosts方法,對應的是/nova/scheduler/host_manager.py。
(1)定義所要使用的過濾器
- # 如果沒有設定過濾器,則使用預設的過濾器
- if filter_class_names is None:
- filters = self.default_filters
- else:
- # 獲取過濾器方法
- filters = self._choose_host_filters(filter_class_names)
(2)然後處理三種類型的host
1》忽略的host
ignore_hosts = filter_properties.get('ignore_hosts', [])
- # 除去忽略的host
- def _strip_ignore_hosts(host_map, hosts_to_ignore):
2》強制使用的host
force_hosts = filter_properties.get('force_hosts', [])
- # 匹配強制使用的host
- def _match_forced_hosts(host_map, hosts_to_force):
3》強制使用的nodes
force_nodes = filter_properties.get('force_nodes', [])
- # 匹配強制使用的nodes
- def _match_forced_nodes(host_map, nodes_to_force):
- # 執行過濾操作,返回滿足所有過濾條件的host物件
- return self.filter_handler.get_filtered_objects(filters,
- hosts, filter_properties, index)
5. 對主機進行稱重
- # 獲取並返回一個WeightedObjects的主機排序列表(最高分排在第一)
- weighted_hosts = self.host_manager.get_weighted_hosts(hosts,
- filter_properties)
深入get_weighted_hosts方法,最終對應的是/nova/weights.py。
(1)用相乘累加的方式計算host主機的權重
- # 根據多方面引數來判定權值,比如主機剩餘記憶體、剩餘磁碟空間、vcpu的使用情況
- # 每個引數乘於一個weight,累加得到host主機的權值
- for i, weight in enumerate(weights):
- obj = weighted_objs[i]
- obj.weight += weigher.weight_multiplier() * weight
(2)將獲取權值的host主機排序後返回
- # 對WeighedObjects列表進行排序返回
- return sorted(weighed_objs, key=lambda x: x.weight, reverse=True)
開發者也可以實現自己的權值計算函式,對於OpenStack採用的方法來說,主機擁有的剩餘記憶體越多,權值越小,被選擇在其上建立虛擬機器的可能性就越大。
6. 設定排程使用的主機數目
- # scheduler_host_subset_size:定義了新的例項將會被排程到一個主機上
- # 這個主機是隨機從最好的(分數最高的)N個主機組成的子集中選擇出來
- scheduler_host_subset_size = CONF.scheduler_host_subset_size
- if scheduler_host_subset_size > len(weighed_hosts):
- scheduler_host_subset_size = len(weighed_hosts)
- if scheduler_host_subset_size < 1:
- scheduler_host_subset_size = 1
7. 獲取隨機選擇出來的主機
- # 從分數最高的若干主機組成的子集中,隨機選擇一個主機
- # 新的例項將會排程到這個主機上
- chosen_host = random.choice(
- weighed_hosts[ 0:scheduler_host_subset_size])
- LOG.debug( "Selected host: %(host)s", {'host': chosen_host})
- # 把選好的主機增加到selected_hosts列表中
- selected_hosts.append(chosen_host)
8. 為下一次例項選擇主機做好準備
- # 此次選擇了一個主機後,在下一個例項選擇主機前,更新主機資源資訊
- chosen_host.obj.consume_from_instance(instance_properties)
- if update_group_hosts is True:
- if isinstance(filter_properties['group_hosts'], list):
- filter_properties['group_hosts'] = set(
- filter_properties['group_hosts'])
- filter_properties['group_hosts'].add(chosen_host.obj.host)
9. 返回所有例項選擇的主機列表
- # 迴圈為每一個例項獲取合適的主機後,返回選擇的主機列表
- return selected_hosts
-