今回は、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 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起動 → …
上記の流れを図にすると、下記のようになります。