カテゴリー
technology

【CDP Advent Calendar 2012】Self Server Registrationパターン(仮称)

CDP logo
17日目担当のゴトウです。当初は勢い余って「こんなCDPはいやだ」というタイトルで参戦しようとしたCDP Advent Calendarですが、みなさんマジメにやられているようなのでひよってしまい、まぁまぁ使える感じや〜つに変更することにしました。
さてAWS環境を有効に活用している多くのシステムでは、インスタンスの自動起動やオートスケールなど、予めサーバー台数や名前が決まってないことが多いです。そんなときでも現状起動中のインスタンス一覧を参照できないといけないケースが多々あります。
たとえばオートスケールする環境でも監視ツールによるモニタリングが必要な場合。または同様にオートスケールする環境でELBではないロードバランサーを利用して負荷分散しているケース。いずれのケースも設定ファイルに現在稼働しているインスタンスのホスト名ないしはIPアドレス一覧を更新する必要があります。
そのための元ネタとしてSimpleDBを使って起動中のインスタンスメタデータを保管する方法を実装したのでPHPのサンプルコードと共にご紹介します。いまいちいい名前が思いつかないのですが、名付けて Self Server Registrationパターン。(気に入ってないので名前は募集します!)


以下のようなファイルを用意します。
■config.php – 各種スクリプト用AWS設定ファイル

<?php
define('AWSAccessKeyId', '<your Access Key Id HERE!>');
define('AWSSecretKey', '<your Security Key HERE!>');
define('REGION', AmazonSDB::REGION_TOKYO);
define('DOMAIN', 'server-list');
?>

■add-server.php – 自身のメタデータを登録するスクリプト

#!/usr/bin/php
<?php
require_once("sdk/sdk.class.php");
require_once("config.php");
if ($argc < 6) { print("Usage: $argv[0] <instance-id> <instance-type> <availability-zone> <public-hostname> <local-ipv4> <status>\n"); exit(1); }
$sdb = new AmazonSDB(array(
"key" => AWSAccessKeyId,
"secret" => AWSSecretKey
));
$sdb->set_region(REGION);
$res = $sdb->put_attributes(DOMAIN, $argv[1], array(
'instance-type' => $argv[2],
'availability-zone' => $argv[3],
'public-hostname' => $argv[4],
'local-ipv4' => $argv[5],
'status' => $argv[6]
), true);
if($res->isOK()){
# print_r($res);
}
else{
# print_r($res);
print("Failed.\n");
exit(1);
}
?>

■delete-server.php – 自身のメタデータを削除するスクリプト

#!/usr/bin/php
<?php
require_once("sdk/sdk.class.php");
require_once("config.php");
if ($argc <= 1) { print("Usage: $argv[0] <instance-id>\n"); exit(1); }
$sdb = new AmazonSDB(array(
"key" => AWSAccessKeyId,
"secret" => AWSSecretKey
));
$sdb->set_region(REGION);
$res = $sdb->delete_attributes(DOMAIN, $argv[1]);
if($res->isOK()){
# print_r($res);
}
else{
# print_r($res);
print("Failed.\n");
exit(1);
}
?>

■/etc/init.d/self-server-registration – インスタンス起動・停止時用スクリプト

#!/bin/bash
#
# EC2 instance metadata self server regitration
#
# chkconfig: 2345 60 60
# description: self server registration script to manage & list current running EC2 instances
#
# source function library
. /etc/rc.d/init.d/functions
RETVAL=0
lockfile=/var/lock/subsys/self-server-registration
srcdir=/opt/self-server-registration
endpoint=http://169.254.169.254/latest/meta-data
get_meta_data() {
ID=`curl -s ${endpoint}/instance-id`
TYPE=`curl -s ${endpoint}/instance-type`
AZ=`curl -s ${endpoint}/placement/availability-zone`
DNSNAME=`curl -s ${endpoint}/public-hostname`
IP=`curl -s ${endpoint}/local-ipv4`
}
start() {
if [ ! -f ${srcdir}/config.php ]; then
echo "${srcdir}/config.php does NOT exist."
failure
fi
echo
if [ -x ${srcdir}/add-server.php ]; then
echo -n $"Running: add-server.php "
${srcdir}/add-server.php $ID $TYPE $AZ $DNSNAME $IP 'running' && success || failure
echo
fi
touch ${lockfile}
}
stop () {
if [ ! -f ${srcdir}/config.php ]; then
echo "${srcdir}/config.php does NOT exist."
failure
fi
echo
if [ -x ${srcdir}/delete-server.php ]; then
echo -n $"Running: delete-server.php "
${srcdir}/delete-server.php $ID && success
echo
fi
rm -f ${lockfile}
}
case "$1" in
start)
get_meta_data
start
;;
stop)
get_meta_data
stop
;;
restart)
get_meta_data
stop
start
;;
*)
echo $"Usage: $0 {start|stop|restart}"
RETVAL=1
esac
exit $RETVAL

以下を前提としていますのでご注意を。
・config.php中のAWSAccessKeyIdとAWSSecretKeyを自身のものへ書き換え必須
・config.php中のDOMAINは好きなものに変更
・add-server.phpとdelete-server.phpは実行権限付きで/opt/self-server-registrationに設置されている
・self-server-registrationは実行権限付きで/etc/init.dに設置されている
テスト実行をするのであればドメインを作成してから、以下のようにコマンド実行してください。
(ドメイン作成スクリプトはサンプルがgithubにおいてあります)

$ sudo /etc/init.d/self-server-registration start
Running: add-server.php [ OK ]

SimpleDBで指定してドメインの中をみてましょう。RazorSQLを使って確認してみるとこうなってます。
RazorSQL - 1台分のデータ登録完了
同様に、削除してみましょう。

$ sudo /etc/init.d/self-server-registration stop
Running: delete-server.php [ OK ]

RazorSQL - 無事消えます
当然消えてますね。ここまでくれば設定はほぼ完了。最後に以下のようなコマンドを実行して起動・停止スクリプトとして登録します。

$ sudo chkconfig --add self-server-registration
$ sudo chkconfig --list | grep self-server-registration
self-server-registration0:off1:off2:on3:on4:on5:on6:off
$ ls -al /etc/rc3.d/ | grep self-server-registration
lrwxrwxrwx 1 root root 34 12月 17 11:15 2012 S60self-server-registration -> ../init.d/self-server-registration

あとはRebootしても、Stop→Startしても正確な情報が保存されることが確認できると思います。特にStop→Startの場合はIPが変わるので情報がよくわかると思います。
さてそれでは複数台起動してみましょう。自前で作成したAMIを元にして…
自前AMIを起動
10台起動します。
10台同時起動します
無事10台起動しました。
無事10台起動されました
一方RazorSQLで確認してみると…
RazorSQL - 当然10台分のデータが登録されてます
無事10件あることが確認できました。
今度は停止してみましょう。5台停止してみると
5台停止してみます
無事に5件になっていることが確認できます。
RazorSQL - データも5台に減りました
あとはこの状態から必要なフォーマットにあわせて出力して使うことになります。
とりあえずダンプでよければ…

$ ./list-servers.php
sql = SELECT * FROM `server-list`
CFSimpleXML Object
(
[Item] => Array
(
[0] => CFSimpleXML Object
(
[Name] => i-06157105
[Attribute] => Array
(
[0] => CFSimpleXML Object
(
[Name] => availability-zone
[Value] => ap-northeast-1a
)
[1] => CFSimpleXML Object
(
[Name] => public-hostname
[Value] => ec2-54-248-189-143.ap-northeast-1.compute.amazonaws.com
)
[2] => CFSimpleXML Object
(
[Name] => status
[Value] => running
)
[3] => CFSimpleXML Object
(
[Name] => local-ipv4
[Value] => 10.128.21.18
)
[4] => CFSimpleXML Object
(
[Name] => instance-type
[Value] => t1.micro
)
)
)
[1] => CFSimpleXML Object
(
[Name] => i-0a157109
[Attribute] => Array
(
[0] => CFSimpleXML Object
(
[Name] => availability-zone
[Value] => ap-northeast-1a
)
[1] => CFSimpleXML Object
(
[Name] => public-hostname
[Value] => ec2-54-248-199-208.ap-northeast-1.compute.amazonaws.com
)
[2] => CFSimpleXML Object
(
[Name] => status
[Value] => running
)
[3] => CFSimpleXML Object
(
[Name] => local-ipv4
[Value] => 10.128.29.151
)
[4] => CFSimpleXML Object
(
[Name] => instance-type
[Value] => t1.micro
)
)
)
[2] => CFSimpleXML Object
(
[Name] => i-0815710b
[Attribute] => Array
(
[0] => CFSimpleXML Object
(
[Name] => availability-zone
[Value] => ap-northeast-1a
)
[1] => CFSimpleXML Object
(
[Name] => public-hostname
[Value] => ec2-54-248-183-207.ap-northeast-1.compute.amazonaws.com
)
[2] => CFSimpleXML Object
(
[Name] => status
[Value] => running
)
[3] => CFSimpleXML Object
(
[Name] => local-ipv4
[Value] => 10.128.20.135
)
[4] => CFSimpleXML Object
(
[Name] => instance-type
[Value] => t1.micro
)
)
)
[3] => CFSimpleXML Object
(
[Name] => i-0e15710d
[Attribute] => Array
(
[0] => CFSimpleXML Object
(
[Name] => availability-zone
[Value] => ap-northeast-1a
)
[1] => CFSimpleXML Object
(
[Name] => public-hostname
[Value] => ec2-54-248-25-102.ap-northeast-1.compute.amazonaws.com
)
[2] => CFSimpleXML Object
(
[Name] => status
[Value] => running
)
[3] => CFSimpleXML Object
(
[Name] => local-ipv4
[Value] => 10.128.19.95
)
[4] => CFSimpleXML Object
(
[Name] => instance-type
[Value] => t1.micro
)
)
)
[4] => CFSimpleXML Object
(
[Name] => i-04157107
[Attribute] => Array
(
[0] => CFSimpleXML Object
(
[Name] => availability-zone
[Value] => ap-northeast-1a
)
[1] => CFSimpleXML Object
(
[Name] => public-hostname
[Value] => ec2-54-248-26-190.ap-northeast-1.compute.amazonaws.com
)
[2] => CFSimpleXML Object
(
[Name] => status
[Value] => running
)
[3] => CFSimpleXML Object
(
[Name] => local-ipv4
[Value] => 10.128.19.111
)
[4] => CFSimpleXML Object
(
[Name] => instance-type
[Value] => t1.micro
)
)
)
)
)

こんな感じですね。ちなみにcloudpackではこいつを整形してNagiosやHAProxyなんかへ利用しています。
サンプルソースはgithubにおいてありますので、適当にカスタマイズして使ってみてください。(環境はAmazon Linuxを前提としています。PHPのAWS SDKは各種PHPスクリプトと同一ディレクトリ内に sdk/ でアクセス可能な状態で設置してください。)
明日は @k_yokokura さんですね。
それでは!