小規模なWebサイトをAWSで運営しています。突発的なアクセスに備えてインスタンスを増減できるようElastic Load Balancer(Classic Load Balancer)を利用しauto scalingするようしており、稼働中インスタンスのAMIバックアップをLambdaファンクションで夜間にとるようにしています。
今回は、このバックアップしたAMIでAuto Scaling設定を更新するLambdaファンクションを作ってみたいと思います。
こちら記事を参考にさせていただきました。
suz-lab – blog: “Auto Scaling”で利用するAMIをアップデートしてみる
Auto Scalingの設定を更新するコマンドは主にこちらの2つ。
●起動設定(Launch Configuration)を新規作成するコマンド
OK-Created launch config
●Auto Scalingグループを更新するコマンド
OK-Updated AutoScalingGroup
です。
新規に作成したLaunch Configurationで、Auto Scalingグループを更新する、という流れになります。
それでは、Lambdaファンクションの作成に入ります。
boto3というPythonで書かれたライブラリを使います。
AutoScaling — Boto 3 Docs 1.4.1 documentation
上記2つのコマンド同等のboto3の関数を使っていくことになります。
まず、バックアップされたAMIのIDを取得します。
バックアップは3世代前まで世代管理をしていますので、その中から最新のものを取得しています。
def get_latests_image_id(): ec2_client = boto3.client('ec2') sorted_images = [] response = ec2_client.describe_images( Owners = ['self'], Filters = [ { 'Name': 'tag:Name', 'Values': ['Nameタグの値'] } ] ) images = response['Images'] sorted_images = sorted( images, key = lambda x: x['CreationDate'], reverse = True ) return sorted_images[0]['ImageId']
取得したAMI IDを指定して、Launch Configurationを作成します。
こちらの関数を使用します。
http://boto3.readthedocs.io/en/latest/reference/services/autoscaling.html#AutoScaling.Client.create_launch_configuration
def create_launch_config(image_id): as_client = boto3.client('autoscaling') launch_configuration_name = '起動設定名' + dt.now().strftime('%Y%m%d%H%M%S') response = as_client.create_launch_configuration( LaunchConfigurationName = launch_configuration_name, ImageId = image_id, KeyName = 'キーペア名', SecurityGroups = ['セキュリティーグループID'], InstanceType = 't2.micro', BlockDeviceMappings=[ { 'DeviceName': '/dev/sda1', 'Ebs': { 'VolumeSize': 8, 'VolumeType': 'gp2', 'DeleteOnTermination': True } } ] ) return launch_configuration_name
BlockDeviceMappingsのパラメータはなくてもLaunch Configurationは作成できますが、実際にauto scaleする際にInServiceにならないので、最低限の情報を指定するようにしました(内容は適宜変更してください)。
BlockDeviceMappings=[ { 'DeviceName': '/dev/sda1', 'Ebs': { 'VolumeSize': 8, 'VolumeType': 'gp2', 'DeleteOnTermination': True } } ]
古いLaunch Configurationを取得して、
※Launch Configurationの取得にはこちらの関数を使用します。
def get_launch_config(): as_client = boto3.client('autoscaling') response = as_client.describe_launch_configurations() configs = [] if response is not None: for config in response['LaunchConfigurations']: launch_configuration = config['LaunchConfigurationName'] if ('起動設定名' in launch_configuration): configs.append(launch_configuration) return configs
取得したLaunch Configurationを削除します。
※Launch Configurationの削除はこちらの関数を使用します。
http://boto3.readthedocs.io/en/latest/reference/services/autoscaling.html?#AutoScaling.Client.delete_launch_configuration
def delete_old_launc_config(configs): as_client = boto3.client('autoscaling') for config in configs: response = as_client.delete_launch_configuration( LaunchConfigurationName = config ) return True
Auto ScalingグループのLaunch Configurationを更新します。
こちらの関数を使用します。
http://boto3.readthedocs.io/en/latest/reference/services/autoscaling.html?#AutoScaling.Client.update_auto_scaling_group
def update_auto_scaling_group(launch_configuration_name): as_client = boto3.client('autoscaling') as_client.update_auto_scaling_group( AutoScalingGroupName = 'オートスケーリンググループ名', LaunchConfigurationName = launch_configuration_name ) return True
これらの処理をまとめて整理するとこんな感じになりました。
import json import boto3 from boto3.session import Session import time from datetime import datetime as dt def lambda_handler(event, context): ec2_client = boto3.client('ec2') as_client = boto3.client('autoscaling') # get the latest backup of AMI. image_id = get_latests_image_id(ec2_client) # get all Launch Configurations. configs = get_launch_configs(as_client) # create new Launch Configuration. launch_config_name = create_launch_config(as_client, image_id) # update the Auto Scaling group setting. update_auto_scaling_group(as_client, launch_config_name) if (configs is not None): delete_old_launc_config(as_client, configs) return 0 def get_latests_image_id(ec2_client): sorted_images = get_sorted_images(ec2_client) return sorted_images[0]['ImageId'] def get_sorted_images(ec2_client): sorted_images = [] response = ec2_client.describe_images( Owners = ['self'], Filters = [ { 'Name': 'tag:Name', 'Values': ['example.com'] } ] ) images = response['Images'] sorted_images = sorted( images, key = lambda x: x['CreationDate'], reverse = True ) return sorted_images def get_launch_configs(as_client): response = as_client.describe_launch_configurations() configs = [] if response is not None: for config in response['LaunchConfigurations']: launch_configuration = config['LaunchConfigurationName'] if ('example.com' in launch_configuration): configs.append(launch_configuration) return configs def create_launch_config(as_client, image_id): launch_configuration_name = 'example.com_' + dt.now().strftime('%Y%m%d%H%M%S') response = as_client.create_launch_configuration( LaunchConfigurationName = launch_configuration_name, ImageId = image_id, KeyName = 'your keypair', SecurityGroups = ['your security group name'], InstanceType = 't2.micro', BlockDeviceMappings=[ { 'DeviceName': '/dev/sda1', 'Ebs': { 'VolumeSize': 8, 'VolumeType': 'gp2', 'DeleteOnTermination': True } } ] ) return launch_configuration_name def delete_old_launc_config(as_client, configs): for config in configs: response = as_client.delete_launch_configuration( LaunchConfigurationName = config ) return True def update_auto_scaling_group(as_client, launch_configuration_name): as_client.update_auto_scaling_group( AutoScalingGroupName = 'your auto scaling group name', LaunchConfigurationName = launch_configuration_name ) return True
GitHubにアップしました!
update_launch_config/update_launch_config.py at master · mojyamojya/update_launch_config
ただ、このlambdaファンクションをlambda_basic_executionのroleで実行してもエラーになってしまいます。
An error occurred (AccessDenied) when calling the DescribeLaunchConfigurations operation: User: arn:aws:sts::xxxxxxxxxxxx:assumed-role/lambda_basic_execution/update_launch_config is not authorized to perform: autoscaling:DescribeLaunchConfigurations: ClientError
以下のようなPolicyを追記し、autoscalingのリソースにアクセスできるIAM roleを作成しました。
{ "Effect": "Allow", "Action": [ "autoscaling:*" ], "Resource": [ "*" ] }
細かいところで雑な部分はありますが、今度は正常終了しました。
何かの参考になれば幸いです。
よければ参考にしてみてください!
関連記事
- 今、私にできること。 (0.238)
- 英訳Twitter botをTumblrにも投稿するようにした (0.238)
- @urayasu_kohobotをTwitter API v1.1対応しました (0.238)
- 過去に開発した翻訳アプリのソースをGitHubにアップしました! (0.238)
- Google Chrome 拡張機能を作ってみた (0.238)