AutoScaleを設定し、lambdaでAMIとAutoScaleの設定を自動更新する

今回はAuto Scaleの設定のまとめです。

### ・概要
負荷分散のためにAutoScaleを検討することはよくあることです。

ゲームのイベントなどでは負荷がかかる時間帯が決まっているので、常時起動しているサーバーを減らしAutoScaleのスケージュールで設定することで費用を抑えることが出来ます。

※AutoScaleは起動に時間がかかるので、スケーリングポリシーでは突発的な負荷に対応できないのでスケジュールを使っています。

### ・問題点

AutoScaleを設定した場合、設定しているAMIのassetやsourceが起動中のインスタンスと違うといった事が起こります。

そういった場合にAMIを更新し、自動的AutoScaleに割り当てる設定を行います。

### ・AutoScaleグループの作成

今回の設定は元々あるAutoScaleグループの設定を置き換えるので先にAutoScaleグループを作成しておきます。

・AWSコンソールの[AUTO SCALING]→[起動設定]→[起動の作成]

※ここの起動設定は最終的に使わないので適当でOKです

・AWSコンソールの[AUTO SCALING]→[AUTO SCALINGグループ]→[AUTO SCALINGグループの作成]

上記の起動設定を選択、下記を設定

ネットワーク
グループ名
ロードバランシング

※今回はスケジュールなのでスケーリングポリシーは設定しない
※通知を使用するとauto scaleの情報をlambda等に引き渡して色々出来ます。
※タグを使いauto scaleで作成されたインスタンスに適用できます。

<b>※ここで作成したAUTO SCALINGグループの名前を控えておきます。</b>

##### lambdaを設定
・[Blank Function]→リストから[CloudWatch Events]を選択
・ルール:新規のルール
・ルール名：任意
・ルール説明：任意
・ルールタイプ:スケジュール
・スケジュール式:cron(0 * * * ? *)
※cronの設定と同じ、上記は1時間毎
・トリガーの有効化にチェック

・名前：任意
・説明：任意
・ランタイム：python

・ロール：[カスタムロールの作成]を選択、IAMに下記をアタッチし作成、再度[既存のロールを選択]で作成したIAMを選択

・コード：下記を貼り付けて***の部分を自分の環境に置き換える

```py
import boto3
import time
from botocore.client import ClientError
from datetime import datetime, timedelta, tzinfo
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)

ec2 = boto3.client('ec2')
autoscaling = boto3.client('autoscaling')

instance = "***" #コピー元のec2インスタンスID(例：i-22a9edc2ted27d2a1)
device_name = "***" #コピー元のec2のブロックデバイス(例：/dev/sda1)
image_prefix = "***" #amiの名前の識別子（任意※他のAMIで使っていない文字）
launch_prefix = "***" #auto scaleの起動設定の名前の識別子（任意※他の起動設定で使っていない文字）
ec2_volume_size = *** #ebsのボリュームサイズ(単位：GB)
ec2_volume_type = "***" #ebsのボリュームタイプ(例：gp2)
security_group = [***] #ec2インスタンスのセキュリティグループ
ec2_instance_type = "***", #ec2インスタンスタイプ(例：c4.2xlarge)
ec2_key_name = "***", #ec2インスタンスのキーペア名
auto_scaling_group_name ="***",#作成したAutoScaleGroup名


def lambda_handler(event, context):
    try:
        dstr = datetime.now().strftime('_%Y-%m-%d-%H-%M-%S_') + instance
        logger.warning("create ami:%s" % (dstr))
        new_ami = ec2.create_image(Name=image_prefix + dstr ,InstanceId=instance,NoReboot=True,DryRun=False)
        recv = ec2.describe_images(Owners=['self'])
        list=[]
        target = image_prefix
        for img in recv['Images']:
            if target in img['ImageLocation']:
                list.append(img)
        s_list = sorted(list,key=lambda h: h['CreationDate'])
        if len(s_list) > 0:
            del_target = s_list[0]
            logger.info("delete ami:%s" % (del_target['Name']))
            ec2.deregister_image(ImageId=del_target['ImageId'],DryRun= False)
        dstr = datetime.now().strftime('_%Y-%m-%d-%H-%M-%S')
        logger.info("create auto scale launch config:%s" % (dstr))
        device = {}
        device['DeviceName'] = device_name
        ebs = {}
        ebs['VolumeSize'] = ec2_volume_size
        ebs['VolumeType'] = ec2_volume_type
        ebs['DeleteOnTermination'] = True
        device['Ebs'] = ebs
        device_mapping = [device]
        launch_name = launch_prefix + dstr
        
        res = autoscaling.create_launch_configuration(
            LaunchConfigurationName = launch_name,
            ImageId = new_ami['ImageId'],
            InstanceType = ec2_instance_type,
            SecurityGroups = security_group, 
            KeyName = ec2_key_name,
            BlockDeviceMappings = device_mapping,
            AssociatePublicIpAddress = True
        );
        logger.info("update auto scale group name:%s" % (launch_name))
        res = autoscaling.update_auto_scaling_group(
            AutoScalingGroupName=auto_scaling_group_name 
            LaunchConfigurationName = launch_name
        );  
        target = launch_prefix
        recv = autoscaling.describe_launch_configurations()
        list=[]
        for launch in recv['LaunchConfigurations']:
            if target in launch['LaunchConfigurationName']:
                list.append(launch)
        s_list = sorted(list,key=lambda h: h['CreatedTime'])
        if len(s_list) > 2:
            del_target = s_list[0]
            logger.info("delete launch config:%s" % (del_target['LaunchConfigurationName']))
            autoscaling.delete_launch_configuration(
                LaunchConfigurationName=del_target['LaunchConfigurationName'],
            )
        logger.info("end ami update")
        
    except ClientError as e:
        logger.error( e )
    return 'end'

```

### ・auto scaleのスケジュールを登録する

負荷の時間に合わせて自由に

### ・注意事項
<span style="color: #ff0000"><b>※最初はelbのhealthチェックだけじゃなくwebサーバーのログやawsコンソールで正常な起動を確認すること</b></span>

<span style="color: #ff0000"><b>※デプロイがAMIの更新と被らないようにすること</b></span>