日別アーカイブ: 2014年9月12日

AWS 環境の自動停止スクリプト

AWS を必要な時だけ立ち上げておきたいんだけど、人が操作するとどうしてもうっかり落とし忘れてしまう。
そこで、SDK を使うことである時間が来た際に自動で落とすような状態にしておきたい。

立ち上げるのは、必要に応じて自分で手動で行えばいいので、あくまで落とすことを優先させる。

AWS の SDK はいろいろな言語で出ているが、今回は Ruby を使ってみることにした。
あまりこれまで触れたことのない言語だったので。
理由はそれだけ。面白そうだったからに近い。

EC2 を落とすことは比較的簡単にできる。

予め、定義を作っておいて

[aws-config.yml]
access_key_id: <ACCESS_KEY_ID>
secret_access_key: <SECRET_ACCESS_KEY>
ec2_endpoint: ec2.ap-northeast-1.amazonaws.com

 

止めるための文を書く

[stopall-ec2.rb]
require 'aws-sdk'

AWS.config(YAML.load(File.read("./aws-config.yml")))

ec2 = AWS::EC2.new

ec2.instances.each do |ins|
  ins.stop if ins.status == :running
end

 

EC2 は比較的すんなりと行ったんだけど、RDS はよくわかっていない。

RDS の場合、そもそも停止という機能が存在していないため、本来の停止手順としては

  1.  一世代前の Snapshot の削除
  2. Snapshot の作成
  3. Instance の Delete

で、起動時には

  1. Snapshot からの RDS 作成
  2. セキュリティグループやパラメータグループ付け替え
  3. RDS インスタンスの再起動

という感じになると思う。
Snapshotをいつ削除するのかは好みなのかもしれないが、作成直前まで保持しておけば
バックアップとしても使えるし、もう少し手を加えて世代管理をしてもいいと思う。

EC2 と異なり、RDS は Snapshot からの起動となるので、手操作による名前付けミスは避けたい。
そういう意味では、起動まで自動化したくなるところではあるかな。

ただ、そもそも RDS の一覧が取れないように感じる。

[stopall-rds.rb]
require "aws-sdk"
rds = AWS::RDS.new(
  :access_key_id => <ACCESS_KEY_ID>,
  :secret_access_key => <SECRET_ACCESS_KEY>,
  :rds_endpoint => <RDS_ENDPOINT_URL>
)

rds.db_instances.each {|instance|
  p instance.endpoint_address
}

 

試しに Github 上で見かけたコードを元にこんなのを書いてみたけど、接続の時点で

C:/Ruby200-x64/lib/ruby/2.0.0/net/http.rb:878:in `initialize': execution expired (Net::OpenTimeout)
from C:/Ruby200-x64/lib/ruby/2.0.0/net/http.rb:878:in `open'
from C:/Ruby200-x64/lib/ruby/2.0.0/net/http.rb:878:in `block in connect'
from C:/Ruby200-x64/lib/ruby/2.0.0/net/http.rb:877:in `connect'
from C:/Ruby200-x64/lib/ruby/2.0.0/net/http.rb:862:in `do_start'
from C:/Ruby200-x64/lib/ruby/2.0.0/net/http.rb:857:in `start'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/aws-sdk-v1-1.52.0/lib/aws/core/http/connection_pool.rb:321:in `start_s
ession'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/aws-sdk-v1-1.52.0/lib/aws/core/http/connection_pool.rb:125:in `session
_for'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/aws-sdk-v1-1.52.0/lib/aws/core/http/net_http_handler.rb:55:in `handle'

from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/aws-sdk-v1-1.52.0/lib/aws/core/client.rb:253:in `block in make_sync_re
quest'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/aws-sdk-v1-1.52.0/lib/aws/core/client.rb:289:in `retry_server_errors'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/aws-sdk-v1-1.52.0/lib/aws/core/client.rb:249:in `make_sync_request'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/aws-sdk-v1-1.52.0/lib/aws/core/client.rb:511:in `block (2 levels) in c
lient_request'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/aws-sdk-v1-1.52.0/lib/aws/core/client.rb:391:in `log_client_request'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/aws-sdk-v1-1.52.0/lib/aws/core/client.rb:477:in `block in client_reque
st'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/aws-sdk-v1-1.52.0/lib/aws/core/client.rb:373:in `return_or_raise'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/aws-sdk-v1-1.52.0/lib/aws/core/client.rb:476:in `client_request'
from (eval):3:in `describe_db_instances'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/aws-sdk-v1-1.52.0/lib/aws/rds/db_instance_collection.rb:56:in `_each_i
tem'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/aws-sdk-v1-1.52.0/lib/aws/core/collection/with_limit_and_next_token.rb
:54:in `_each_batch'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/aws-sdk-v1-1.52.0/lib/aws/core/collection.rb:80:in `each_batch'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/aws-sdk-v1-1.52.0/lib/aws/core/collection.rb:47:in `each'
from stop-rds.rb:11:in `<main>'

のようにタイムアウトしてしまう。

と、ここで RDS 側の Firewall 関連の設定じゃないかと気づく。
RDS の構築を自分で行った環境ではなかったので気づくのが遅れた。

RDS を構築する際、 “Enable Public Access” って項目があって、通常は同じ VPC 内からのアクセスのみ許すようになっていた。
一時的にこれを変更して試してみたら、エラー内容が変化した

C:/Ruby200-x64/lib/ruby/2.0.0/net/http.rb:918:in `connect': SSL_connect SYSCALL returned=5 errno=0 state=SSLv2/v3 read server hello A (OpenSSL::SSL::SSLError)
from C:/Ruby200-x64/lib/ruby/2.0.0/net/http.rb:918:in `block in connect'
from C:/Ruby200-x64/lib/ruby/2.0.0/timeout.rb:66:in `timeout'
from C:/Ruby200-x64/lib/ruby/2.0.0/net/http.rb:918:in `connect'
from C:/Ruby200-x64/lib/ruby/2.0.0/net/http.rb:862:in `do_start'
from C:/Ruby200-x64/lib/ruby/2.0.0/net/http.rb:857:in `start'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/aws-sdk-v1-1.52.0/lib/aws/core/http/connection_pool.rb:321:in `start_session'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/aws-sdk-v1-1.52.0/lib/aws/core/http/connection_pool.rb:125:in `session_for'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/aws-sdk-v1-1.52.0/lib/aws/core/http/net_http_handler.rb:55:in `handle'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/aws-sdk-v1-1.52.0/lib/aws/core/client.rb:253:in `block in make_sync_request'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/aws-sdk-v1-1.52.0/lib/aws/core/client.rb:289:in `retry_server_errors'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/aws-sdk-v1-1.52.0/lib/aws/core/client.rb:249:in `make_sync_request'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/aws-sdk-v1-1.52.0/lib/aws/core/client.rb:511:in `block (2 levels) in client_request'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/aws-sdk-v1-1.52.0/lib/aws/core/client.rb:391:in `log_client_request'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/aws-sdk-v1-1.52.0/lib/aws/core/client.rb:477:in `block in client_request'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/aws-sdk-v1-1.52.0/lib/aws/core/client.rb:373:in `return_or_raise'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/aws-sdk-v1-1.52.0/lib/aws/core/client.rb:476:in `client_request'
from (eval):3:in `describe_db_instances'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/aws-sdk-v1-1.52.0/lib/aws/rds/db_instance_collection.rb:56:in `_each_item'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/aws-sdk-v1-1.52.0/lib/aws/core/collection/with_limit_and_next_token.rb:54:in `_each_batch'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/aws-sdk-v1-1.52.0/lib/aws/core/collection.rb:80:in `each_batch'
from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/aws-sdk-v1-1.52.0/lib/aws/core/collection.rb:47:in `each'
from stop-rds.rb:17:in `<main>'

 

はて・・・。困ったな。

ちなみに、

rds.db_instances["<INSTANCE_ID>"]

という形では AWS:RDS:DBInstance オブジェクトへアクセスできる。ここから delete を呼び出すことは出来そうなので、ID を指定すればできそうだ。

逆に、 EC2 のように each で無条件にインスタンスを落とすことは出来ないのかもしれない。

AWS:RDS 以外に AWS:RDS:Client も RDS の操作する手段としてはあるので、こちらを使えばもしかしたらいけるのかな?
エラーとなっている、 db_instance_collection の each_item 実装を見てみると client.describe_db_instances を呼び出しているみたいなので、
AWS:RDS:Client の使い方を習熟することがエラー解決への近道なのかもしれない。

まぁ、実際の問題としては RDS がそんなに増えることはないだろうから、とりあえずこれでやってみようかな・・・。

なんか、もうちょっとうまいこと行くとうれしいんだが。。。