AWS SDK for Rubyを使ってEC2インスタンスのステータスを確認する

アプリケーション側で、任意のAWSのEC2インスタンスについて、現在の稼働状況をリアルタイムに確認したい時がある。例えば外部アプリケーションからEC2インスタンスを起動させたり、停止させたりする場合などに、インスタンスの稼動状況を確認してステータスが変わったら次のプロセスを実行したいとかいうケースが、それに当てはまる。

SDK for Rubyでは、AWS::EC2クラスのclient.describe_instance_statusメソッドで特定のECインスタンスのステータスを取得することができる。インスタンスのステータスにはsystem_statusinstance_statusの2種類があって、正確な稼動状態を確認したい場合はこの2つのステータスを取得する必要がある。 そして、注意しないといけないのが、インスタンスが停止している時はステータスが存在していないということだ。つまり、停止しているインスタンスに対してclient.describe_instance_statusメソッドを実行した場合、ステータス情報が格納されているinstance_status_setオブジェクトの中身が取得できないので、それを踏まえてコーディングしないと、ステータス取得エラーとなってしまう。 例えば、停止状態のインスタンスを起動した場合のステータスを監視する時など、最初はインスタンスが停止しているので、instance_status_setオブジェクト内にステータスがないということをあらかじめ想定して処理を作る必要がある。 また、ターミネートされたインスタンスはclient.describe_instance_statusメソッドを実行する対象自体がないことので、こちらも注意する必要がある。

さて、上記もろもろを踏まえてEC2インスタンスのステータス確認するRubyプログラムを作ってみた。

chkins.rb
# encoding: utf-8
require "aws-sdk"

def check_instance_status

    ec2 = AWS::EC2.new(
      :access_key_id => Params[:access_key_id], 
      :secret_access_key => Params[:secret_access_key], 
      :region => Params[:region]
    )

    AWS.memoize do
      status = []
      if ec2.instances[Params[:instance_id]].exists?
        ec2info = ec2.client.describe_instance_status({ 'instance_ids' => [Params[:instance_id]] })
        if ec2info.instance_status_set.empty?
          # instance has stopped
          status << 'stopped'
          message = '%s has stopped'
        else
          ec2info.instance_status_set.map { |i| 
            sys_status = i.respond_to?(:system_status) ? i.system_status.details[0].status : nil
            ins_status = i.respond_to?(:instance_status) ? i.instance_status.details[0].status : nil
            status << sys_status << ins_status
          }
          if status.include?('passed')
            # instance is running
            message = '%s is running'

          elsif status.include?('initializing')
            # instance is initializing
            message = '%s is starting'

          else
            # otherwise
            message = '%s is unknown status'

          end
        end
      else
        # instance has terminated
        status << 'terminated'
        message = '%s has already terminated'
      end
      puts sprintf("#{message}. status: %s", "Instance: #{Params[:instance_id]}", status)
    end
end

begin
  # init
  Params = { 
    :access_key_id => ARGV[0], 
    :secret_access_key => ARGV[1], 
    :region => ARGV[2], 
    :instance_id => ARGV[3], 
    :timeout => ARGV[4].nil? ? 1 : ARGV[4].to_i
  }
  raise 'Parameter is missing.' if Params.values.include?(nil)

  for i in 1..Params[:timeout] do
    sleep 1 if i > 1
    check_instance_status
  end

rescue => e
  puts e
end

プログラム実行時の引数として、

  • AWSアクセスキー(必須)
  • AWSシークレットアクセスキー(必須)
  • リージョン(必須)
  • インスタンスID(ステータスを確認したいインスタンスID)
  • タイムアウト回数(1秒単位で何回確認するか。省略可。省略時は1回のみ)

を渡してあげると、ほぼタイムアウト回数≒秒数の間ずっと指定インスタンスのステータスを標準出力でモニタリングしてくれます。

実行例:
$ ruby -Ku chkins.rb <AWSアクセスキー> <AWSシークレットキー> ap-southeast-1 <インスタンスID> 10
Instance: i-******** is running. status: ["passed", "passed"]
Instance: i-******** is running. status: ["passed", "passed"]
Instance: i-******** is running. status: ["passed", "passed"]
Instance: i-******** is running. status: ["passed", "passed"]
Instance: i-******** is running. status: ["passed", "passed"]
Instance: i-******** is running. status: ["passed", "passed"]
Instance: i-******** is running. status: ["passed", "passed"]
Instance: i-******** is running. status: ["passed", "passed"]
Instance: i-******** is running. status: ["passed", "passed"]
Instance: i-******** is running. status: ["passed", "passed"]

指定したインスタンスIDが稼働中であれば、上記のような結果が約10秒間モニタリングされます。

※ 実行前には、あらかじめAWS SDK for Ruby(バージョンは1系)をインストールしておく必要があります。

特定ステータスを検知した時にメールを発報するなどの処理を付け足して、スクリプトの起動をcronに登録し、定期的に実行するようにすれば、AWSのCloudWatchに依存せずにインスタンスの稼動状況を監視させたりとかできて良いかも。