Rubyで楽しむAWS APIプログラミング入門

記事タイトルとURLをコピーする

サービス開発課のくればやしです。ドラクエⅠ ・Ⅱが楽しみすぎます☺️

AWSでは様々なリソースをAPIで操作可能です。

それらを簡単に扱うため、AWS公式からPythonやGo等のライブラリが提供されており、RubyのSDK(AWS SDK for Ruby、以下Ruby SDK)もその中の一つです。本記事ではRubyを用いてAWSのAPIに入門してみたいと思います。

本記事の対象

  • RubyでAWSのAPIプログラミングを試してみたい人

事前準備

以下の環境で動作確認しました。

  • ホスト: Amazon Linux 2023
  • Ruby: #3.2.2
  • AWS SDK for Ruby: 3.232.0

SDK等インストール手順(サンプル)

# Ruby本体やAWS SDK の実行に必要なライブラリのインストール
> sudo yum install ruby ruby-devel gcc
> ruby -v
# Version が表示されることを確認
> bundle init
> vim Gemfile

gem "aws-sdk-ec2"
gem "aws-sdk-iam"
gem "ox"

> bundle install

環境準備

EC2インスタンスの操作を例にしてみるので、リソース操作の対象のEC2インスタンスを停止済みで準備してください。

基本的な方法

まずは停止しているEC2インスタンスを開始する操作を通じて簡単な使い方を確認します。インスタンスID等は適宜読み替えてください。

基本的な流れとしては、操作したいリソースのサービスのクライアントを生成し、それを用いてオペレーションを実行します。

例として、以下のコードを実行することで、EC2インスタンスの開始をリクエストできます。AWSマネジメントコンソール等で確認すると、停止していたインスタンスが起動することを確認できると思います。

require "aws-sdk-ec2"

client = Aws::EC2::Client.new
client.start_instances(
  instance_ids: ["i-xxxxxxxxxxxxx"]
)

EC2のクライアントのAPIドキュメントは以下になりますので、こちらでオプション等確認できます。

Class: Aws::EC2::Client — AWS SDK for Ruby V3

オプションとして、例えば、認証情報やリージョンの設定が可能です。以下のように記載することで、リージョンとアクセスキーによる認証情報を指定できます。

Aws::EC2::Client.new(
  region: "us-east-1",
  credentials: Aws::Credentials("access_key_id", "secret_access_key")
)

認証情報については、アクセスキーの他、STSを用いた設定等も可能です。以下にドキュメントがあります。

docs.aws.amazon.com

操作結果の確認

上記でリソースの操作をリクエストするところまで確認したので、ここからは操作結果を確認していきます。

AWSではリソースにもよりますが、APIの実行成功 = 意図したステータスへの変更とは限りません。EC2インスタンスのように stopped -> pending -> running のように状態が遷移するものが多くあります。

実際に、停止状態のインスタンスを開始したときのレスポンスの中身を展開してみましょう。 current_statepending になっていることが分かります。これが running に変われば開始処理が最終的に成功したことを確認できます。

response = client.start_instances(
  instance_ids: ["i-xxxxxxxxxxxxx"]
)

#<struct Aws::EC2::Types::StartInstancesResult
 starting_instances=
  [#<struct Aws::EC2::Types::InstanceStateChange
    current_state=#<struct Aws::EC2::Types::InstanceState code=0, name="pending">,
    instance_id="i-xxxxxxxxxxxxx",
    previous_state=#<struct Aws::EC2::Types::InstanceState code=80, name="stopped">>]>

APIを実行後に意図したステータスに変更が完了したかを確認するためには、API実行後にステータスのチェックをポーリングする必要があります。ステータスのチェックは #describe_instances APIで可能です。

例えば、1分おきに10回まで状態を確認するのであれば以下のように実装できます。

Class: Aws::EC2::Client — AWS SDK for Ruby V3

10.times do |i|
  res = client.describe_instances(instance_ids: ["i-xxxxxxxxxxxxx"])
  status = res.reservations[0].instances[0].state.name
  if status == "running"
    break
  end

  sleep(60)
end

ただし、少し実装が直接的で煩わしいと思うかもしれません。そこで、Ruby SDKにはウェイターという仕組みがあります。 以下のようにウェイターを実行することで、インスタンスが指定のステータスになるまで待機します。

# インスタンスが runngin になるまで待機
client.wait_until(:instance_running, instance_ids:['i-xxxxxxxxxxxxxxxx'])

docs.aws.amazon.com


リトライ

APIとのやり取りにあたっては、サーバー側の一時的なエラー等で接続に失敗することがあるため、リトライの整理が必須となります。Ruby SDK でもリトライの仕組みが実装されており、デフォルトで3回リトライされます。また、リトライのアルゴリズムはデフォルトで、exponential backoffが用いられます。

これらの設定はカスタマイズが可能です。例えば、リトライ回数を5回に調整するには以下のように指定します。

client = Aws::EC2::Client.new(
  retry_limit: 5,
)

その他のオプションについては、各サービスのクライアントのドキュメント等に詳細が記載されています。

https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/EC2/Client.html

ページング

API経由でのAWSのリソースの取得は大量になることがあるため、一度のAPI実行で取得しきれない場合、 Ruby SDKではバックグランドでページングを行い全件取得します。

レスポンスのインスタンスがEnumerableになっており、 Enumerable なメソッドを用いることで、リソース全件を参照できます。実際にコードで確認してみましょう。

IAMのポリシーはブログ執筆時点で、 1600 ほどあり、一度のAPIコールで100件取得されます。ページングの動作確認にちょうどいいので、これを使います。

まずは Enumerable なメソッドを使わずにレスポンス中に含まれるポリシーの数を確認します。すると100件であることが分かります。

# APIコールの動作を確認するためにデバッグモードで
# クライアントを生成します
client = Aws::IAM::Client.new(
  logger: Logger.new($stdout),
  log_level: :debug,
)
res = client.list_policies.size # 100

次は、レスポンスに対して Enumerable なメソッドを呼んでみます。出力の結果をわかりやすく確認するため、最終的に取得できるポリシーの総数等を取得します。

count = 0
policies_count = 0

res = client.list_policies
res.each do |r|
  count += 1
  policies_count  += r.policies.size
  p count # => インクリメント
end
p policies_count # => 1584

実行すると以下のような出力を得られます。

each 内でループするたびに、APIコールし、必要なデータを取得していることが見て取れます。 marker は前回リクエストからの続きのリソースを取得するのに必要なパラメータです。

D, [2025-09-17T11:14:42.659059 #13676] DEBUG -- : [Aws::IAM::Client 200 0.976706 0 retries] list_policies()

1
D, [2025-09-17T11:14:43.059577 #13676] DEBUG -- : [Aws::IAM::Client 200 0.398486 0 retries] list_policies(marker:"xxxxxxxxxxxxxxx")

2
D, [2025-09-17T11:14:43.300114 #13676] DEBUG -- : [Aws::IAM::Client 200 0.235317 0 retries] list_policies(marker:"xxxxxxxxxxxxxxx")

...

1584

docs.aws.amazon.com

アプリケーションの都合で、自動で全件取得するのではなく、自分で繰り返し処理をコントロールしたいときもあるかもしれません。そのような場合は、 例えば、以下のように marker を自分で取り回して処理することで、(この例では)100件ずつ処理を進めることができます。

res = client.list_policies
loop do
  if res.is_truncated
    res = client.list_policies(marker: res.marker)
  else
    break
  end
end

リソースインターフェース

また、リソースの情報の取得には、クライアントのインスタンスにより取得する方法とは別のアプローチが用意されています。 https://github.com/aws/aws-sdk-ruby#resource-interfaces

以下のように指定することで、AWSのリソースをより直感的に取得できます。

ec2 = Aws::EC2::Resource.new
pp ec2.instances.first # Aws::EC2::Instance

ただし、こちらは提供されているサービスが限られているため、利用の前には注意が必要です。

Only a few services implement a resource interface. They are defined by hand in JSON and have limitations. Please use the Client API instead.

エラーハンドリング

次に、エラーハンドリグについて確認しておきたいと思います。EC2を例にすると、エラークラスの継承関係は以下のようになっています。

EC2の個別のエラー < Aws::EC2::Errors::ServiceError < Aws::Errors::ServiceError Module: Aws::EC2::Errors — AWS SDK for Ruby V3

すなわち、個別にハンドリングしたいエラーがある場合は、そのエラークラスを指定し、EC2サービス全体、あるいはAWSのサービス全体でハンドリングしたい場合は、それぞれ指定できます。

def test
  ...
  rescue Aws::EC2::Errors::InvalidInstanceIDNotFound => e
    # インスタンスIDが見つからなかった場合
  rescue Aws::EC2::Errors::ServiceError => e
    # それ以外のEC2関係のエラー
  rescue Aws::Errors::ServiceError => e
    # EC2以外のAWSのエラー
end

上記ドキュメントを確認すると分かるようにサービスによっては、エラークラスが動的に定義されている場合があるため、検証等を通じてエラークラスを確認しておく必要はあると思います。

Additionally, error classes are dynamically generated for service errors based on the error code if they are not defined above.

EC2では静的に定義されている個別のエラークラスはドキュメント上で確認できませんでしたが、例えばFSxでは静的に定義されているエラークラスが確認できますので、実装の際はまずはこれらを確認しておくと良さそうです。

AccessPointAlreadyOwnedByYou
ActiveDirectoryError
BackupBeingCopied
...
Module: Aws::FSx::Errors — AWS SDK for Ruby V3

テスト

アプリケーション開発ではテスト(Spec)の実装を行うことが多いと思います。AWSのリソース操作をスタブするための機能が Ruby SDK には用意されています。

クライアントのインスタンス生成時にスタブを行うことを指定し、ダミーのデータを登録します。

client = Aws::EC2::Client.new(stub_responses: true)
client.stub_responses(:describe_volumes, {
     volumes: [
       {volume_id: "volume-11111111", state: "available"},
     ]
})
response = client.describe_volumes
pp response.volumes

以下のようにダミーのデータが取得されます。

[#<struct Aws::EC2::Types::Volume
  attachments=[],
  availability_zone=nil,
  create_time=nil,
  encrypted=nil,
  kms_key_id=nil,
  outpost_arn=nil,
  size=nil,
  snapshot_id=nil,
  state="available",
  volume_id="volume-11111111",
  iops=nil,
  tags=[],
  volume_type=nil,
  fast_restored=nil,
  multi_attach_enabled=nil>]

その他の使い方については以前別の記事でも書きましたので、こちらを参照頂けたらと思います。

blog.serverworks.co.jp

おわりに

本記事では、AWS SDK for Rubyを用いてAWSのAPIプログラミングの基本をご紹介しました。どなたかのお役にたてば幸いです。

関連

aws.amazon.com

docs.aws.amazon.com

blog.serverworks.co.jp

紅林輝(くればやしあきら)(サービス開発部) 記事一覧

サービス開発部所属。2015年にサーバーワークスにJOIN。クラウドインテグレーション部を経て、現在はCloud Automatorの開発に従事。ドラクエ部。推しナンバーはⅤ、推しモンスターはクックルー。