Amazon SQSとCloudWatchによる高速AutoScaling

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

キューの長さと連動する

Amazon SQSはキューのサービスです。メッセージを送る側と受ける側を疎結合にできるため、たまにスケールするアプリケーションにおいては非常に重要な役割を果たします。今回は、急激にメッセージが増えた時にできるだけ早くチェックして高速にスケールする方法についてご紹介します。

5分から1分へ

Amazon SQSでは、規定値として5分に1回Amazon CloudWatchに監視用のデータを送っています。このデータの種類には、キューの長さも含まれていて、標準の状態では最大5分前の状態確認を1分毎に行うことになり、急激なキューへのメッセージ追加に対して、即座に反応ができません。そこで、カスタムメトリクスを使って1分以下のタイミングで監視データを送ることで、今までよりも早くアクションをすることができるようになります。

カスタムメトリクスの登録

SQSのキューの長さを確認するためには、ApproximateNumberOfMessagesという名前の属性情報を指定して取得します。そして、その情報をCloudWatchのカスタムメトリクスとして登録します。

以下は、Javaでコードを書いた場合

import java.util.ArrayList;
import java.util.Map;

import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.profile.ProfileCredentialsProvider;
import com.amazonaws.regions.Region;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.cloudwatch.AmazonCloudWatch;
import com.amazonaws.services.cloudwatch.AmazonCloudWatchClient;
import com.amazonaws.services.cloudwatch.model.MetricDatum;
import com.amazonaws.services.cloudwatch.model.PutMetricDataRequest;
import com.amazonaws.services.cloudwatch.model.StandardUnit;
import com.amazonaws.services.sqs.AmazonSQS;
import com.amazonaws.services.sqs.AmazonSQSClient;

public class SQSSizePutSample {

	public static final String APPROXIMATE_NUMBER_OF_MESSAGES_ATT 
		= "ApproximateNumberOfMessages";
	public static final String NAMESPACE = "QueueDepthNamespace";
	public static final String QUEUE_URL 
		= "https://sqs.ap-northeast-1.amazonaws.com/XXXXXXXXXXXX/queue-foo";
	public static final String QUEUE_NAME 
		= "queue-foo";
	

	public static void main(String[] args) throws Exception {

		AWSCredentials credentials 
		= new ProfileCredentialsProvider("default").getCredentials();

		AmazonSQS sqs = new AmazonSQSClient(credentials);
		Region ap1 = Region.getRegion(Regions.AP_NORTHEAST_1);
		sqs.setRegion(ap1);

		java.util.List<String> attributeList = new ArrayList<String>();
		attributeList.add("ApproximateNumberOfMessages");
		
		Map<String, String> attributes 
			= sqs.getQueueAttributes(QUEUE_URL, attributeList).getAttributes();
		double approximateNumOfMsg 
		= Double.parseDouble(attributes.get(APPROXIMATE_NUMBER_OF_MESSAGES_ATT));

		MetricDatum md = new MetricDatum()
		  .withMetricName(QUEUE_NAME + "-OneMinute-" + APPROXIMATE_NUMBER_OF_MESSAGES_ATT)
		  .withUnit(StandardUnit.Count)
		  .withValue(approximateNumOfMsg);
		 
		AmazonCloudWatch cw = new AmazonCloudWatchClient(credentials);
		cw.setRegion(Region.getRegion(Regions.AP_NORTHEAST_1));
		cw.putMetricData(new PutMetricDataRequest()
		  .withNamespace(NAMESPACE)
		  .withMetricData(md));
		
	}
}

コンソールでもやってみましょう。まずは、SQSからキューのメッセージ数を取り出します。

$ aws sqs get-queue-attributes \
  --queue-url https://sqs.ap-northeast-1.amazonaws.com/XXXXXXXXXXXX/queue-foo \
  --attribute-names ApproximateNumberOfMessages

次に取り出した数をCloudWatchのカスタムメトリクスに投げます。

$ aws cloudwatch put-metric-data \
  --namespace QueueDepthNamespace \
  --metric-name queue-sample-OneMinute-ApproximateNumberOfMessages \
  --value 53

アラームの設定

SQSのキューの長さを頻繁に送ってくるようになったので、次はアラームを設定したいと思います。

まずはじめにAuto Scalingグループを作成しておいてください。ポリシーは空で構いません。

screenshot 2015-01-02 19.52.51

次にCloudWatchでアラームを新規作成してください。アクションの指定は空で構いません。普通用(+1台)のアラームと緊急用(+2台)のアラームの2つ設定します。

screenshot 2015-01-02 19.55.22

次にAuto Scalingの画面で、スケーリングポリシーを設定します。+1のアクションと+2のアクションです。

screenshot 2015-01-02 19.56.13

最後にCloudWatchの画面で、先ほど作成したアラームにアクションを指定します。

screenshot 2015-01-02 19.57.50

キューのサイズが急激に増えていた場合にはいつもよりも多くの処理用インスタンスを一気に追加しています。

まとめ

SQSの標準では5分に1回送っていた監視用データの送信をカスタムメトリクスによって1分間隔にし、CloudWatch側で1分毎にチェックできるようになりました。次に、カスタムメトリクスにアラームを設定し、状態に応じてAuto Scalingグループに関連付けられたポリシーを呼び出すことができました。キューが1000を超えたら1台追加、2000を超えたら2台追加といった具合に設定することができました。このやり方であれば、10000超えたら10台追加なども簡単ですね。

さて、これでコンソリされたアカウントのDetailed Billing Reportの締め処理が100倍速になることを祈りつつお雑煮を食べたいと思います。

参考資料

Rapid Auto Scaling with Amazon SQS