電子の密林を開拓する

・サーバ構成

AmazonLinuxをOSとしてphpコンテンツ用のスタンドアロンサーバーを作ってみます。
DBとメール送受信を利用するプログラムが動作することを想定しています。

構成は以下の通り。
  • Tokyo リージョンを利用
  • EC2 micro (non-VPC)
  • AmazonLinux 32bit  (64bitより、使用メモリ量が少なくて済むハズ)
  • EIP を利用
  • DNS は DDNS を利用
ミドルウェアは以下のものを使用します。
  • Aapache 2.x~
  • php 5.3.x~ (5.3.x 以上であれば何でもOKのツモリ)
  • MySQL 5.x (基本的なSQLしか使わないので、バージョンは気にしない)
  • Postfix (バージョンは…気にしたことないです)


・セキュリティグループ作成
今回はセキュリティグループを複数利用したいので、先行して、GUIのインスタンス設定画面で以下のようなセキュリティグループを作成しておきます。
名前 = 解放ポート -- 許可するアクセス元
-------------------------------------------------
web = tcp 80,443 -- 0.0.0.0/0
mail = tcp 25 -- 0.0.0.0/0
ssh = tcp 22 -- 自宅のIP

※自宅に固定IPを持っていないなら 0.0.0.0/0 で...
セキュリティグループを用途ごとに分割しておけば、後で変更が容易になる…と想像しています(^^;


・EC2インスタンス作成
特に難しい手順は必要ないので、サクっと作成します。
作成時に、先ほど登録しておいた三つのセキュリティグループを指定するのを忘れないようします。
後から追加は出来ないので…。
EC2 micro のインスタンスが作成できたら、EIPを割り当てておきます。


・DynamicDNSを登録する
no-ip.biz というサービスを利用してDynamicDNSを登録します。
no-ip.biz でアカウント作成後、操作画面経由で適当なサプドメインを発行してもらい、AWS で作成した EIP のアドレスを登録しておきます。
しかし、無料サービスの範囲では A、MX レコードは登録できるものの TXT (SPF)レコードの登録は出来ないようです。
むむむ…。まぁ、メールが送れないわけではないので、TXT(SPF)は未設定のままでも良いでしょう。
A   = 【AWSのEIP】
MX = 【発行されたホスト名】.no-ip.biz
TXT =  無料版のno-ip.bizでは利用不能

・AWS EC2 へ ssh ログイン
no-ip.biz だと、登録と同時に使えるようになったので、さっそくその名前でsshログインしてみます。
ssh -l ec2-user  ~.no-ip.biz
特に困ることもなくログインできます。


・Linuxアカウントを設定する
今回は、別の開発者がAWSにsshログインする予定なので、そのユーザーのためのアカウントを作成します。
まず、アカウント名が重複しないように /etc/passwd を確認し、重複しないアカウント名を考えます。
ここでは、(ec2-user を真似て) dev-user という識別子のアカウントを作成します。
ec2-user$ sudo adduser dev-user

ec2-user$ sudo su - dev-user

dev-user$ pwd
/home/dev-user

dev-user$ groups
dev-user

dev-user$ exit
logout

adduserコマンドに対して何も応答が無いのが不気味ですが、一応 dev-user というアカウントは出来たようです。

ssh 経由で dev-user がログインできるように、ssh公開鍵を追加しておきます。
ec2-user$ sudo su - dev-user

dev-user$ ls
dev-user$ mkdir ~/.ssh
dev-user$ cd ~/.ssh
dev-user$ ls -la
total 8
drwxrwxr-x 2 dev-user dev-user 4096 Jul 31 09:58 .
drwx------ 3 dev-user dev-user 4096 Jul 31 09:58 ..

dev-user$ chmod og-w .
dev-user$ echo '【SSH公開鍵】' > authorized_keys
dev-user$ chmod og= authorized_keys
dev-user$ ls -la
total 12
drwxr-xr-x 2 dev-user dev-user 4096 Jul 31 09:59 .
drwx------ 3 dev-user dev-user 4096 Jul 31 09:58 ..
-rw------- 1 dev-user dev-user 231 Jul 31 09:59 authorized_keys

dev-user$ exit
logout
設定出来たら、ssh -l dev-user ~.no-ip.biz などとして、sshログインできることを確認しておいてください。


・ミドルウェアをインストールする
以下のコマンドでインストール済みパッケージをアップデートし、さらにインストール済みパッケージも確認しておきます。
yum update
yum list installed
基本的なパッケージしかインストールされていないようで、httpd, mta 等はリストにありませんでした。
とりあえず、ハッキリ必要だと判明しているパッケージをインストールします。
httpd.i686 : Apache HTTP Server
php.i686 : PHP scripting language for creating dynamic web sites
mysql.noarch : MySQL meta package
mysql-server.noarch : The MySQL server and related files
postfix.i686 : Postfix Mail Transport Agent
sudo yum install …で上記パッケージをインストールすれば、特に指定しなくても php-cli なども依存関係としてインストールされるようです…。
これで、以下のモノがインストールされました。
Apache2.2.25
php5.3.27
MySQL5.5.31 (server含む)
Postfix2.6.6
特に期待から外れるようなバージョンにはならなかったので、コレでOKということにします。


・そのほかのツール
なぜか telnet クライアントがインストールされていないので、コレをインストールしておきます。
sudo yum install telnet.i686
Apache の動作確認などで使うので… (curlでも困らないけど…)。


・Apacheの設定

AmazonLinuxでは、Apache用 httpd.conf は /etc/httpd/conf に配備されているようです。
早速確認して編集します。
…と思いましたが、それ以前に apache が起動されていないようです。起動して、動作確認しておきましょう。
$ sudo apachectl -k start
$ curl 'http://localhost/'
apachectl が無反応なのが気になりますが、一応、期待通りに起動しました。

まず、初期からインストールされている「ようこそ」のページを表示している ~/conf.d/welcome.conf を削除します。
$ cd /etc/httpd/conf.d/
$ sudo rm welcome.conf
次に、httpd.conf 内で用意されている DocumentRoot を消去します。単純に以下の部分を消去します。
DocumentRoot "/var/www/html"

<Directory />

</Directory>
名前ベースのVirtualHostを使用するため、NameVirtualHost タグを httpd.conf 内に設定します。
NameVirtualHost *:80

DocumentRoot の代わりに、VirtualHost 用の /etc/httpd/conf.d/virtualhost.conf ファイルを作成して、先頭にカラッポの VirtualHost を定義します。専用のDocumentRoot ディレクトリも作成しておきます。
$i sudo mkdir /var/www/default

$ cat /etc/httpd/conf.d/virtualhost.conf

<VirtualHost *:80>
ServerName example.com
ServerAdmin webmaster@example.com

DocumentRoot "/var/www/default"
<Directory />
Order deny,allow
deny from all
Options None
AllowOverride None
</Directory>

LogLevel info
CustomLog /dev/null combined
ErrorLog /dev/null
</VirtualHost>

# END of FILE #
この「最初のVirtualHost」がワーム等のアクセスを引き受けてくれるようになるので、ゴミのようなログが出なくて済むようになります。無駄なログが出力されてもよいのであれば、このVirtualHostは不要ですが…。


ここから実際に使用されるハズの VirtualHost を定義しますが、その前に ログフォーマットを規定します。httpd.conf に、以下のような設定を追記します。
LogFormat "%V\t%{%Y-%m-%d %H:%M:%S}t\t%h\t%u\t%r\t%>s\t%b\t%D\t%I\t%O\t%{Referer}i\t%{User-Agent}i\t%{HOST}i" tsvlog
このフォーマットは TAB 区切りにしているだけなので、別の形式が良いのなら、適当に変更ください。

で、作成したログフォーマットを使用して、 VirtualHost を作成します。先ほどの virtualhost.conf の末尾に、以下のような設定を追記します。
<VirtualHost *:80>
ServerName ~.no-ip.biz
ServerAdmin webmaster@~.no-ip.biz

LogLevel warn
CustomLog "| /usr/sbin/rotatelogs /var/log/httpd/~.acc.%Y-%m-%d 86400 +540" tsvlog
ErrorLog "| /usr/sbin/rotatelogs /var/log/httpd/~.err.%Y-%m-%d 86400 +540"

DocumentRoot /var/www/~
<Directory />
AllowOverride None
Options None
</Directory>

<Directory "/var/www/~">
AllowOverride None
Options None
</Directory>
</VirtualHost>
「~」の部分は、自分のホスト名に変更して使用ください。

これで apache を再起動すれば、~.no-ip.biz でアクセスすれば /var/www/~ を DocumentRoot とする 名前ベースのバーチャルホストにアクセスできます。Apacheの再起動は以下の通り。
$ sudo apachectl -k restart


あと、予備的に、httpd.conf で設定されている AddDefaultCharset UTF-8 も消しておきます。
主に php プログラムで利用するので、文字コードをphp側で明示的に指定させるためです。





ここまでで 名前ベースの VirtualHost を利用する Apache が設定できた……ツモリです。


次回は、php (mod_php)を設定します。



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のセキュリティグループを復活させたい!」という人には良いかもしれません(^^;


今回ここまで。
 

このページのトップヘ