小規模な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)