AWS SDK for PHP3 AutoScalingClientからAuto Scaling グループを設定してみる

## 経緯
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クラスのソース内容
ソースに一時的にコメントを記載しています。

```:OptimizeAutoScalingGroup.php
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)

## 所感
一旦やりたいことはできましたが、まだまだブラッシュアップする必要があるなと感じました。
随時記事も整理していきます。
以上です。