電子の密林を開拓する

2013年04月

AWS EC2インスタンス上でメールサーバを構築する実験をしている最中、どうしても「今まで使用したことが無いEIPが欲しい」という状況に遭遇しました。
…とは言っても、EIP確保コマンド(ec2-allocate-address)に、そんなオプション指定は存在しないので……何度もEIP確保を繰り返して、気に入らなければ解放する…という作業になります。

ハッキリ言ってメンドクサイので、自動化しました。AmazonLinux + ruby 1.8.7 (2012-10-12 patchlevel 371) [i386-linux] で動作確認しています。

・allocate_flesh_address.rb
#!/usr/bin/ruby
# encoding: utf8

# 2013-04-09 Original Coding by ExploreAWS (http://exploreaws.doorblog.jp/)

require "time"
require "open3"
require "ipaddr"
require "optparse"

DEFAULT_WAIT_TIMER = 10 # as secconds
INCREMENT_WAIT_TIMER = 60 # increment at retry
MAX_WAIT_TIMER = (60*5) # as secconds
MAX_RETRY_COUNT = 99 #

ALLOCATOR_COMMAND = 'ec2-allocate-address' ## AWS ec2 command
ALLOCATOR_VPC_COMMAND = 'ec2-allocate-address --domain vpc' ## AWS ec2 command
DEALLOCATOR_COMMAND = 'ec2-release-address' ## AWS ec2 command
DEALLOCATOR_VPC_COMMAND = 'ec2-release-address --allocation-id' ## AWS ec2 command
UNFAVORITE_IP_DIR = 'unfavorite_ipv4/'


################################################################################
## PRINT LOG
def printLog( msg )
print Time.now.strftime( "%Y-%m-%d %H:%M:%S %Z" ) + " : " + msg
end


################################################################################
## GET A LIST OF UNFAVOTITE IPv4 ADDRESSES
def getUnfavoriteIpList() ## RET: Array of string (as IPv4s)
tFile = Dir::entries( UNFAVORITE_IP_DIR )
tIp = Array.new()
tFile.each do |v|
v.strip!
v.sub!( %r!^.*/!, '' ) ## remove the path string before its filename.
next if v.length < 8
begin
if (IPAddr.new( v ).ipv4?) # trash unless it's a valid IPv4.
tIp.push( v )
end
rescue => err
## Nothing to do... just ignore error ##
end
end
return tIp
end

################################################################################
## DEALLOCATE A IPv4, AND GIVE IT BACK TO AWS
def deallocateIp( ip, allocation_id = nil ) ## RET: ture / false
command = if (allocation_id.nil?)
DEALLOCATOR_COMMAND + ' ' + ip
else
DEALLOCATOR_VPC_COMMAND + ' ' + allocation_id
end
return system( command + ' > /dev/null')
end


################################################################################
## ALLOCATE A IPv4from AWS
def allocateIp( fVPC = false ) ## RET: string ip, string allocation_id
strError = nil
strResult = nil

command = (fVPC ? ALLOCATOR_VPC_COMMAND : ALLOCATOR_COMMAND )
Open3.popen3( command ) do |stdin, stdout, stderr|
stdin.close() # no need...
strError = stderr.read()
strResult = stdout.readlines()
end

if (strError.length > 0)
raise "#{command} returns an error. \n" + strError
end

if (strResult.length != 1 || !strResult[0].match( /^ADDRESS/ ))
raise "#{command} returns unknown result... \n" + strResult
end

t = strResult[0].split( "\t" )
address = t[1].strip
allocation_id = (t[4].strip rescue nil)

ip = nil
begin
ip = IPAddr.new( address )
if (! ip.ipv4?) then raise "#{address} is not a valid IPv4 address." end
rescue => err
raise err
end

allocation_id = nil if (allocation_id.length <= 0) ## regulate...

return address, allocation_id
end



################################################################################
## main

# initialize
fVPC = false
tUnfavoriteIp = getUnfavoriteIpList()

# options
opt = OptionParser.new()
opt.on( '-vpc' ) { fVPC = true }
opt.on( '--vpc' ) { fVPC = true }
opt.parse!( ARGV )


# main loop
#begin
nWaitTimer = DEFAULT_WAIT_TIMER
nRetryCount = 0
while( true ) do
ip, allocation_id = allocateIp( fVPC )
printLog "IP - " + ip
if (!tUnfavoriteIp.include?( ip ))
print " ... appreciated! \n"
break
end

print " ... but I hate it. \n"
deallocateIp( ip, allocation_id )

nRetryCount += 1
if (nRetryCount > MAX_RETRY_COUNT)
raise "Give up!"
end

sleep( nWaitTimer )
nWaitTimer += INCREMENT_WAIT_TIMER
nWaitTimer += MAX_WAIT_TIMER if (MAX_WAIT_TIMER < nWaitTimer)
end
#rescue => err
# print "\n"
# puts err
# exit( -1 )
#end


# End of Main
exit( 0 )

# END of FILE #


使い方は、以下の通り。
  1. 気に入らないEIPのIPアドレスをファイル名として ./unfavorite_ipv4/ ディレクトリ内に touch しておく。
  2. ruby スクリプトを実行する。VPC用 EIPが必要な場合は --vpc オプションを指定しておく。


実際のコマンドだと、こんな感じになります。

$ ls allocate_flesh_address.rb
allocate_flesh_address.rb


$ mkdir  unfavorite_ipv4/
$ touch unfavorite_ipv4/54.248.98.67

$ touch unfavorite_ipv4/54.248.98.92


$ ruby allocate_flesh_address.rb
2013-04-09 11:54:53 UTC : IP - 54.248.98.67 ... but I hate it.

2013-04-09 11:55:05 UTC : IP - 54.248.98.92 ... but I hate it.
2013-04-09 11:56:17 UTC : IP - 54.248.98.67 ... but I hate it.
2013-04-09 11:58:29 UTC : IP - 54.248.98.67 ... but I hate it.
2013-04-09 12:01:42 UTC : IP - 54.248.98.67 ... but I hate it.
2013-04-09 12:05:54 UTC : IP - 54.248.246.65 ... appreciated!


$ ec2-describe-addresses
ADDRESS 54.248.246.65           standard


そんなに高度なものではありませんが、人力で作業するより全然マシになりました(^^;


AWSの中の人に見られたら「そんなEIP確保の仕方はダメだ!」と怒られるかもしれませんが…(x_x;




参考まで…。


今回は、AWS OpsWoks が残した Security Group 群を CLI でサクっと消去してみます。
また、消去したセキュリティグループを OpsWorks 自身に再生成させる方法も記載しています。


・OpsWorksが残す残骸...
AWS自身が提供する機能で OpsWorks というものがあり、簡単な手順で WEBサーバやロードバランサー、DBなど準備できるようです。チラっとしか使っていないので、それぐらいしか分からないのですが…。

しかし、使ってみたは良いものの、セキュリティグループに大量の設定が残されてしまいました。

※IAM Role も残されていますが、IAM は未だ使っていないので気にしていないです(^^;



・AWS OpsWorks のスタックを消去する
ここではOpsWorksをCLIで操作するわけではないので、残っているOpsWorksのスタックがあるようなら、AWS Management Console で消去しておいてください。
「利用中のセキュリティグループは消去できない」という制約があるので、スタックが残っているとセキュリティグループが消去できないのです…。


・AWS OpsWorks が作成したセキュリティグループを特定する
OpsWorks自身は Region を超えて使用されるものらしく、すべての Region にセキュリティグループが作成されています。
まず、全 Region に設定されているセキュリティグループから AWS-OpsWorks- で始まる名前のものを抽出します。
$ ec2-describe-regions \
     | cut --field=2 \
     | xargs -i \
          ec2-describe-group \
          --region {} \
          --filter 'group-name=AWS-OpsWorks-*' \
     | grep '^GROUP'

OpsWorksを使ったことがあるのなら、大量のセキュリティグループが表示されるハズです。
⇒ ルールも確認したいなら、最後の grep の行を外してください。



AWS OpsWorks が作成したセキュリティグループを消去する
ここから先は bash の 1ライナーで処理するのは困難で、見栄えも悪いので bash のバッチ化します。
#!/bin/bash

REGIONS=`ec2-describe-regions | cut --field=2`

for R in ${REGIONS}
do
  ec2-describe-group --region ${R} \
                     --filter 'group-name=AWS-OpsWorks-*' \
  | grep '^GROUP' \
  | cut --field=2 \
  | xargs -i  ec2-delete-group --region ${R}  {}
done
バッチ化しておきながら、ほとんど1ライナー…。
まぁ、動けばよいんです!!!


実行すると、以下のようなエラーがガンガン出力されます。
Client.InvalidGroup.InUse: Group 488224276535:AWS-OpsWorks-Memcached-Server is used by groups: 488224276535:AWS-OpsWorks-Monitoring-Master-Server
エラーが出るのはセキュリティグループ同士に依存関係があるからで、他から依存されているものは消せない…というコトです。
とりあえず、エラーを無視して何度か実行し続けると、そのうち全部消えてしまいます。



・改善案
ec2-delete-group のエラー出力を解析すれば、依存しているセキュリティグループが分かるので、先にソチラを消去する…というように改修すれば、エラーを見ることなくキレイにセキュリティグループを消去できると思われます。
もっとも、このバッチプログラム自体、そんなに頻繁に使うものでもなく、また何度か実行すれば 期待通りの動作になるため、そこまで対応する必要は無いのかなー…と思ったり。



・問題発生!!!
消すのはいいけど、復活できない!!! (x_x;
AWS OpsWorks の Security Group を消してしまうと、AWS Management Conolse から OpsWorks を差ウ指しても、セキュリティグループを復活させることが出来ないようです。

AWS公式のフォーラムなどを読むと、「新しいスタックを作成された時」「完全新規のIAMロールがスタックに適用された時(使用された時)」に、セキュリティグループが再生成されるらしいのですが…。
とりあえず、困ってないなら OpsWorks が作成した セキュリティグループは消去しない方が良いかもしれません。
そのうち、AWS側でセキュリティグループを再生成する方法を提供してくれるかもしれませんが…。

↓↓↓

何度かトライ&エラーを繰り返したところ、AWS OpsWorks が使用するセキュリティグループの復元に成功しました☆ AWS Management Conolse の OpsWokrs から操作します。

  1. OpsWorks の Add Stack メニューから、新しいスタックをデフォルト設定のまま作成する(作成したスタックは使わないので、何でもOK)
  2. もう一度、OpsWorks の Add Stack メニューから、二つ目の新しいスタックを作成する画面を呼び出す(続く)。
  3. 二つ目のスタックを作成する際、「Advanced≫」オプションから「IAM Role ⇒ New IAM Role」「Default IAM Instance Profile ⇒ New IAM Instance Profile」と指定しておく。
opsworks_stack

スタックが生成されてからしばらく(数分?)待つと、IAM Role と SecurityGroup がじわじわ作成されていきます。
一通りセキュリティグループが生成されてしまえば、IAM Role もスタックも削除して構わない…ハズ。


IAM Role については「aws-opsworks-ec2-role」と「aws-opsworks-service-role」があれば足りるようです。二番目のスタックを生成する際に「aws-opsworks-ec2-role.何か数字列」「aws-opsworks-service-role.何か数字列」というIAMが作成されているので、それらを消してしまえばよいでしょう。

opsworks_iam




役に立つのか立たないのか分からないビミョーな内容ですが、「OpsWorksを試したら、セキュリティグループがたくさんできて気持ち悪い!!!」あるいは「消してしまった OpsWorksのセキュリティグループを復活させたい!」という人には良いかもしれません(^^;


今回ここまで。
 

このページのトップヘ