以nova kilo版本為例
nova調度過程分析
之前創建虛擬機流程分析提到過:
#nova/scheduler/manager.py
class SchedulerManager(manager.Manager):
@messaging.expected_exceptions(exception.NoValidHost)
def select_destinations(self, context, request_spec, filter_properties):
'''driver其實就是調度算法實現,由配置文件決定,通常用的比較多的就是filter_scheduler,
對應filter_scheduler.py模塊,該模塊首先通過host_manager拿到所有的計算節點信息,
然后通過filters過濾掉不滿足條件的計算節點,剩下的節點通過weigh方法計算權值,
最后選擇權值高的作為候選計算節點返回。nova-scheduler進程結束。'''
dests = self.driver.select_destinations(context, request_spec,
filter_properties)
return jsonutils.to_primitive(dests)
driver在SchedulerManager 的init方法中定義
class SchedulerManager(manager.Manager):
def __init__(self, scheduler_driver=None, *args, **kwargs):
if not scheduler_driver:
scheduler_driver = CONF.scheduler_driver
#driver 是通過配置文件中的選項值指定的類來返回的對象
self.driver = importutils.import_object(scheduler_driver)
在nova的配置文件中:
scheduler_driver=nova.scheduler.filter_scheduler.FilterScheduler
繼續往下分析:
#nova/scheduler/filter_scheduler.py
class FilterScheduler(driver.Scheduler):
def select_destinations(self, context, request_spec, filter_properties):
...
# 需要創建的 Instances 的數量
num_instances = request_spec['num_instances']
# 獲取滿足笫一次過濾條件的主機列表 List
selected_hosts = self._schedule(context, request_spec,
filter_properties)
# 當請求的 Instance 數量大于合適的主機數量時,不會創建 Instance 且輸出錯誤信息
if len(selected_hosts) < num_instances and\
len(selected_hosts) >= min_instances:
...
elif len(selected_hosts) < min_instances:
...
dests = [dict(host=host.obj.host, nodename=host.obj.nodename,
limits=host.obj.limits) for host in selected_hosts]
...
return dests
def _schedule(self, context, request_spec, filter_properties):
...
#獲取所有Hosts 狀態,主要用來去除不活躍的節點
hosts = self._get_all_host_states(elevated)
#調用HostManager的get_filtered_hosts方法來選擇可用的計算節點
hosts = self.host_manager.get_filtered_hosts(hosts,
filter_properties, index=num, context=context)
...
#通過 Weighed 選取最優 Host
weighed_hosts = self.host_manager.get_weighed_hosts(hosts,
filter_properties, context=context)
...
return selected_hosts
再來看看get_filtered_hosts的調用過程:
#nova/scheduler/host_manager.py
class HostManager(object):
def get_filtered_hosts(self, hosts, filter_properties,
filter_class_names=None, index=0, context=None):
...
if filter_class_names is None:
filters = self.default_filters
else:
filters = self._choose_host_filters(filter_class_names)
...
return self.filter_handler.get_filtered_objects(filters,
hosts, filter_properties, index, context=context)
def _choose_host_filters(self, filter_cls_names):
...
#將filter_cls_names封裝成列表
if not isinstance(filter_cls_names, (list, tuple)):
filter_cls_names = [filter_cls_names]
good_filters = []
bad_filters = []
for filter_name in filter_cls_names:
if filter_name not in self.filter_obj_map:
if filter_name not in self.filter_cls_map:
bad_filters.append(filter_name)
continue
filter_cls = self.filter_cls_map[filter_name]
self.filter_obj_map[filter_name] = filter_cls()
good_filters.append(self.filter_obj_map[filter_name])
if bad_filters:
msg = ", ".join(bad_filters)
raise exception.SchedulerHostFilterNotFound(filter_name=msg)
return good_filters
def __init__(self):
self.filter_handler = filters.HostFilterHandler()
#__init__方法中調用get_matching_classes方法去加載nova.conf配置文件的scheduler_available_filters屬性設置的fileter
filter_classes = self.filter_handler.get_matching_classes(
CONF.scheduler_available_filters)
self.filter_cls_map = {cls.__name__: cls for cls in filter_classes}
self.default_filters = self._choose_host_filters(self._load_filters())
def _load_filters(self):
return CONF.scheduler_default_filters
nova.conf中的相關配置如下:
scheduler_available_filters=nova.scheduler.filters.all_filters
scheduler_default_filters=RetryFilter,AvailabilityZoneFilter,RamFilter,ComputeFilter,ComputeCapabilitiesFilter,ImagePropertiesFilter
總結一下_choose_host_filters的工作流程:
- 將filter_cls_names封裝成列表
- 依次檢查filter_cls_names中的所有可用的filter列表(個人理解,其檢查過程就是判斷scheduler_default_filters定義的filter能不能在nova/filter/下找到相關類)
- 返回可用filter列表
添加自定義filter
自定義一個filter類,specified_host_filter.py:
from oslo_log import log as logging
from nova.scheduler import filters
LOG = logging.getLogger(__name__)
#任何filter類必須繼承filters.BaseHostFilter類
class SpecifiedHostFilter(filters.BaseHostFilter):
def __init__(self):
#通知成功加載SpecifiedHostFilter類
LOG.info("SpecifiedHostFilter is initialized!")
#host_passes是每個類必須實現的方法,host_state保存了詢問的計算節點的信息
#filter_properties保存了一些幫助nvoa scheduler完成調度的信息
def host_passes(slef, host_state, filter_properties):
#獲取客戶端要求的計算節點主機名
scheduler_hints = filter_properties.get('scheduler_hints',{})
requested_host = scheduler_hints.get('requested_host',None)
#如果客戶提供了要求的計算節點,則檢查當前計算節點與客戶要求的節點是否匹配
if requested_host:
return requested_host == host_state.host
#如果客戶沒有提供要求的計算節點,則返回真
return True
將自定義filter放在nova目錄下,形成下面的目錄結構:
├── scheduler
├── myproject
│ ├── __init__.py
│ ├── specified_host_filter.py
修改nova.conf配置文件,
scheduler_available_filters=nova.scheduler.filters.all_filters
scheduler_available_filters=nova.myproject.specified_host_filter.SpecifiedHostFilter
scheduler_default_filters=RetryFilter,AvailabilityZoneFilter,RamFilter,ComputeFilter,ComputeCapabilitiesFilter,ImagePropertiesFilter,SpecifiedHostFilter
重啟nova-scheduler服務
客戶端測試
使用rdo快速搭建openstack-allinone環境,詳情見官網
在specified_host_filter.py中打斷點后,停止nova-scheduler服務,手動啟動scheduler服務:
/usr/bin/python /usr/bin/nova-scheduler --config-file /etc/nova/nova.conf --logfile /var/log/nova/nova-scheduler.log
創建一臺虛擬機:
openstack server create --flavor m1.tiny --image cirros --nic net-id=4eace7c7-ec56-4e12-9a05-fc70a0887220 --security-group default ----hint requested_host=no-such-host test
執行到斷點處:
(Pdb) l
8 def __init__(self):
9 LOG.info("SpecifiedHostFilter is initialized!")
10
11 def host_passes(slef, host_state, filter_properties):
12 import pdb; pdb.set_trace()
13 -> scheduler_hints = filter_properties.get('scheduler_hints',{})
14 requested_host = scheduler_hints.get('requested_host',None)
15 if requested_host:
16 return requested_host ==host_state.host
17 return True
[EOF]
Pdb) n
-> requested_host = scheduler_hints.get('requested_host',None)
(Pdb) p scheduler_hints
{u'requested_host': u'no-such-host'}
(Pdb) n
-> if requested_host:
(Pdb) n
-> return requested_host ==host_state.host
(Pdb) p requested_host
u'no-such-host'
(Pdb) p host_state.host
u'openstack'
因為我們傳的'no-such-host'和可用的主機'openstack'不相等,所以日志中看到這樣的消息:
2017-05-02 18:19:42.417 13453 INFO nova.myproject.specified_host_filter [-] SpecifiedHostFilter is initialized!
2017-05-02 18:21:54.628 13453 INFO nova.filters [req-d120d748-eb65-4b86-a5ed-22673ea76a52 00ce1cf60c3a4df2842437b5c23d35f6 67ee8dfa658e43b2b1a3d8108f624a95 - - -] Filter SpecifiedHostFilter returned 0 hosts
如果我們不傳--hint參數 或者----hint requested_host=openstack ,虛擬機創建以后狀態為Active