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.
スケジューラーを作成するには、 nova.scheduler.driver.Scheduler
クラスを継承しなければなりません。オーバーライド可能な 5 つのメソッドのうち、以下のアスタリスク (*) で示される 2 つのメソッドはオーバーライド しなければなりません 。
update_service_capabilities
hosts_up
group_hosts
schedule_run_instance
select_destinations
OpenStack のカスタマイズをデモするために、リクエストの送信元IPアドレスとホスト名のプレフィックスに基づいてインスタンスを一部のホストにランダムに配置するような Compute のスケジューラーの例を作成します。この例は、1つのユーザのグループが1つのサブネットにおり、インスタンスをホスト群の中の一部のサブネットで起動したい場合に有用です。
警告
この例は実証目的のみのためにあります。さらなる作りこみとテストなしで、Compute のスケジューラーとして使用するべきではありません。
stack.sh
が screen -r stack
で作成したセッションに join すると、多数の screen ウィンドウが見えます。
0$ shell* 1$ key 2$ horizon ... 9$ n-api ... 14$ n-sch ...
shell
key
horizon
n-{name}
n-sch
スケジューラーを作成して、設定を通して組み込む方法
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 セッションに切り替えてはじめてください。
n-sch
画面が表示されるまで Ctrl+A に続けて N を押します。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 変数を探してください。
あなたのスケジューラーが完成したら、オープンソースにし、OpenStack メーリングリストでコミュニティに知らせることをお薦めします。もしかしたら他の人も同じ機能を必要としているかもしれません。彼らはあなたのコードを使い、フィードバックし、おそらくコントリビュートするでしょう。もし十分な支持があれば、もしかしたら公式な Compute スケジューラー への追加を提案してもよいでしょう。
Except where otherwise noted, this document is licensed under Creative Commons Attribution 3.0 License. See all OpenStack Legal Documents.