今回は、Cloud Design Pattern(CDP)の記事になります。
対象は「Scheduled Autoscalingパターン」です。

このパターンの注意点に下記のような記載があります。

バッチ処理が終了する時刻を決めるのが難しい場合は、EC2上のバッチ処理が終了してから、EC2自身が自分を終了する
作り込みを行う方法もよく用いられる。

今回は、上記の方法を実際に作り込んでみました。

はじめに、Auto Scalingにてスケールアウトに使われるAMIの作成です。
このAMIはEC2起動時にバッチ処理を開始し、バッチ処理が終了する時に自分自身(EC2)もターミネートされるようにする必要があります。

具体的には、起動時にバッチ処理(/opt/suz-batch/bin/run-batch)が開始されるように、下記の起動スクリプト(/etc/init.d/suz-batch)を用意します。

#!/bin/bash
#
# suz-batch        Run Batch.
#
# chkconfig: 2345 99 10
# description: Run Batch

# Source function library.
. /etc/init.d/functions

prog=suz-batch
lock=/var/lock/subsys/$prog
log=/opt/suz-batch/log/error.log

# Source config
if [ -f /etc/sysconfig/$prog ] ; then
    . /etc/sysconfig/$prog
fi

case "$1" in
    start)
        echo `date +"%Y-%m-%d %T"` "begin" > $log
        /opt/suz-batch/bin/run-batch &
        touch $lock
        ;;
    stop)
        echo `date +"%Y-%m-%d %T"` "end" >> $log
        /opt/suz-batch/sbin/upload-log
        rm -f $lock
        ;;
    *)
        echo $"Usage: $0 {start|stop}"
        exit 1
esac

exit $?

※終了時はログをS3にアップロード(/opt/suz-batch/sbin/upload-log)します。

当然ですが、上記の起動スクリプトが有効になるようにもしておきます。

# chkconfig --add suz-batch
# chkconfig suz-batch on
# chkconfig --list suz-batch
suz-batch       0:off 1:off 2:on 3:on 4:on 5:on 6:off

バッチ処理のスクリプト(/opt/suz-batch/bin/run-batch)は下記の通りとなり、
バッチ終了時に自分自身(EC2)をターミネート(/opt/suz-batch/sbin/terminate-instance)
するようにしています。

log=/opt/suz-batch/log/error.log
echo `date +"%Y-%m-%d %T"` "run" >> $log
/opt/suz-batch/sbin/terminate-instance
exit 0

自分自身(EC2)をターミネート(/opt/suz-batch/sbin/terminate-instance)するスクリプトは下記のようにしています。

require_once("/opt/aws/php/latest/sdk.class.php");
date_default_timezone_set("Asia/Tokyo");

$as = new AmazonAS(array(
    "key"    => "ACCESS KEY",
    "secret" => "SECRET KEY"
));
$as->set_region(AmazonAS::REGION_APAC_NE1);
$response = $as->update_auto_scaling_group(
    "as-test",
    array(
       "MinSize"         => 0,
       "MaxSize"         => 0,
       "DesiredCapacity" => 0
    )
);

if(!$response->isOK()) {
    error_log(
        date("Y-m-d H:i:s") . " " . basename($_SERVER["PHP_SELF"]) . " " . $response->body->Error->to_json() . "n",
        3,
        "/opt/suz-batch/log/error.log"
    );
    exit(1);
}
exit(0);

上記のように、EC2をターミネートするのではなく、Auto ScalingのEC2起動数を0にすることで、結果的に自分自身(EC2)がターミネートされるようにしています。

尚、自分自身(EC2)をターミネートする時に実行される、ログをS3にアップロードするスクリプト(/opt/suz-batch/sbin/upload-log)は下記のようにしています。

require_once("/opt/aws/php/latest/sdk.class.php");
date_default_timezone_set("Asia/Tokyo");

$s3 = new AmazonS3(array(
    "key"    => "ACCESS KEY",
    "secret" => "SECRET KEY"
));
$s3->set_region(AmazonS3::REGION_APAC_NE1);
$s3->use_ssl = false;
$response = $s3->create_object("www.suz-lab.com", "test/error-" . date("YmdHis") . ".log", array( 
    "fileUpload" => "/opt/suz-batch/log/error.log"
));

if(!$response->isOK()) {
    error_log(
        date("Y-m-d H:i:s") . " " . basename($_SERVER["PHP_SELF"]) . " " . $response->body->to_json() . "n",
        3,
        "/opt/suz-batch/log/error.log"
    );
    exit(1);
}
exit(0);

次に、このAMIを使ってAuto Scalingの設定を行います。
具体的にはLaunch ConfigurationとAuto Scaling Groupを設定するのですが、以前紹介しました下記の記事が参考になるかと思います。

Auto ScalingでEC2を自動復旧(クラウドデザインパターン(CDP):Multi-Serverパターン)

特にAuto Scaling Groupの設定は、初期時にEC2は起動していない状態にしたいので、最大起動数と最小起動数を共に0にしておきます。
(ELB関係の設定も必要ありません)

最後に、今回の記事のポイントである、定期的にAutoScalingがEC2を起動(0 → 1にスケール)する部分です。

設定は下記のようにPHP(AWS SDK)で行なっています。

require_once("/opt/aws/php/latest/sdk.class.php");

$as = new AmazonAS(array(
    "key"    => "ACCESS KEY",
    "secret" => "SECRET KEY"
));
$as->set_region(AmazonAS::REGION_APAC_NE1);
$response = $as->put_scheduled_update_group_action(
    "as-test", // auto scaling group name
    "as-test", // scheduled action name
    array(
        "Recurrence"      => "*/5 * * * *", // unix cron syntax format
        "MinSize"         => 1,
        "MaxSize"         => 1,
        "DesiredCapacity" => 1
    )
);

var_dump($response->body);

APIはput_scheduled_update_group_actionを利用しており、Recurrenceにcronの文法でスケールアウトする時刻を指定することができます。

そして上記は、5分毎にEC2が1インスタンス稼働している状態にするように設定しています。
つまり、はじめから稼働していない(0インスタンス)の場合は、上記のタイミングで1インスタンス、新規に稼働することになります。

結果としては、下記のように動作し、S3へのログも下記のように5分毎にアップロードされることがわかります。

5分後 → EC2起動 → バッチ終了 → EC2ターミネート → 5分後 → EC2起動 → …

上記の流れを図にすると、下記のようになります。

こちらの記事はなかの人(suz-lab)監修のもと掲載しています。
元記事は、こちら