第8回:インスタンスを操作するEucalyptusのAPI


 前回はEucalyptusのAPIについて説明しました。今回はEucalyptusが提供するAPIのうち、もっとも基本的なインスタンスを操作するAPIを呼び出すプログラミングを説明します。各ライブラリの使用方法とAPIについて学ぶことでProgrammable Infrastructureを活用したアプリケーションの作成ができるようになります。

 

APIライブラリのセットアップ

 前回にEucalyptusで利用できるライブラリを紹介しましたが、本連載ではEucalyptus社が提供するeuca2oolsにも使用されているライブラリ「boto」と、RightScale社が開発・公開している「RightAws」を使用します。

 botoはPython言語で記述され、EC2/S3/SQS/SDBなどに対応したAmazon Web Services用のAPIライブラリです。MITライセンスで提供されているOSSのため無償で利用することができます。

 RightAwsはRuby言語で記述され、EC2/S3/SQS/SDBなどに対応しており、boto同様MITライセンスで公開されています。

【botoとRightAwsで対応しているAWSの機能】
AWSの機能botoRightAws
Amazon Elastic Compute Cloud (Amazon EC2)
Amazon Elastic MapReduce×
Auto Scaling
Amazon CloudFront
Amazon SimpleDB
Amazon Relational Database Service (Amazon RDS)
Amazon Fulfillment Web Service (Amazon FWS)××
Amazon Simple Queue Service (Amazon SQS)
Amazon Simple Notification Service (Amazon SNS)×
Amazon CloudWatch
Amazon Virtual Private Cloud (Amazon VPC)
Elastic Load Balancing
Amazon Flexible Payments Service (Amazon FPS)××
Amazon DevPay××
Amazon Simple Storage Service (Amazon S3)
Amazon Elastic Block Storage (EBS)
AWS Import/Export××
Alexa Web Information Service××
Alexa Top Sites××
Amazon Mechanical Turk×
AWS Identity and Access Management (IAM)×
Amazon Route53 DNS Service×

 botoは公式サイトよりダウンロードできますが、前述したようにEucalyptusが提供するコマンドラインツール「euca2ools」で利用されているため、euca2oolsをインストールするだけで開発環境も動作環境も構築することができます。本連載のサンプルコードは、CentOS 5.5上にeuca2oolsをインストールした環境で動作確認しています。

 RightAwsはRubyForgeからダウンロードできますが、gemで簡単にインストールすることができます。なお、RubyForgeで公開されているバージョンは1.10.0ですが、gemでインストールするとなぜか2.0.0がインストールされます。

 

botoの使い方

 botoをEucalyptusで使用するための方法を説明します。基本的にはどのAPIを使用する場合も下記の手順を実行し作成したコネクションを介します。botoはQuery APIを使用するためアクセスキーとシークレットキーを使用します。

#!/usr/bin/env python2.5  # -*- coding:utf-8 -*-  import boto  from boto.ec2.regioninfo import RegionInfo    HOSTNAME="your-cloud-host"       # eucarcの EC2_URLの内 ホスト部分を設定します.  EC2_ACCESS_KEY='your-access-key' # eucarcの EC2_ACCESS_KEYを指定します.  EC2_SECRET_KEY='your-secret-key' # eucarcの EC2_SECRET_KEYを指定します.    # botoでEucalyptusを使用するための設定です.  region = RegionInfo(name="eucalyptus", endpoint=HOSTNAME)    # コネクションを作成します.  connection = boto.connect_ec2(      aws_access_key_id=EC2_ACCESS_KEY,      aws_secret_access_key=EC2_SECRET_KEY,      is_secure=False,      region=region,      port=8773,      path="/services/Eucalyptus"      )

 作成したconnectionは、boto.ec2.connection.EC2Connectionクラスのインスタンスです。botoではこのクラスのメソッドを使用してAPIを呼び出します。

 euca2oolsを使用する際には、毎回eucarcをsourceして環境変数を設定していると思います。環境変数を使用する場合は下記のように記述します。

#!/usr/bin/env python2.5  # -*- coding:utf-8 -*-  import boto  from boto.ec2.regioninfo import RegionInfo  import os  import sys  import urlparse    EC2_ACCESS_KEY = os.environ.get("EC2_ACCESS_KEY")  EC2_SECRET_KEY = os.environ.get("EC2_SECRET_KEY")  EC2_URL = os.environ.get("EC2_URL")    url = urlparse.urlparse(EC2_URL)    region = RegionInfo(name="eucalyptus", endpoint=url.hostname)  connection = boto.connect_ec2(      aws_access_key_id=EC2_ACCESS_KEY,      aws_secret_access_key=EC2_SECRET_KEY,      is_secure=url.scheme=="https",      region=region,      port=url.port,      path=url.path      )

 

RightAwsの使い方

 以下では、RightAwsをEucalyptusで使用するための方法を説明します。基本的には、どのAPIを使用する場合も下記の手順を実行し作成したコネクションを介します。

 RightAwsも、Query APIを使用するためアクセスキーとシークレットキーを使用します。なお、ちょっと試してみる程度であれば、コード内でEC2エンドポイントやアクセスキーやシークレットキーをハードコーディングしてもよいですが、以下のように記述してeucarcで設定された環境変数を参照するか、もしくは実行時のオプション(ruby -s -ec2_access_key=VALUE, -ec2_secret_key=VALUE, -ec2_url=VALUE)で定義できるようにしたほうが取り回しがよくなります。

#!/usr/bin/ruby -s    require 'right_aws'    $ec2_access_key ||= ENV['EC2_ACCESS_KEY'] if ENV.has_key?('EC2_ACCESS_KEY')  $ec2_secret_key ||= ENV['EC2_SECRET_KEY'] if ENV.has_key?('EC2_SECRET_KEY')  $ec2_url ||= ENV['EC2_URL'] if ENV.has_key?('EC2_URL')    @ec2 = RightAws::Ec2.new(     $ec2_access_key, $ec2_secret_key, {:endpoint_url => $ec2_url}     )

 

インスタンスを操作するAPI

 インスタンスを操作するAPIは下記の4つがあります。特にRunInstancesとDescribeInstancesは非常に良く使用するAPIです。

【インスタンス機能のAPI】
API説明
RunInstancesインスタンスを起動します。一度に複数のインスタンスを起動することができます。
DescribeInstancesインスタンスの情報を取得します。ユーザーの全インスタンスの情報を取得しますが、インスタンスIDを指定して特定のインスタンスの情報を取得することができます。
RebootInstancesインスタンスを再起動します。一度に複数のインスタンスを再起動することができます。
TerminateInstancesインスタンスを停止します。一度に複数のインスタンスを停止することができます。

 

インスタンスを起動する

 インスタンスを起動するには、RunInstances APIを使用します。RunInstances APIを呼び出す際のパラメータと説明は下記の通りです。

【インスタンス機能のAPI】
パラメータ説明必須/任意
ImageId起動するインスタンスのマシンイメージのIDを指定します。emi-12345678必須
MinCountインスタンスの最小起動台数を設定します。クラウドに大して最低でも起動して欲しいインスタンスの台数を指定します。この台数を下回るキャパシティしかクラウドにない場合は、インスタンスの起動リクエストは受け付けられず、エラーが返却されます。1必須
MaxCountインスタンスの最大起動台数を設定します。MinCountとセットで指定されます。MinCountとMaxCountを同じ値にすることで指定した台数起動できる場合はリクエストが受け付けられるリクエストを送付できます。1必須
KeyNameキーペア名を指定します。key001任意
SecurityGroupセキュリティグループを指定します。default任意
InstanceTypeインスタンスタイプを指定します。Eucalyptusでは下記の5つが使用できます。
・ m1.small
・ c1.medium
・ m1.large
・ m1.xlarge
・ c1.xlarge
m1.small任意
Placementゾーンを指定します。Eucalyptusでは、クラスタ名を指定します。cluster01任意
KernelIdカーネルイメージのIDを指定します。指定がない場合は、マシンイメージに紐づくカーネルイメージが使用されますが、そちらもない場合は、クラウドのデフォルト値に設定されているカーネルイメージが使用されます。eki-12345678任意
RamdiskIdラムディスクイメージのIDを指定します。指定がない場合は、マシンイメージに紐づくラムディスクイメージが使用されますが、そちらもない場合は、クラウドのデフォルト値に設定されているラムディスクイメージが使用されます。eri-12345678任意
UserDataインスタンスから取得できるメタデータを設定します。様々なデータを自由に設定することができます。user-data任意

 RunInstances APIを呼び出すとレスポンスとして下記のようなXMLが返却されます。

<RunInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2010-08-31/">    <requestId>582b4fab-3fbc-473b-adef-dde39655aa65</requestId>    <reservationId>r-467008F0</reservationId>    <ownerId>user001</ownerId>    <groupSet>      <item>        <groupId>default</groupId>      </item>    </groupSet>    <instancesSet>      <item>        <instanceId>i-442208CD</instanceId>        <imageId>emi-37981294</imageId>        <instanceState>          <code>0</code>          <name>pending</name>        </instanceState>        <privateDnsName>euca-0-0-0-0.eucalyptus.internal</privateDnsName>        <dnsName>euca-0-0-0-0.eucalyptus.localhost</dnsName>        <reason>NORMAL:  -- []</reason>        <keyName>key01</keyName>        <amiLaunchIndex>0</amiLaunchIndex>        <productCodes/>        <instanceType>m1.small</instanceType>        <launchTime>2010-12-14T13:27:26.159Z</launchTime>        <placement>          <availabilityZone>cluster0</availabilityZone>        </placement>        <kernelId>eki-BB6A13B9</kernelId>        <ramdiskId>eri-0DE3150A</ramdiskId>        <monitoring>          <state>false</state>        </monitoring>      </item>    </instancesSet>  </RunInstancesResponse>

 上記XMLに含まれる情報のうち重要な情報は下記の表の通りです。

【RunInstancesの戻り値】
情報説明
requestIdどのAPIのレスポンスにも含まれるリクエスト毎に割り当てられるIDです。
reservationIdインスタンス起動リクエスト毎に発行されるIDです。1つのリザベーションに複数のインスタンスが所属します。
ownerIdインスタンスを起動したユーザのIDです。
groupSetインスタンス起動時に指定したセキュリティグループです。
instanceIdインスタンスに一意に付けられるIDです。
imageIdマシンイメージのIDです。
instanceStateインスタンスの状態です。起動途中を表す「pending」、起動済みを表す「running」、停止中を表す「shutting-down」、停止を表す「terminated」の4つの状態が存在します。
privateDnsNameインスタンス同士の通信のみに利用可能なプライベートIP・DNSです。
dnsNameインスタンスにアクセスする際のパブリックIP・DNSです。
instanceTypeインスタンスのインスタンスタイプです。
keyNameインスタンス起動時に指定したキーペアです。
amiLaunchIndexリザベーション単位でインスタンスに割り振る番号です。一度のリクエストで複数台のインスタンスを起動した場合、0からインクリメントされたIDが付与されます。
launchTimeインスタンスの起動時刻です。
placementインスタンスが起動したゾーン名です。Eucalyptusではクラスタ名が該当します。
kernelIdカーネルイメージのIDです。
ramdiskIdラムディスクイメージのIDです。

 それでは、表中のパラメータの例を参考にしてインスタンスを起動するAPIを呼び出す方法を説明します。

 botoもRightAwsでも、先ほど作成したコネクションのrun_instancesメソッドを使用して起動しますが、botoではセキュリティグループをリストで渡す必要がある点に注意してください(RightAwsは配列・文字列どちらでも可)。

【botoでのRunInstances】
# インスタンス起動  reservation = connection.run_instances(      image_id="emi-12345678",      min_count=1,      max_count=1,      instance_type="m1.small",      security_groups=["default"],      kernel_id="eki-12345678",      ramdisk_id="eri-12345678",      key_name="key001",      placement="cluster01",      )    # 結果の表示  for instance in reservation.instances:    print(reservation.id,          reservation.groups[0].id,          instance.id,          instance.image_id,          instance.dns_name,          instance.private_dns_name,          instance.state,          instance.placement,          instance.launch_time)
【RightAwsでのRunInstances】
# run_instancesメソッドによるインスタンスの起動  # 引数は以下の5つが必須  # run_instances(image_id, min_count, max_count, group_ids, key_name)  instances = @ec2.run_instances(     'emi-12345678', 1, 1, 'default', 'key001'  )    # launch_instancesメソッドによるインスタンスの起動  # 引数はマシンイメージIDが必須  # launch_instances(image_id, options={})  instances = @ec2.launch_instances(     'emi-12345678', {        :group_ids => 'default', :key_name => 'key001',        :instance_type => 'm1.small'     }  )    # 結果の表示  pp reservation

 

インスタンスの情報を取得する

 起動したインスタンスの一覧や情報を取得するには、DescribeInstances APIを使用します。DescribeInstancesはパラメータ無で実行することができますが、インスタンスIDを指定してインスタンスの情報を取得することもできます。その際インスタンスIDは複数個指定することができます。

 DesribeInstances APIレスポンスのXMLには、RunInstancesのレスポンスと同様の情報が含まれています。ただし、RunInstancesのレスポンスには1つのReservationのみ含まれるのに対して、DescribeInstancesのレスポンスには複数のReservationが含まれています。

 botoでのインスタンスの情報取得には、コネクションのget_all_instancesメソッドを使用します。

# botoでのインスタンス情報取得  reservations = connection.get_all_instances()    # 取得した情報を表示する  for reservation in reservations:    for instance in reservation.instances:      print(reservation.id,            reservation.groups[0].id,            instance.id,            instance.image_id,            instance.dns_name,            instance.private_dns_name,            instance.state,            instance.placement,            instance.launch_time)    # インスタンスIDを指定する場合  instance_ids = ["i-12345678", "i-87654321"]  reservations = connection.get_all_instances(instance_ids)

 RightAwsでのインスタンスの情報取得には、describe_instancesメソッドを使用します。

# RIghtAwsでのインスタンス情報取得  instances = @ec2.describe_instances    # 取得した情報を表示する  pp reservations    # インスタンスIDを指定する場合  instance_ids = ['i-12345678', 'i-87654321']  instances = @ec2.describe_instances(instance_ids)

 

インスタンスを再起動する

 起動したインスタンスを再起動するには、RebootInstances APIを使用します。

 インスタンスを停止した場合は、インスタンスのルートディスクに保存したデータはリセットされてしまいますが、RebootInstancesではインスタンスのルートディスクに保存したデータはリセットされません。RebootInstances APIでは複数個、最低1つのインスタンスIDをパラメータとして指定する必要があります。

 botoでインスタンスを再起動するには、コネクションのreboot_instancesメソッドにインスタンスIDのリストを渡します。

# botoでのインスタンス再起動  instance_ids = ["i-12345678", "i-87654321"]  connection.reboot_instances(instance_ids)

 RightAwsでインスタンスを再起動する場合もreboot_instancesメソッドを使用します。

# RightAwsでのインスタンス再起動  instance_ids = ['i-12345678', 'i-87654321']  @ec2.reboot_instances(instance_ids)

 

インスタンスを停止する

 最後に、インスタンスの停止について説明します。

 起動したインスタンスを停止するには、TerminateInstances APIを使用します。TerminateInstances APIを使用してインスタンスを停止した場合、インスタンスのルートディスクに保存したデータはすべて失われてしまいます。

 インスタンスを停止する前に大事なデータやイメージは保存しておきましょう。TerminateInstances APIはRebootInstances APIと同様に複数個、最低1つのインスタンスIDをパラメータとして指定する必要があります。

 botoでインスタンスを停止するには、terminate_instancesメソッドにインスタンスIDのリストを渡します。

instance_ids = ["i-12345678", "i-87654321"]  connection.terminate_instances(instance_ids)

 RightAwsでインスタンスを停止する場合もterminate_instancesメソッドを使用します。

instance_ids = ['i-12345678', 'i-87654321']  @ec2.terminate_instances(instance_ids)

 

インスタンス操作の他の方法

 botoでは上記で紹介した方法以外にもrun_instancesメソッドやget_all_instancesメソッドで取得したreservation内のinstanceを表すオブジェクトを使用して再起動や停止を行うことができます。

reservations = connection.get_all_instances()  for reservation in reservations:    for instance in reservation.instances:      if instance.id == "i-12345678":        instance.reboot() # インスタンスを再起動する      else        instance.terminate() # インスタンスを停止する

 一方、RightAwsではdescribe_instancesの戻り値はbotoのようになっていないため、以下のように記述しなければなりません。

@ec2.describe_instances.each do |instance|     if 'i-12345678' == instance[:aws_instance_id]        @ec2.reboot_instances(instance[:aws_instance_id])     else        @ec2.terminate_instances(instance[:aws_instance_id])     end  end

 さらに特定のインスタンスの状態を監視したい場合、例えばインスタンスがrunning状態になるまでポーリングする処理はbotoでは下記のように記述することができます。

reservation = connection.run_instances(      image_id="emi-12345678",      min_count=1,      max_count=1,      instance_type="m1.small",      security_groups=["default"],      kernel_id="eki-12345678",      ramdisk_id="eri-12345678",      key_name="key001",      placement="cluster01",      )  instance = reservation.instances[0]    # インスタンスの状態を監視する  import time  while True:    instance.update() # インスタンスの情報を更新する    if instance.state == "running":      break    else:      time.sleep(10) # 10秒待つ.  print instance.id, "is running !"

 一方で、RightAwsでは以下のように記述することができます。

instances = []  @ec2.launch_instances(     'emi-12345678', {        :min_count => 1, :max_count => 1,        :instance_type => 'm1.small', :group_ids => 'default',        :kernel_id => 'eki-12345678', :ramdisk_id => 'eri-12345678',        :key_name => 'key001', :availability_zone => 'cluster01',     }  ).each {|instance| instances << instance[:aws_instance_id]}    # インスタンスの状態を監視する  state = false  begin     sleep 10     @ec2.describe_instances(instances).each do |instance|        if 'running' == instance[:aws_state]           state = true        elsif 'pending' == instance[:aws_state]           state = false        else           state = false           break        end     end  end until state  puts instances, "is/are running" if state

 

次回は、ElasticIPやネットワークに関するAPIについて

 これでインスタンス操作のAPIを呼び出すプログラミングができるようになりました。次回は、ElasticIPやセキュリティグループなどのネットワークに関するAPIについて説明します。

 


羽深 修
Eucalyptus歴はまだ1年ですが、周囲からはEucalyptus中毒と勘違いされているようです。Japan Eucalyptus User Groupの活動に参加し、オープンソースカンファレンスでネタなどを披露しています。度々Eucalyptusへのパッチも書いてます。ちなみに仕事ではCentOS + Xenという環境でEucalyptusを利用していますが、自宅のEucalyptus環境はGentoo Linux + KVMで動かしています。

志田 隆弘
主にEucalyptusやクラウドとはあまり関係のない分野でちょこちょこと活動していました。Eucalyptusはバージョン 1.3の頃からいじり始め、かれこれ2年近くEucalyptusに浸かった生活をしています。Eucalyptus 1.4が出たタイミングで、Tanacasinoという名前のGUIクライアントを作ったりしていました。最新のEucalyptusで動作するので、ぜひ使ってみてください。

田中 智文
志田さんとともに初期の頃からEucalyptusの調査・検証・使用してきました。志田さんの作成したTanacasinoのメンテナンスと機能拡張を行っています。新婚ほやほやなので家でのハック活動時間が少ないですが、Walrus Clientを作ってみたりbotoを使ってEucalyptusを操作して遊んでいます。
関連情報
(羽深 修/志田 隆弘/田中 智文)
2010/12/24 06:00