[ 英语 | 日本語 | Deutsch | Indonesia ]
定制 OpenStack 计算 (nova) 调度器¶
许多 OpenStack 项目允许使用驱动程序架构定制特定功能。您可以编写符合特定接口的驱动程序,并通过配置将其插入。例如,您可以轻松地为计算插入新的调度器。计算的现有调度器功能齐全,并在 调度 中有充分的文档记录。但是,根据您的用户用例,现有的调度器可能无法满足您的要求。您可能需要创建一个新的调度器。
要创建调度器,您必须从类 nova.scheduler.driver.Scheduler 继承。在您可以重写五个方法中,您必须重写以下标有星号 (*) 的两个方法
update_service_capabilitieshosts_upgroup_hosts*
schedule_run_instance*
select_destinations
为了演示 OpenStack 的定制,我们将创建一个计算调度器的示例,该调度器根据请求的源 IP 地址和主机名的前缀,将实例随机放置在主机的一个子集上。当您有一组位于子网上的用户,并且希望他们的所有实例都启动在您主机的一个子集内时,这样的示例可能很有用。
警告
此示例仅用于说明目的。在进一步开发和测试之前,不应将其用作计算的调度器。
当您加入 stack.sh 使用 screen -r stack 启动的 screen 会话时,您会看到许多 screen 窗口
0$ shell* 1$ key 2$ horizon ... 9$ n-api ... 14$ n-sch ...
shell一个您可以完成一些工作的 shell
keykeystone 服务
horizonhorizon 仪表板 Web 应用程序
n-{name}nova 服务
n-schnova 调度器服务
要创建调度器并通过配置将其插入
OpenStack 的代码位于
/opt/stack中,因此转到nova目录并编辑您的调度器模块。更改到nova安装的目录$ cd /opt/stack/nova
创建
ip_scheduler.pyPython 源代码文件$ vim nova/scheduler/ip_scheduler.py
下面显示的代码是一个驱动程序,它将根据 IP 地址调度服务器到主机,如本节开头所述。将代码复制到
ip_scheduler.py中。完成后,保存并关闭文件。# vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright (c) 2014 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # https://apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ IP Scheduler implementation """ import random from oslo_config import cfg from nova.compute import rpcapi as compute_rpcapi from nova import exception from nova.openstack.common import log as logging from nova.openstack.common.gettextutils import _ from nova.scheduler import driver CONF = cfg.CONF CONF.import_opt('compute_topic', 'nova.compute.rpcapi') LOG = logging.getLogger(__name__) class IPScheduler(driver.Scheduler): """ Implements Scheduler as a random node selector based on IP address and hostname prefix. """ def __init__(self, *args, **kwargs): super(IPScheduler, self).__init__(*args, **kwargs) self.compute_rpcapi = compute_rpcapi.ComputeAPI() def _filter_hosts(self, request_spec, hosts, filter_properties, hostname_prefix): """Filter a list of hosts based on hostname prefix.""" hosts = [host for host in hosts if host.startswith(hostname_prefix)] return hosts def _schedule(self, context, topic, request_spec, filter_properties): """Picks a host that is up at random.""" elevated = context.elevated() hosts = self.hosts_up(elevated, topic) if not hosts: msg = _("Is the appropriate service running?") raise exception.NoValidHost(reason=msg) remote_ip = context.remote_address if remote_ip.startswith('10.1'): hostname_prefix = 'doc' elif remote_ip.startswith('10.2'): hostname_prefix = 'ops' else: hostname_prefix = 'dev' hosts = self._filter_hosts(request_spec, hosts, filter_properties, hostname_prefix) if not hosts: msg = _("Could not find another compute") raise exception.NoValidHost(reason=msg) host = random.choice(hosts) LOG.debug("Request from %(remote_ip)s scheduled to %(host)s" % locals()) return host def select_destinations(self, context, request_spec, filter_properties): """Selects random destinations.""" num_instances = request_spec['num_instances'] # NOTE(timello): Returns a list of dicts with 'host', 'nodename' and # 'limits' as keys for compatibility with filter_scheduler. dests = [] for i in range(num_instances): host = self._schedule(context, CONF.compute_topic, request_spec, filter_properties) host_state = dict(host=host, nodename=None, limits=None) dests.append(host_state) if len(dests) < num_instances: raise exception.NoValidHost(reason='') return dests def schedule_run_instance(self, context, request_spec, admin_password, injected_files, requested_networks, is_first_time, filter_properties, legacy_bdm_in_spec): """Create and run an instance or instances.""" instance_uuids = request_spec.get('instance_uuids') for num, instance_uuid in enumerate(instance_uuids): request_spec['instance_properties']['launch_index'] = num try: host = self._schedule(context, CONF.compute_topic, request_spec, filter_properties) updated_instance = driver.instance_update_db(context, instance_uuid) self.compute_rpcapi.run_instance(context, instance=updated_instance, host=host, requested_networks=requested_networks, injected_files=injected_files, admin_password=admin_password, is_first_time=is_first_time, request_spec=request_spec, filter_properties=filter_properties, legacy_bdm_in_spec=legacy_bdm_in_spec) except Exception as ex: # NOTE(vish): we don't reraise the exception here to make sure # that all instances in the request get set to # error properly driver.handle_schedule_error(context, ex, instance_uuid, request_spec)
在
context、request_spec和filter_properties中有很多有用的信息,您可以使用它们来决定在哪里调度实例。要了解更多有关可用属性的信息,您可以将以下日志语句插入调度器的schedule_run_instance方法中LOG.debug("context = %(context)s" % {'context': context.__dict__}) LOG.debug("request_spec = %(request_spec)s" % locals()) LOG.debug("filter_properties = %(filter_properties)s" % locals())
要将此调度器插入 nova,请编辑一个配置文件,
/etc/nova/nova.conf$ vim /etc/nova/nova.conf
找到
scheduler_driver配置并像这样更改它scheduler_driver=nova.scheduler.ip_scheduler.IPScheduler
重新启动 nova 调度器服务,以使 nova 使用您的调度器。首先切换到
n-schscreen按下 Ctrl+A,然后按下 9。
按下 Ctrl+A,然后按下 N,直到到达
n-schscreen。按下 Ctrl+C 以终止该服务。
按下 Up Arrow 以调出上一个命令。
按下 Enter 以运行它。
使用 nova CLI 测试您的调度器。首先切换到
shellscreen,最后切换回n-schscreen 以检查日志输出按下 Ctrl+A,然后按下 0。
确保您位于
devstack目录中$ cd /root/devstack
source
openrc以设置 CLI 的环境变量$ . openrc
将唯一已安装的图像的图像 ID 放入环境变量中
$ IMAGE_ID=`openstack image list | egrep cirros | egrep -v "kernel|ramdisk" | awk '{print $2}'`
启动测试服务器
$ openstack server create --flavor 1 --image $IMAGE_ID scheduler-test
切换回
n-schscreen。在日志语句中,您将看到以下行2014-01-23 19:57:47.262 DEBUG nova.scheduler.ip_scheduler [req-... demo demo] Request from xx.xx.xx.xx scheduled to devstack-havana _schedule /opt/stack/nova/nova/scheduler/ip_scheduler.py:76
警告
像这样的功能测试不能替代适当的单元和集成测试,但它可以帮助您入门。
可以使用相同的模式在其他使用驱动程序架构的项目中进行操作。只需创建一个符合驱动程序接口的模块和类,并通过配置将其插入。当使用该功能时,您的代码会运行并可以根据需要调用其他服务。不会触碰项目核心代码。在项目的 .conf 配置文件中查找“driver”值,位于 /etc/<project> 中,以识别使用驱动程序架构的项目。
当您的调度器完成后,我们鼓励您将其开源并在 OpenStack 邮件列表中告知社区。也许其他人需要相同的功能。他们可以使用您的代码、提供反馈并可能做出贡献。如果存在足够的支持,也许您可以建议将其添加到官方计算 调度器 中。