[ English | 日本語 | Deutsch | Indonesia ]
OpenStack Compute (nova) スケジューラーのカスタマイズ¶
Many OpenStack projects allow for customization of specific features using a driver architecture. You can write a driver that conforms to a particular interface and plug it in through configuration. For example, you can easily plug in a new scheduler for Compute. The existing schedulers for Compute are feature full and well documented at Scheduling. However, depending on your user's use cases, the existing schedulers might not meet your requirements. You might need to create a new scheduler.
To create a scheduler, you must inherit from the class
nova.scheduler.driver.Scheduler. Of the five methods that you can
override, you must override the two methods marked with an asterisk
(*) below:
update_service_capabilitieshosts_upgroup_hosts*
schedule_run_instance*
select_destinations
OpenStack のカスタマイズをデモするために、リクエストの送信元IPアドレスとホスト名のプレフィックスに基づいてインスタンスを一部のホストにランダムに配置するような Compute のスケジューラーの例を作成します。この例は、1つのユーザのグループが1つのサブネットにおり、インスタンスをホスト群の中の一部のサブネットで起動したい場合に有用です。
警告
This example is for illustrative purposes only. It should not be used as a scheduler for Compute without further development and testing.
stack.sh が screen -r stack で作成したセッションに join すると、多数の screen ウィンドウが見えます。
0$ shell* 1$ key 2$ horizon ... 9$ n-api ... 14$ n-sch ...
shell作業を行うためのシェル
keykeystone サービス
horizonhorizon dashboard 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 # # http://www.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 を押します。
n-sch画面が表示されるまで Ctrl+A に続けて N を押します。Ctrl+C を押し、サービスを強制停止します。
上矢印キー を押し、最後のコマンドを表示させます。
Enter キーを押し、実行します。
nova の CLI でスケジューラーのテストをしてください。
shellの screen セッションに切り替えてテストを開始し、n-schscreen セッションにもどってログ出力をチェックして終了します。Ctrl+A に続けて 0 を押します。
devstackディレクトリーにいることを確認します。$ cd /root/devstack
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-sch画面に切り替えます。ログ出力の中に、以下の行を見つけられます。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
警告
このような機能試験は、正しいユニットテストと結合テストの代わりになるものではありませんが、作業を開始することはできます。
ドライバ・アーキテクチャーを使う他のプロジェクトで、類似のパターンに従うことができます。単純に、そのドライバーインタフェースに従うモジュールとクラスを作成し、環境定義によって組み込んでください。あなたのコードはその機能が使われた時に実行され、必要に応じて他のサービスを呼び出します。プロジェクトのコアコードは一切修正しません。ドライバーアーキテクチャーを使っているプロジェクトを確認するには、/etc/<project> に格納されている、プロジェクトの .conf 設定ファイルの中で driver 変数を探してください。
When your scheduler is done, we encourage you to open source it and let the community know on the OpenStack mailing list. Perhaps others need the same functionality. They can use your code, provide feedback, and possibly contribute. If enough support exists for it, perhaps you can propose that it be added to the official Compute schedulers.