[ English | Indonesia | Deutsch | 日本語 ]
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_capabilities
hosts_up
group_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
作業を行うためのシェル
key
keystone サービス
horizon
horizon dashboard Web アプリケーション
n-{name}
nova サービス
n-sch
nova スケジューラーサービス
スケジューラーを作成して、設定を通して組み込む方法
OpenStack のコードは
/opt/stack
にあるので、nova
ディレクトリに移動してあなたのスケジューラーモジュールを編集します。nova
をインストールしたディレクトリーに移動します。$ cd /opt/stack/nova
ip_scheduler.py
Python ソースコードファイルを作成します。$ 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-sch
screen セッションに切り替えてはじめてください。Ctrl+A に続けて 9 を押します。
n-sch
画面が表示されるまで Ctrl+A に続けて N を押します。Ctrl+C を押し、サービスを強制停止します。
上矢印キー を押し、最後のコマンドを表示させます。
Enter キーを押し、実行します。
nova の CLI でスケジューラーのテストをしてください。
shell
の screen セッションに切り替えてテストを開始し、n-sch
screen セッションにもどってログ出力をチェックして終了します。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.