1. 程式人生 > >深挖Openstack Nova - Scheduler排程策略

深挖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中

  1.   # 排程方法,返回一系列滿足要求的主機(host)
  2.   def _schedule(self, context, request_spec, filter_properties)

1. 資訊初始化

  1.   # 返回帶有admin標誌設定的context的版本
  2.   elevated = context.elevated()
  3.   # 獲取例項資訊
  4.   instance_properties = request_spec[ 'instance_properties']

 

2. 更新過濾器屬性資訊

  1.   filter_properties.update({ 'context': context,
  2.   'request_spec': request_spec,
  3.                              'config_options': config_options,
  4.                            'instance_type': instance_type})

 

3. 過濾不可用的host

  1.   # 過濾掉不可用的主機節點
  2.   hosts = self._get_all_host_states(elevated)

深入_get_all_host_states方法,對應的是/nova/scheduler/host_manager.py。

(1)獲取可用的計算節點

  1.   # 獲取可用計算節點的資源使用情況
  2.   # 獲取所有compute_node(計算節點)
  3.   compute_nodes = objects.ComputeNodeList.get_all(context)

(2)設定基本資訊

  1.   # 獲取主機host
  2.   host = compute.host
  3.   # 獲取hypervisor_hostname作為節點名
  4.   node = compute.hypervisor_hostname
  5.   state_key = (host, node)
  6.   # 從host_state_map獲取並更新host狀態
  7.   host_state = self.host_state_map.get(state_key)
  8.   if host_state:
  9.       host_state.update_from_compute_node(compute)
  10.   else:
  11.       host_state = self.host_state_cls(host, node, compute=compute)
  12.       self.host_state_map[state_key] = host_state

(3)更新host狀態

  1.   # 每次請求到來都要更新host狀態
  2.   host_state.aggregates = [self.aggs_by_id[agg_id] for agg_id in
  3.                            self.host_aggregates_map[
  4.                               host_state.host]]
  5.   host_state.update_service(dict(service))
  6.   self._add_instance_info(context, compute, host_state)
  7.   seen_nodes.add(state_key)

(4)刪除不活躍的計算節點

  1.   # 從host_state_map中刪除不活躍的計算節點
  2.   dead_nodes = set(self.host_state_map.keys()) - seen_nodes
  3.   for state_key in dead_nodes:
  4.       host, node = state_key
  5.       LOG.info(_LI( "Removing dead compute node %(host)s:%(node)s "
  6.                    "from scheduler"), {'host':host, 'node': node})
  7.        del self.host_state_map[state_key]

 

4.迴圈遍歷例項,獲取符合過濾要求的host

  1.   for num in range(num_instances):
  2.        # 基於具體要求過濾本地主機
  3.       hosts = self.host_manager.get_filtered_hosts(hosts,
  4.               filter_properties, index=num)
  5.        # 一個符合要求的host都沒有
  6.        if not hosts:
  7.            break

深入get_filtered_hosts方法,對應的是/nova/scheduler/host_manager.py。

(1)定義所要使用的過濾器

  1.   # 如果沒有設定過濾器,則使用預設的過濾器
  2.   if filter_class_names is None:
  3.       filters = self.default_filters
  4.   else:
  5.        # 獲取過濾器方法
  6.       filters = self._choose_host_filters(filter_class_names)

(2)然後處理三種類型的host

1》忽略的host

ignore_hosts = filter_properties.get('ignore_hosts', [])
  1.   # 除去忽略的host
  2.   def _strip_ignore_hosts(host_map, hosts_to_ignore):

2》強制使用的host

force_hosts = filter_properties.get('force_hosts', [])
  1.   # 匹配強制使用的host
  2.   def _match_forced_hosts(host_map, hosts_to_force):

3》強制使用的nodes

force_nodes = filter_properties.get('force_nodes', [])
  1.   # 匹配強制使用的nodes
  2.   def _match_forced_nodes(host_map, nodes_to_force):
(3)返回滿足過濾條件的host物件
  1.   # 執行過濾操作,返回滿足所有過濾條件的host物件
  2.   return self.filter_handler.get_filtered_objects(filters,
  3.           hosts, filter_properties, index)

 

5. 對主機進行稱重

  1.   # 獲取並返回一個WeightedObjects的主機排序列表(最高分排在第一)
  2.   weighted_hosts = self.host_manager.get_weighted_hosts(hosts,
  3.           filter_properties)

深入get_weighted_hosts方法,最終對應的是/nova/weights.py。

(1)用相乘累加的方式計算host主機的權重

  1.   # 根據多方面引數來判定權值,比如主機剩餘記憶體、剩餘磁碟空間、vcpu的使用情況
  2.   # 每個引數乘於一個weight,累加得到host主機的權值
  3.   for i, weight in enumerate(weights):
  4.       obj = weighted_objs[i]
  5.       obj.weight += weigher.weight_multiplier() * weight

(2)將獲取權值的host主機排序後返回

  1.   # 對WeighedObjects列表進行排序返回
  2.   return sorted(weighed_objs, key=lambda x: x.weight, reverse=True)

 

開發者也可以實現自己的權值計算函式,對於OpenStack採用的方法來說,主機擁有的剩餘記憶體越多,權值越小,被選擇在其上建立虛擬機器的可能性就越大。

 

6. 設定排程使用的主機數目

  1.   # scheduler_host_subset_size:定義了新的例項將會被排程到一個主機上
  2.   # 這個主機是隨機從最好的(分數最高的)N個主機組成的子集中選擇出來
  3.   scheduler_host_subset_size = CONF.scheduler_host_subset_size
  4.   if scheduler_host_subset_size > len(weighed_hosts):
  5.       scheduler_host_subset_size = len(weighed_hosts)
  6.   if scheduler_host_subset_size < 1:
  7.       scheduler_host_subset_size = 1

 

7. 獲取隨機選擇出來的主機

  1.   # 從分數最高的若干主機組成的子集中,隨機選擇一個主機
  2.   # 新的例項將會排程到這個主機上
  3.   chosen_host = random.choice(
  4.       weighed_hosts[ 0:scheduler_host_subset_size])
  5.   LOG.debug( "Selected host: %(host)s", {'host': chosen_host})
  6.   # 把選好的主機增加到selected_hosts列表中
  7.   selected_hosts.append(chosen_host)

 

8. 為下一次例項選擇主機做好準備

  1.   # 此次選擇了一個主機後,在下一個例項選擇主機前,更新主機資源資訊
  2.   chosen_host.obj.consume_from_instance(instance_properties)
  3.   if update_group_hosts is True:
  4.       if isinstance(filter_properties['group_hosts'], list):
  5.           filter_properties['group_hosts'] = set(
  6.               filter_properties['group_hosts'])
  7.       filter_properties['group_hosts'].add(chosen_host.obj.host)

 

9. 返回所有例項選擇的主機列表

  1.   # 迴圈為每一個例項獲取合適的主機後,返回選擇的主機列表
  2.   return selected_hosts
  3.