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

  • 投稿日:
  • by
  • カテゴリ:

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 さんですね。
それでは!