OpenStack はあなたが必要とするすべてのことをしてくれるわけではないかもしれません。この場合、主に2つのやり方のうちのいずれかに従ってください。まず最初に、貢献するには (https://wiki.openstack.org/wiki/How_To_Contribute) を学び、Code Review Workflow (https://wiki.openstack.org/wiki/GerritWorkflow) に従って、あなたの修正を上流の OpenStack プロジェクトへコントリビュートしてください。もし、あなたが必要な機能が既存のプロジェクトと密にインテグレーションする必要がある場合、これが推奨される選択肢です。コミュニティは、いつでも貢献に対して開かれていますし、機能開発ガイドラインに従う新機能を歓迎します。
代替え案としては、もしあなたが必要とする機能が密なインテグレーションを必要としないのであれば、OpenStack をカスタマイズする他の方法があります。もし、あなたの機能が必要とされるプロジェクトが Python Paste フレームワークを使っているのであれば、そのための ミドルウェアを作成し、環境設定を通じて組み込めばよいのです。例えば OpenStack Compute の新しいスケジューラーや、カスタマイズされたダッシュボードを作成するといった、プロジェクトをカスタマイズする特定の方法もあるかもしれません。この章では、OpenStack をカスタマイズする後者の方法にフォーカスします。
OpenStack をこの方法でカスタマイズするためには、開発環境が必要です。開発環境を手軽に動作させる最良の方法は、クラウドの中で DevStack を動かすことです。
ドキュメンテーションはすべて DevStack (http://devstack.org/) のウェブサイトにあります。 どのプロジェクトをカスタマイズしたいかによって、つまり Object Storage (swift) なのか他のプロジェクトなのかによって、DevStack の環境設定が異なります。以下のミドルウェアの例では、Object Storage を有効にしてインストールしなければなりません。
インスタンス上で、Folsom の安定版用の DevStack を動作させるためには:
ダッシュボード、または nova のコマンドラインインタフェース(CLI)から、以下のパラメータでインスタンスを起動してください。
名前: devstack
イメージ: Ubuntu 12.04 LTS
メモリサイズ: 4 GB RAM (おそらく 2 GB でもなんとかなるでしょう)
ディスクサイズ: 最低 5 GB
nova
コマンドを使っているのであれば、適切なメモリ量とディスクサイズを得るためにnova boot
コマンドに--flavor 6
を指定してください。利用するイメージで root ユーザしか使えない場合、「stack」ユーザを作成しなければなりません。でなければ、
stack.sh
スクリプトに「stack」ユーザを作成させる時に、screen に関するパーミッションの問題にぶつかります。利用するイメージに、既に root 以外のユーザがあるのであれば、このステップは省略できます。ssh root@<IP Address>
adduser --gecos "" stack
プロンプトに対して新しいパスワードを入力します。
adduser stack sudo
grep -q "^#includedir.*/etc/sudoers.d" /etc/sudoers || echo "#includedir /etc/sudoers.d" >> /etc/sudoers
( umask 226 && echo "stack ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/50_stack_sh )
exit
stack ユーザとしてログインし、DevStack の設定を行います。
ssh stack@<IP address>
プロンプトに対して、stack ユーザに作ったパスワードを入力します。
sudo apt-get -y update
sudo apt-get -y install git
git clone https://github.com/openstack-dev/devstack.git -b stable/folsom devstack/
cd devstack
vim localrc
Swift のみ、ミドルウェア例 で使用された, 以下の [1] Swift only localrc の例を参照してください。
他のすべてのプロジェクトについて、 Nova Scheduler Example で使用された, 以下の [2] All other projects localrc の例を参照してください。
./stack.sh
screen -r stack
注記 stack.sh
の実行には、しばらく時間がかかります。できれば、この時間を使って OpenStack ファウンデーションに参加してください (http://www.openstack.org/join/)。stack.sh
を実行する際、“ERROR: at least one RPC back-end must be enabled” というエラーメッセージが出るかもしれません。これは心配しないでください。swift と keystone はRPC (AMQP) バックエンドを必要としないのです。同様に、ImportErrors
はすべて無視できます。Screen は、多くの関連するサービスを同時に見るための便利なプログラムです。GNU screen quick reference. (http://aperiodic.net/screen/quick_reference) を参照してください。
以上で OpenStack の開発環境を準備できましたので、運用環境にダメージを与えることを心配せずに自由にハックできます。Swift のみの環境では ミドルウェア例 に、‘他のすべてのプロジェクトでは Nova Scheduler Example に進んでください。
[1] Swift only localrc
ADMIN_PASSWORD=devstack MYSQL_PASSWORD=devstack RABBIT_PASSWORD=devstack SERVICE_PASSWORD=devstack SERVICE_TOKEN=devstack SWIFT_HASH=66a3d6b56c1f479c8b4e70ab5c2000f5 SWIFT_REPLICAS=1 # Uncomment the BRANCHes below to use stable versions # unified auth system (manages accounts/tokens) KEYSTONE_BRANCH=stable/folsom # object storage SWIFT_BRANCH=stable/folsom disable_all_services enable_service key swift mysql
[2] All other projects localrc
ADMIN_PASSWORD=devstack MYSQL_PASSWORD=devstack RABBIT_PASSWORD=devstack SERVICE_PASSWORD=devstack SERVICE_TOKEN=devstack FLAT_INTERFACE=br100 PUBLIC_INTERFACE=eth0 VOLUME_BACKING_FILE_SIZE=20480M # For stable versions, look for branches named stable/[milestone]. # compute service NOVA_BRANCH=stable/folsom # volume service CINDER_BRANCH=stable/folsom # image catalog service GLANCE_BRANCH=stable/folsom # unified auth system (manages accounts/tokens) KEYSTONE_BRANCH=stable/folsom # django powered web control panel for openstack HORIZON_BRANCH=stable/folsom
ほとんどの OpenStack プロジェクトは Python Paste(http://pythonpaste.org/) フレームワークに基づいています。A Do-It-Yourself Framework (http://pythonpaste.org/do-it-yourself-framework.html) は、このアーキテクチャの最良の紹介です。このフレームワークを使っているため、コードに一切手をいれずに、プロジェクトの処理パイプラインになんらかのカスタム・コードを入れて機能追加を行うことができます。
このように OpenStack をカスタマイズすることを説明するため、Swift 用に、あるコンテナに対して、そのコンテナのメタデータで指定される一群のIPアドレスのみからアクセスできるようにするミドルウェアを作成してみます。このような例は多くの状況で有用です。例えば、一般アクセス可能なコンテナを持っていますが、実際に行いたいことはホワイトリストに基づいた一群のIPアドレスにのみ制限したい場合です。
![]() | 警告 |
---|---|
この例は実証目的のみのためにあります。さらなる作りこみと広範なセキュリティテストなしにコンテナのIPホワイトリスト・ソリューションとして使用するべきではありません。 |
stack.sh
が screen -r stack
で作成したセッションに join すると、Swift インストール用の localrc を使ったのであれば、3つの screen セッションが見えます。
0$ shell* 1$ key 2$ swift
* (アスタリスク)は、どの screen にいるのかを示します。
0$ shell
. 何か作業することができる shell セッションです。1$ key
. keystone サービス。2$ swift
. swift プロキシサービス。
ミドルウェアを作成して Paste の環境設定を通して組み込むためには:
すべての OpenStack のコードは
/opt/stack
にあります。shell セッションの screen の中で swift ディレクトリに移動し、あなたのミドルウェアモジュールを編集してください。cd /opt/stack/swift
vim swift/common/middleware/ip_whitelist.py
以下のコードをコピーしてください。作業が終わったら、ファイルを保存して閉じてください。
# 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. import socket from swift.common.utils import get_logger from swift.proxy.controllers.base import get_container_info from swift.common.swob import Request, Response class IPWhitelistMiddleware(object): """ IP Whitelist Middleware Middleware that allows access to a container from only a set of IP addresses as determined by the container's metadata items that start with the prefix 'allow'. E.G. allow-dev=192.168.0.20 """ def __init__(self, app, conf, logger=None): self.app = app if logger: self.logger = logger else: self.logger = get_logger(conf, log_route='ip_whitelist') self.deny_message = conf.get('deny_message', "IP Denied") self.local_ip = socket.gethostbyname(socket.gethostname()) def __call__(self, env, start_response): """ WSGI entry point. Wraps env in swob.Request object and passes it down. :param env: WSGI environment dictionary :param start_response: WSGI callable """ req = Request(env) try: version, account, container, obj = req.split_path(1, 4, True) except ValueError: return self.app(env, start_response) container_info = get_container_info( req.environ, self.app, swift_source='IPWhitelistMiddleware') remote_ip = env['REMOTE_ADDR'] self.logger.debug(_("Remote IP: %(remote_ip)s"), {'remote_ip': remote_ip}) meta = container_info['meta'] allow = {k:v for k,v in meta.iteritems() if k.startswith('allow')} allow_ips = set(allow.values()) allow_ips.add(self.local_ip) self.logger.debug(_("Allow IPs: %(allow_ips)s"), {'allow_ips': allow_ips}) if remote_ip in allow_ips: return self.app(env, start_response) else: self.logger.debug( _("IP %(remote_ip)s denied access to Account=%(account)s " "Container=%(container)s. Not in %(allow_ips)s"), locals()) return Response( status=403, body=self.deny_message, request=req)(env, start_response) def filter_factory(global_conf, **local_conf): """ paste.deploy app factory for creating WSGI proxy apps. """ conf = global_conf.copy() conf.update(local_conf) def ip_whitelist(app): return IPWhitelistMiddleware(app, conf) return ip_whitelist
env
とconf
には、リクエストについて何をするのか判断するのに使える有用な情報が多数含まれています。どんなプロパティが利用可能なのかを知るには、以下のログ出力文を__init__
メソッドに挿入してください。self.logger.debug(_("conf = %(conf)s"), locals())
そして以下のログ出力分を
__call__
メソッドに挿入してください。self.logger.debug(_("env = %(env)s"), locals())
このミドルウェアを Swift のパイプラインに組み込むには、設定ファイルを1つ編集する必要があります。
vim /etc/swift/proxy-server.conf
[filter:ratelimit]
セクションを探し、以下の環境定義セクションを貼り付けてください。[filter:ip_whitelist] paste.filter_factory = swift.common.middleware.ip_whitelist:filter_factory # You can override the default log routing for this filter here: # set log_name = ratelimit # set log_facility = LOG_LOCAL0 # set log_level = INFO # set log_headers = False # set log_address = /dev/log deny_message = You shall not pass!
[pipeline:main]
セクションを探し、このようにip_whitelist
リストを追加してください。完了したら、ファイルを保存して閉じてください。[pipeline:main] pipeline = catch_errors healthcheck cache ratelimit ip_whitelist authtoken keystoneauth proxy-logging proxy-server
Swift にこのミドルウェアを使わせるために、Swift プロキシサービスを再起動します。swift の screen セッションに切り替えてはじめてください。
Ctrl-A の後で 2 を押します。ここで、2 は screen セッションのラベルです。Ctrl-A の後で n を押し、次の screen セッションに切り替えることもできます。
Ctrl-C を押し、サービスを終了させます。
上矢印キーを押し、最後のコマンドを表示させます。
Enter キーを押し、実行します。
Swift の CLI でミドルウェアのテストをしてください。shell の screen セッションに切り替えてテストを開始し、swift の screen セッションにもどってログ出力をチェックして終了します。
Ctrl-A の後で 0 を押します。
cd ~/devstack
source openrc
swift post middleware-test
Ctrl-A の後で 2 を押します。
ログの中に以下の行があるでしょう。
proxy-server ... IPWhitelistMiddleware proxy-server Remote IP: 203.0.113.68 (txn: ...) proxy-server Allow IPs: set(['203.0.113.68']) (txn: ...)
基本的に、最初の3行はこのミドルウェアが Swift の他のサービスとやりとりする際に、再度認証を行う必要がないことを示しています。最後の2行は、このミドルウェアによって出力されており、リクエストが DevStack インスタンスから送られており、許可されていることを示しています。
-
DevStack 環境の外の、DevStack 用インスタンスにアクセス可能なリモートマシンからミドルウェアをテストします。
swift --os-auth-url=http://203.0.113.68:5000/v2.0/ --os-region-name=RegionOne --os-username=demo:demo --os-password=devstack list middleware-test
Container GET failed: http://203.0.113.68:8080/v1/AUTH_.../middleware-test?format=json 403 Forbidden You shall not pass!
再び Swift のログをチェックすると、以下の行が見つかるでしょう。
proxy-server Invalid user token - deferring reject downstream proxy-server Authorizing from an overriding middleware (i.e: tempurl) (txn: ...) proxy-server ... IPWhitelistMiddleware proxy-server Remote IP: 198.51.100.12 (txn: ...) proxy-server Allow IPs: set(['203.0.113.68']) (txn: ...) proxy-server IP 198.51.100.12 denied access to Account=AUTH_... Container=None. Not in set(['203.0.113.68']) (txn: ...)
ここで、リモートIPアドレスが、許可されたIPアドレスの中になかったため、リクエストが拒否されていることがわかります。
DevStack用インスタンスに戻り、リモートマシンからのリクエストを許可するようなコンテナのメタデータを追加します。
Ctrl-A の後で 0 を押します。
swift post --meta allow-dev:198.51.100.12 middleware-test
ステップ 9 に記載したコマンドをもう一度試すと、今度は成功します。
このような機能試験は、正しいユニットテストと結合テストの代わりになるものではありませんが、作業を開始することはできます。
Python Paste フレームワークを使う他のすべてのプロジェクトで、類似のパターンに従うことができます。単純にミドルウェアモジュールを作成し、環境定義によって組み込んでください。そのミドルウェアはプロジェクトのパイプラインの一部として順番に実行され、必要に応じて他のサービスを呼び出します。プロジェクトのコア・コードは一切修正しません。Paste を使っているプロジェクトを確認するには、/etc/<project>
に格納されている、プロジェクトの conf
または ini
環境定義ファイルの中で pipeline
変数を探してください。
あなたのミドルウェアが完成したら、オープンソースにし、OpenStack メーリングリストでコミュニティに知らせることを薦めます。コミュニティの人々はあなたのコードを使い、フィードバックし、おそらくコントリビュートするでしょう。もし十分な支持があれば、公式なSwift ミドルウェア (https://github.com/openstack/swift/tree/master/swift/common/middleware) に追加するように提案することもできるでしょう。
多くの OpenStack のプロジェクトでは、ドライバ・アーキテクチャを使うことによって、特定の機能をカスタマイズすることができます。特定のインターフェースに適合するドライバを書き、環境定義によって組み込むことができます。例えば、簡単に nova に新しいスケジューラーを組み込むことができます。nova の既存のスケジューラーは、フル機能であり、スケジューリング (http://docs.openstack.org/folsom/openstack-compute/admin/content/ch_scheduling.html) によくドキュメントされています。しかし、あなたのユーザのユース・ケースに依存して、既存のスケジューラで要件が満たせないかもしれません。この場合は、新しいスケジューラーを作成する必要があるでしょう。
スケジューラーを作成するには、nova.scheduler.driver.Scheduler
クラスを継承しなければなりません。オーバーライド可能な5つのメソッドのうち、以下の「*」で示される2つのメソッドをオーバーライドしなければなりません。
update_service_capabilities
hosts_up
schedule_live_migration
*
schedule_prep_resize
*
schedule_run_instance
OpenStack のカスタマイズをデモするために、リクエストの送信元IPアドレスとホスト名のプレフィックスに基づいてインスタンスを一部のホストにランダムに配置するようなNova のスケジューラーの例を作成します。この例は、1つのユーザのグループが1つのサブネットにおり、インスタンスをホスト群の中の一部のサブネットで起動したい場合に有用です。
![]() | 注記 |
---|---|
この例は実証目的のみのためにあります。さらなる作りこみと広範なテストなしにNovaのスケジューラーとして使用するべきではありません。 |
stack.sh
が screen -r stack
で作成したセッションに join すると、多数の screen セッションが見えます。
0$ shell* 1$ key 2$ g-reg 3$ g-api 4$ n-api 5$ n-cpu 6$ n-crt 7$ n-net 8-$ n-sch ...
0$ shell
. 何か作業することができる shell セッションです。1$ key
. keystone サービス。g-*
. glance サービス。n-*
. nova サービス。n-sch
。nova スケジューラーサービス。
スケジューラーを作成して、設定を通して組み込むためには:
OpenStack のコードは
/opt/stack
にあるので、nova ディレクトリに移動してあなたのスケジューラーモジュールを編集します。cd /opt/stack/nova
vim nova/scheduler/ip_scheduler.py
以下のコードをコピーしてください。作業が終わったら、ファイルを保存して閉じてください。
# vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright (c) 2013 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 nova import exception from nova.openstack.common import log as logging from nova import flags from nova.scheduler import driver FLAGS = flags.FLAGS LOG = logging.getLogger(__name__) class IPScheduler(driver.Scheduler): """ Implements Scheduler as a random node selector based on IP address and hostname prefix. """ def _filter_hosts(self, hosts, 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 based on IP address and hostname prefix. """ 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(hosts, hostname_prefix) host = hosts[int(random.random() * len(hosts))] LOG.debug(_("Request from %(remote_ip)s scheduled to %(host)s") % locals()) return host def schedule_run_instance(self, context, request_spec, admin_password, injected_files, requested_networks, is_first_time, filter_properties): """Attempts to run the instance""" 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, 'compute', 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) 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) def schedule_prep_resize(self, context, image, request_spec, filter_properties, instance, instance_type, reservations): """Select a target for resize.""" host = self._schedule(context, 'compute', request_spec, filter_properties) self.compute_rpcapi.prep_resize(context, image, instance, instance_type, host, reservations)
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 に組み込むには、設定ファイルを1つ編集する必要があります。
LOG$ vim /etc/nova/nova.conf
compute_scheduler_driver
設定を見つけ、このように変更してください。LOGcompute_scheduler_driver=nova.scheduler.ip_scheduler.IPScheduler
Nova にこのスケジューラーを使わせるために、Nova スケジューラーサービスを再起動します。
n-sch
screen セッションに切り替えてはじめてください。Ctrl-A の後で 8 を押します。
Ctrl-C を押し、サービスを終了させます。
上矢印キーを押し、最後のコマンドを表示させます。
Enter キーを押し、実行します。
Nova の CLI でスケジューラーのテストをしてください。shell の screen セッションに切り替えてテストを開始し、
n-sch
screen セッションにもどってログ出力をチェックして終了します。Ctrl-A の後で 0 を押します。
cd ~/devstack
source openrc
IMAGE_ID=`nova image-list | egrep cirros | egrep -v "kernel|ramdisk" | awk '{print $2}'`
nova boot --flavor 1 --image $IMAGE_ID scheduler-test
Ctrl-A の後で 8 を押します。
ログの中に以下の行があるでしょう。
LOG2013-02-27 17:39:31 DEBUG nova.scheduler.ip_scheduler [req-... demo demo] Request from 50.56.172.78 scheduled to devstack-nova from (pid=4118) _schedule /opt/stack/nova/nova/scheduler/ip_scheduler.py:73
このような機能試験は、正しいユニットテストと結合テストの代わりになるものではありませんが、作業を開始することはできます。
ドライバ・アーキテクチャを使う他のすべてのプロジェクトで、類似のパターンに従うことができます。単純に、そのドライバ・インタフェースに従うモジュールとクラスを作成し、環境定義によって組み込んでください。あなたのコードはその機能が使われた時に実行され、必要に応じて他のサービスを呼び出します。プロジェクトのコア・コードは一切修正しません。ドライバ・アーキテクチャを使っているプロジェクトを確認するには、/etc/<project>
に格納されている、プロジェクトの環境定義ファイルの中で「driver」変数を探してください。
あなたのスケジューラーが完成したら、オープンソースにし、OpenStack メーリングリストでコミュニティに知らせることをお薦めします。もしかしたら他の人も同じ機能を必要としているかもしれません。彼らはあなたのコードを使い、フィードバックし、おそらくコントリビュートするでしょう。もし十分な支持があれば、もしかしたら公式なNova スケジューラー (https://github.com/openstack/nova/tree/master/nova/scheduler) への追加を提案してもよいでしょう。
ダッシュボードは、Python Django (https://www.djangoproject.com/) Webアプリケーションフレームワークに基づいています。カスタマイズのための最良のガイドは既に執筆されており、 Build on Horizon (http://docs.openstack.org/developer/horizon/topics/tutorial.html) にあります。