経緯
LaravelアプリケーションでAWSのメッセージキューイングサービス(Amazon SQS)を利用する機会があり、
Webから取得したデータをキューに送信、定期的にキューからデータを取得しては処理をするということをしていました。
但し、気づいたらキューに数万件のメッセージが。。
もう少し早く、柔軟に処理できないかと模索した結果、キューに溜まっているメッセージ件数に応じてAuto Scaling グループ(AWS Auto Scaling)の設定を変更、必要なだけサーバーを立てるということをしたいと思い、この記事を書くに至りました。
以下、ざっくりとした処理フローです。
①Laravelアプリケーション実装しているCommandでwebから情報を定期取得
↓
②Amazon SQS(メッセージキューイングサービス)に取得したデータを追加
*必要なデータを配列に入れ、serializeしたものを送信しています。
↓
③定期的にキューからメッセージを取得し処理を実施
追伸:
私もまだまだ道半ば故、ご指摘やアドバイスを頂ければとっても嬉しいです。
■事前準備
今回はローカルに構築したLaradock環境下で行っています。
・まず、workspaceコンテナへ
docker-compose exec workspace bash
・ホームへ移動
cd ~
・.awsディレクトリを作成し、移動
mkdir .aws
cd .aws
・以下ファイルを作成し、AWSへのアクセス情報を書き込んでおきましょう
vim credentials
[default]
aws_access_key_id = アクセスキーID
aws_secret_access_key = シークレットキー
vim config
[default]
output = json
region = ap-northeast-1
・config、credentailsファイルの権限を600に変更
・.awsフォルダの権限を775に変更
chmod 600 *
chmod 775 .aws
作成したcommandクラスのソース内容
ソースに一時的にコメントを記載しています。
namespace App\Console\Commands;
use Aws\Sqs\SqsClient;
use Aws\Sqs\AwsException;
use Aws\AutoScaling\AutoScalingClient;
use Illuminate\Console\Command;
class OptimizeAutoScalingGroup extends Command
{
protected $signature = 'command:optimize_auto_scaling_group';
protected $description = '監視対象のSQSキューのメッセージ件数に応じてAutoScalingGroupの設定を最適化'
/**
* @return int
*
*/
public function handle()
{
try {
// SQSクライアント生成
$sqs_client = new SqsClient(['profile' => 'default', 'region' => 'ap-northeast-1', 'version' => 'latest']);
// AutoScalingClient生成
$auto_scaling_client = new AutoScalingClient(['profile' => 'default', 'region' => 'ap-northeast-1', 'version' => 'latest']);
// SQSキューからメッセージ件数を取得
$receive_result = $sqs_client->getQueueAttributes([
'AttributeNames' => ['ApproximateNumberOfMessages'],
'QueueUrl' => 'queue_url' // こちらに対象のSQSキューのURLを設定
]);
$message_count = intval($receive_result->get('Attributes')['ApproximateNumberOfMessages']);
// LaunchConfigurationNameを設定
$launch_configuration_name = 'CONFIGURATION-TEMPLATE';
// AutoScalingGroupNameを設定
$auto_scaling_group_name = 'AUTO-SCALING-GROUP';
// AutoScalingGroupの存在確認
$receive_result = $auto_scaling_client->describeAutoScalingGroups(['AutoScalingGroupNames' => [$auto_scaling_group_name]]);
$auto_scaling_group = $receive_result['AutoScalingGroups'];
if (empty($auto_scaling_group) !== true) {
// 指定したAutoScalingGroupプロセスを停止
$auto_scaling_client->suspendProcesses([
'AutoScalingGroupName' => $auto_scaling_group_name
]);
// AutoScalingGroupを削除
$auto_scaling_client->deleteAutoScalingGroup([
'AutoScalingGroupName' => $auto_scaling_group_name,
'ForceDelete' => true,
]);
// AutoScalingGroupが削除されるまで待機
while (true) {
$receive_result = $auto_scaling_client->describeAutoScalingGroups(['AutoScalingGroupNames' => [$auto_scaling_group_name]]);
$auto_scaling_group = $receive_result['AutoScalingGroups'];
if (empty($auto_scaling_group)) {
echo '削除処理が完了した為、AutoScalingGroupを再構築します.PHP_EOL;
break;
}
echo '削除処理中...'.PHP_EOL;
sleep(30);
}
}
// キューから取得したメッセージ件数に応じてAutoScalingGroupの最小値、最大値を設定
$auto_scaling_min_count = 2; // デフォルト値をとりあえず設定
$auto_scaling_max_count = 3;
if ($message_count > 5000 && $message_count <= 10000) {
$auto_scaling_min_count = 5;
$auto_scaling_max_count = 7;
}
if ($message_count > 10000 && $message_count <= 50000) {
$auto_scaling_min_count = 10;
$auto_scaling_max_count = 15;
}
// AutoScaling起動テンプレート設定
$receive_result = $auto_scaling_client->describeLaunchConfigurations(['LaunchConfigurationNames' => [$launch_configuration_name]]);
$launch_configuration = $receive_result['LaunchConfigurations'];
if (empty($launch_configuration)) {
$auto_scaling_client->createLaunchConfiguration([
'LaunchConfigurationName' => $launch_configuration_name,
'ImageId' => 'EC2のAMI',
'InstanceType' => 't2.nano',
'KeyName' => 'キーペア名',
'SecurityGroups' => [
'セキュリティグループ',
],
]);
}
// AutoScalingGroup設定
$auto_scaling_client->createAutoScalingGroup([
'AutoScalingGroupName' => $auto_scaling_group_name,
'LaunchConfigurationName' => $launch_configuration_name,
'HealthCheckType' => 'EC2',
'HealthCheckGracePeriod' => 300,
'MinSize' => $auto_scaling_min_count,
'MaxSize' => $auto_scaling_max_count,
'AvailabilityZones' => [
'ap-northeast-1a',
'ap-northeast-1c',
'ap-northeast-1d',
],
]);
} catch(AwsException $e) {
echo "[Error]".PHP_EOL;
echo $e->getMessage().PHP_EOL;
echo $e->getFile()."(LINE:".$e->getLine().")".PHP_EOL;
die();
}
}
}
ポイント1. SQSキューからメッセージ件数を取得
// SQSキューからメッセージ件数を取得
$receive_result = $sqs_client->getQueueAttributes([
'AttributeNames' => ['ApproximateNumberOfMessages'],
'QueueUrl' => 'queue_url'
]);
$message_count = intval($receive_result->get('Attributes')['ApproximateNumberOfMessages']);
AttributeNames(key)にApproximateNumberOfMessages(value)を指定することでキューからメッセージ件数を取得できます。
*approximateなんであくまで近似値なんでしょう。
・[こちらの公式ページ参考に]
(https://docs.aws.amazon.com/aws-sdk-php/v3/api/class-Aws.Sqs.SqsClient.html)
ポイント2. AutoScalingGroupの存在確認
再構築しようとしているAtuoScalingGroupが既に存在しているかの判定処理です。
$receive_result = $auto_scaling_client->describeAutoScalingGroups([
'AutoScalingGroupNames' => [$auto_scaling_group_name]
]);
$auto_scaling_group = $receive_result['AutoScalingGroups'];
・DescribeAutoScalingGroups
=> 対象のオートスケーリンググループ名を指定し、オートスケーリンググループに関する情報を取得。
・[こちらも公式ページ参考に]
(https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-autoscaling-2011-01-01.html#describeautoscalinggroups)
ポイント3. 起動中のAutoScalingGroupのプロセスを停止し、AutoScalingGroupを削除
if (empty($auto_scaling_group) !== true) {
$auto_scaling_client->suspendProcesses([
'AutoScalingGroupName' => $auto_scaling_group_name
]);
$auto_scaling_client->deleteAutoScalingGroup([
'AutoScalingGroupName' => $auto_scaling_group_name,
'ForceDelete' => true,
]);
while (true) {
$receive_result = $auto_scaling_client->describeAutoScalingGroups(
['AutoScalingGroupNames' => [$auto_scaling_group_name]
]);
$auto_scaling_group = $receive_result['AutoScalingGroups'];
if (empty($auto_scaling_group)) {
echo '削除処理が完了した為、AutoScalingGroupを再構築します.PHP_EOL;
break;
}
echo '削除処理中...'.PHP_EOL;
sleep(30);
}
}
・SuspendProcesses
=> 指定したAutoScalingのプロセスを一時停止。
*第二引数の「ScalingProcesses」でsuspendするプロセスを細かく指定できそうな感じです。多分。
・[こちらも公式ページ参考に]
(https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-autoscaling-2011-01-01.html#suspendprocesses)
・DeleteAutoScalingGroup
=> 指定したAutoScalingGroupを削除。起動中のAutoScalingGroupを削除する為に第二引数のForceDeleteをtrueとしています。
*このあたりはもう少し調べる必要がありそうです。。
・[こちらも公式ページ参考に]
(https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-autoscaling-2011-01-01.html#deleteautoscalinggroup)
次が私が少し苦労した点です。
上記でDeleteAutoScalingGroupを実行すると、
①スケーリングされたインスタンスが削除される
②紐づいているAutoScalingGroupを削除
といった流れなんですが、②でAutoScalingGroupが完全に削除されるまでの時間がまちまちの為、
以後の処理でAutoScalingGroupを再構築する際、既にAutoScalingGroupが存在しているというエラーになる場合があります。
そこでこんな不恰好なコードになっています。。
while (true) {
$receive_result = $auto_scaling_client->describeAutoScalingGroups(
['AutoScalingGroupNames' => [$auto_scaling_group_name]
]);
$auto_scaling_group = $receive_result['AutoScalingGroups'];
if (empty($auto_scaling_group)) {
echo '削除処理が完了した為、AutoScalingGroupを再構築します.PHP_EOL;
break;
}
echo '削除処理中...'.PHP_EOL;
sleep(30);
}
完全に削除されるまで、whileの中でdescribeAutoScalingGroups(オートスケーリンググループに関する情報を取得)を実行しています。
ポイント4. キューから取得したメッセージ件数に応じてAutoScalingGroupの最小値、最大値を設定
// キューから取得したメッセージ件数に応じてAutoScalingGroupの最小値、最大値を設定
$auto_scaling_min_count = 2;
$auto_scaling_max_count = 3;
if ($message_count > 5000 && $message_count <= 10000) {
$auto_scaling_min_count = 5;
$auto_scaling_max_count = 7;
}
if ($message_count > 10000 && $message_count <= 50000) {
$auto_scaling_min_count = 10;
$auto_scaling_max_count = 15;
}
ここは大したポイントではないですね。インスタンスの増減を確認する為に一時的にスケーリングするインスタンスの最小値、最大値を設定しています。
ポイント5. AutoScaling起動設定の作成
$receive_result = $auto_scaling_client->describeLaunchConfigurations(['LaunchConfigurationNames' => [$launch_configuration_name]]);
$launch_configuration = $receive_result['LaunchConfigurations'];
if (empty($launch_configuration)) {
$auto_scaling_client->createLaunchConfiguration([
'LaunchConfigurationName' => $launch_configuration_name,
'ImageId' => 'EC2のAMI',
'InstanceType' => 't2.nano',
'KeyName' => 'キーペア名',
'SecurityGroups' => [
'セキュリティグループ',
],
]);
}
・CreateLaunchConfiguration
=> AutoScalingの起動設定を作成。
・[こちらも公式ページ参考に]
(https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-autoscaling-2011-01-01.html#createlaunchconfiguration)
ポイント6. AutoScalingGroupの作成
$auto_scaling_client->createAutoScalingGroup([
'AutoScalingGroupName' => $auto_scaling_group_name,
'LaunchConfigurationName' => $launch_configuration_name,
'HealthCheckType' => 'EC2',
'HealthCheckGracePeriod' => 300,
'MinSize' => $auto_scaling_min_count,
'MaxSize' => $auto_scaling_max_count,
'AvailabilityZones' => [
'ap-northeast-1a',
'ap-northeast-1c',
'ap-northeast-1d',
],
]);
・CreateAutoScalingGroup
=> AutoScalingGroupの作成。
*最低限必要であろうパラメータを設定していますが、こちらももう少し調べる必要がありそうです。
・[こちらも公式ページ参考に]
(https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-autoscaling-2011-01-01.html#createautoscalinggroup)
所感
一旦やりたいことはできましたが、まだまだブラッシュアップする必要があるなと感じました。
随時記事も整理していきます。
以上です。