EC2 AMI作成&Auto Scaling設定スクリプト

すんすくです。
AWSで一番好きなサービスはRDSです。

AWS上でApacheなどstatelessな鯖をAuto Scalingで立ち上げたいことはよくあります。
instance storeなAMIでAuto Scalingする場合は以下の操作が必要です。

しかし現在のところ、まだAWS Mangement Consoleではこれらの設定ができないので、
API経由で行う必要があり、ゆとりには辛い。
特に後者は毎回手動でやる大変手間なので、スクリプトで自動化しました。


root deviceが"instance store"のインスタンスのAMI化用にスクリプトを書きます。


s3cmdはpython版を利用。
CloudFrontと連携できたり、ruby版より多機能で断然使いやすいです。

#!/bin/bash

nowtime=`date +"%Y%m%d%k%M"`
bucketname="ami-${nowtime}"
region="ap-northeast-1"
mnt="/mnt"
manifest="image.manifest.xml"

# create bucket for storing AMI
s3cmd mb s3://${bucketname} --bucket-location ${region}
# create image 
ec2-bundle-vol -d ${mnt} -k ${EC2_PRIVATE_KEY} -c ${EC2_CERT} -u ${AWS_ACCOUNT_ID}
# put image to bucket
ec2-upload-bundle -b ${bucketname} -m ${mnt}/${manifest} -a ${AWS_ACCESS_KEY_ID} -s ${AWS_SECRET_ACCESS_KEY}
# register image in bucket as AMI (この操作はwebからでも可能)
ec2-register ${bucketname}/${manifest} --region ${region} -n ${bucketname}

rm -r ${mnt}/image* ${mnt}/img-mnt

このスクリプトを作りたいAMIのインスタンス上で実行すると、ami-${time}というIDのAMIが作成されます。

このAMIを使ってAuto Scaling設定。

事前に以下を用意・決めておきます。

  • ELB: MyLoadBalancer
  • 増減させるAMI ID
    • 上のスクリプトで出力されるID または ec2-describe-imagesで確認
  • Auto Scaling名: testas (任意)
  • SNS topic: admin
    • subscriberを適宜登録しておく

Auto Scalingの設定スクリプト (as-action.pl)は以下の通り。こっちはperlで書きました。
変えなさそうな値は決め打ちしてあるので、適宜変数化して用いたいです。

#!/usr/bin/perl

use strict;
use Getopt::Long;

my $op = '';
our $keyname = "sunsuk7tp";
our $securitygroupname = "admin";
our $elbname = "MyLoadBalancer";
our $basename = "autoscale" . time;
our $imageid = '';
our $instancetype = "t1.micro";
our ($minsize, $maxsize) = (1,4);
our $policytype = "cpu";
our $period = 60;
our $region = "ap-northeast-1";
our $az = $region . "b";
our ($uthres, $dthres) = (0, 0);
our $topicname = "admin";

GetOptions(
  "op=s"		=> \$op, # start | stop | clear | restart | info
  "name=s"		=> \$basename, # label
  "imageid=s" 		=> \$imageid, # ami-XXXXXXXX
  "instancetype=s"	=> \$instancetype, # t1.micro | m1.small | m1.large
  "minsize=i"		=> \$minsize, 
  "maxsize=i"		=> \$maxsize,
  "policytype=s"	=> \$policytype, # cpu | drb | dro | dwb | dwo | nin | not 
  "period=i"		=> \$period, # >= 60
  "uthres=i"		=> \$uthres,
  "dthres=i"		=> \$dthres,
);

our $configname = $basename . "_config";
our $groupname = $basename . "_group";
our $scaleoutpolicyname = $basename ."_scaleout_policy";
our $scaleinpolicyname = $basename . "_scalein_policy";
our $scaleoutalarmname = $basename . "_scaleout_alarm";
our $scaleinalarmname = $basename . "_scalein_alarm";

if ($op eq 'start') {
  my $res = _register_as();
  start() if $res > 0;
  print "specify imageid \n" if $res < 0;
} elsif ($op eq 'restart') {
  restart();
} elsif ($op eq 'stop') {
  stop();
} elsif ($op eq 'clear') {
  as_delete();
} elsif ($op eq 'info') { 
  info();
} else {
  print "specify op param (start|restart|stop|clear|info)\n";
}

sub start {
  # set policy (scale in/out)
  my $alarm_scaleout_id  = `as-put-scaling-policy ${scaleoutpolicyname} --auto-scaling-group ${groupname} 
  --adjustment=1 --type ChangeInCapacity | tr -d '\n'`;
  my $alarm_scalein_id  = `as-put-scaling-policy ${scaleinpolicyname} --auto-scaling-group ${groupname}  
  --adjustment=-1 --type ChangeInCapacity | tr -d '\n'`;

  my $metricname = {
    "cpu" => 'CPUUtilization',
    "drb" => 'DiskReadBytes',
    "dro" => 'DiskReadOps',
    "dwb" => 'DiskWriteBytes',
    "dwo" => 'DiskWriteOps',
    "nin" => 'NetworkIn',
    "not" => 'NetworkOut',
  }->{$policytype};

  # set alarm (scale in/out)
  `mon-put-metric-alarm ${scaleoutalarmname} --comparison-operator GreaterThanThreshold --metric-name ${metricname} 
  --namespace \"AWS/EC2\" --period ${period} --statistic Average --threshold ${uthres} --evaluation-periods 1
  --dimensions \"AutoScalingGroupName=${groupname}\" --alarm-actions ${alarm_scaleout_id}`;
  `mon-put-metric-alarm ${scaleinalarmname} --comparison-operator LessThanThreshold --metric-name ${metricname} 
  --namespace \"AWS/EC2\" --period ${period} --statistic Average --threshold ${dthres} --evaluation-periods 1
  --dimensions \"AutoScalingGroupName=${groupname}\" --alarm-actions ${alarm_scalein_id}`;

  # register notification of the above alarm to the sns's topic specified by arnid
  my $arnid = `sns-list-topics | grep ${topicname} | tr -d '\n'`;
  `as-put-notification-configuration ${groupname} 
  --notification-types autoscaling:EC2_INSTANCE_LAUNCH,autoscaling:EC2_INSTANCE_LAUNCH_ERROR,
  autoscaling:EC2_INSTANCE_TERMINATE,autoscaling:EC2_INSTANCE_TERMINATE_ERROR --topic-arn ${arnid}`;
}

sub _register_as {
  return -1 if $imageid eq "";
  # create launch config
  `as-create-launch-config ${configname} --image-id ${imageid} 
  --instance-type ${instancetype} --key ${keyname} --group ${securitygroupname}`;
  # create as group
  `as-create-auto-scaling-group ${groupname} --launch-configuration ${configname} 
  --availability-zones ${az} --min-size ${minsize} --max-size ${maxsize} --load-balancers ${elbname} --region ${region}`;  
  return 1;
}

sub restart {
  `as-update-auto-scaling-group ${groupname} --min-size ${minsize} --max-size ${maxsize}`;
  start();
}

sub stop {
  my $arnid = `sns-list-topics | grep ${topicname} | tr -d '\n'`;
  `as-delete-notification-configuration ${groupname} --topic-arn ${arnid} -f`;

  `mon-delete-alarms ${scaleoutalarmname} -f`;
  `mon-delete-alarms ${scaleinalarmname} -f`;

  `as-delete-policy ${scaleoutpolicyname} --auto-scaling-group ${groupname} -f`;
  `as-delete-policy ${scaleinpolicyname} --auto-scaling-group ${groupname} -f`;

  # auto scaling stop
  `as-update-auto-scaling-group ${groupname} --min-size 0 --max-size 0 -f`;
}

sub as_delete {
  `as-delete-auto-scaling-group ${groupname} -f`;
  `as-delete-launch-config ${configname} -f`;
}

sub info {
  print "####LAUNCH CONFIG####\n";
  system("as-describe-launch-configs | grep ${configname}");
  print "####AUTO SCALING GROUP####\n";
  system("as-describe-auto-scaling-groups | grep ${groupname}");
  print "####POLICIES####\n";
  system("as-describe-policies | grep ${basename}");
  print "####ALARMS####\n";
  system("mon-describe-alarms | grep -e ${basename}");
  print "####NOTIFICATION####\n";
  system("as-describe-notification-configurations | grep ${groupname}");
}

このスクリプトでAuto Scalingの起動・再設定&再起動・停止・削除・情報取得ができます。

  • 起動 (指定AMIをsmall type、1~5台でCPU使用率90% or 30%で増減するauto scalingエントリを作成)
# ./as-action.pl --name=testas --op start --imageid=ami-XXXXXXXX --instancetype=m1.small
--minsize=1 --maxsize=5 --policytype=cpu --uthres=90 --dthres=30
  • 停止 (as config & groupは保持したまま、policy&alarmの削除し、起動インスタンス増減を0に)
# ./as-action.pl --name=testas --op stop
  • 再起動 (新しい閾値を指定する)
# ./as-action.pl --name=testas --op restart --minsize=1 --maxsize=3 --policytype=cpu --uthres=95 --dthres=25 --period=120
  • 削除 (as config&groupの削除)
# ./as-action.pl --name=testas --op clear
  • auto scalingに関する情報一括取得
# /root/bin/as-action.pl --name=testas --op info