AWS Lambda?
- サーバレスでプログラムを実行できるようなもの
- 1 か月 1,000,000 リクエストは無料
- 今回のような1日1回動かす程度なら無料でずっと使えます
- 詳しくは → https://aws.amazon.com/jp/lambda/faqs/
やりたいこと
- Lambdaのスケジュールイベントを使って自動的にEBSのスナップショットを作成する
- 今回使用する言語はPython
- スナップショットの対象は、EC2インスタンスのタグに「Backup-Generation」が付いているものとする
- Backup-Generationの値には、0以上の数字を指定する
- スナップショットの保存件数は、Backup-Generationの値とする
- 0のときはスナップショットを作成しない
- スナップショットの保存件数を超えた場合は、作成日付の古いものから削除する
- スナップショットの保存件数は、Backup-Generationの値とする
- Backup-Generationの値には、0以上の数字を指定する
- 作成したスナップショットのDescriptionには、「EBSボリュームID」と「EC2インスタンス名」をつける
- EC2インスタンス名がない場合は、「EBSボリュームID」のみをつける
- 処理が失敗した場合は、CloudWatchの監視によりメール(SNS)を送る
EC2インスタンスに名前とタグをつける
名前をつける
- EC2のページのサイドメニューから「Instances」をクリック
- カーソルをNameの列に合わせると鉛筆アイコンが表示されるのでクリック
- 名前を入力
タグをつける
- 対象のインスタンスにチェックをつける
- 「Tags」ボタンをクリック
- 「Add/Edit Tags」ボタンをクリック
- 「Create Tag」ボタンをクリック
- Keyに「Backup-Generation」、Valueに保存件数を入力
- 「Save」ボタンをクリック
Lambdaの作成
Select blueprint
- Lambdaのページの作成ボタンをクリック
- 「Python2.7」を選択
- 「hello-world-python」をクリック(動作確認後にスケジュールイベントを設定したいため)
- ※「lambda-canary」はいきなりスケジュール設定画面から始まる
Configure function
- Nameを入力
- Descriptionは空でもOK
Lambda function code
- 下記のコードを貼り付ける
python
import boto3
import collections
import time
from botocore.client import ClientError
ec2 = boto3.client('ec2')
def lambda_handler(event, context):
descriptions = create_snapshots()
delete_old_snapshots(descriptions)
def create_snapshots():
instances = get_instances(['Backup-Generation'])
descriptions = {}
for i in instances:
tags = {t['Key']: t['Value'] for t in i['Tags']}
generation = int(tags.get('Backup-Generation', 0))
if generation < 1:
continue
for b in i['BlockDeviceMappings']:
if b.get('Ebs') is None:
continue
volume_id = b['Ebs']['VolumeId']
description = volume_id if tags.get('Name') is '' else '%s(%s)' % (volume_id, tags['Name'])
description = 'Auto Snapshot ' + description
snapshot = _create_snapshot(volume_id, description)
print 'create snapshot %s(%s)' % (snapshot['SnapshotId'], description)
descriptions[description] = generation
return descriptions
def get_instances(tag_names):
reservations = ec2.describe_instances(
Filters=[
{
'Name': 'tag-key',
'Values': tag_names
}
]
)['Reservations']
return sum([
[i for i in r['Instances']]
for r in reservations
], [])
def delete_old_snapshots(descriptions):
snapshots_descriptions = get_snapshots_descriptions(descriptions.keys())
for description, snapshots in snapshots_descriptions.items():
delete_count = len(snapshots) - descriptions[description]
if delete_count <= 0:
continue
snapshots.sort(key=lambda x: x['StartTime'])
old_snapshots = snapshots[0:delete_count]
for s in old_snapshots:
_delete_snapshot(s['SnapshotId'])
print 'delete snapshot %s(%s)' % (s['SnapshotId'], s['Description'])
def get_snapshots_descriptions(descriptions):
snapshots = ec2.describe_snapshots(
Filters=[
{
'Name': 'description',
'Values': descriptions,
}
]
)['Snapshots']
groups = collections.defaultdict(lambda: [])
{groups[s['Description']].append(s) for s in snapshots}
return groups
def _create_snapshot(id, description):
for i in range(1, 3):
try:
return ec2.create_snapshot(VolumeId=id, Description=description)
except ClientError as e:
print str(e)
time.sleep(1)
raise Exception('cannot create snapshot ' + description)
def _delete_snapshot(id):
for i in range(1, 3):
try:
return ec2.delete_snapshot(SnapshotId=id)
except ClientError as e:
print str(e)
time.sleep(1)
raise Exception('cannot delete snapshot ' + id)
Lambda function handler and role
- 「Role」から「Basic execution role」を選択
IAM Roleの作成
- 「Role Name」を入力
- 「View Policy Document」をクリックしコードを表示させる
- 「Edit」をクリック
- 「OK」ボタンをクリック
- 下記のコードを貼り付け
- 「Allow」ボタンをクリック
iam.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": [
"arn:aws:logs:*:*:*"
]
},
{
"Effect": "Allow",
"Action": [
"ec2:DescribeInstances",
"ec2:DescribeSnapshots",
"ec2:CreateSnapshot",
"ec2:DeleteSnapshot"
],
"Resource": [
"*"
]
}
]
}
Advanced settings
- Timeoutを「10 sec」に変更(※動作確認の「Duration」の値を見て適宜変更)
- 「Next」ボタンをクリック
- 「Create function」ボタンをクリック
Lambdaの動作確認
- 左上の「Test」ボタンをクリック
- Inputデータは使わないのでそのまま「Save and test」をクリック
- 動作結果はページ最下部に表示される
- Summary
- Duration … 実行時間
- Billed duration … 課金対象時間
- Max memory used … メモリ使用量
- Summary
- Log outputの「Click here」をクリックするとCloudWatchログの画面が表示される
Lambdaのスケジュールイベントの作成
スケジュールイベントの追加
- 「Event sources」をクリック
- 「Add event source」をクリック
Add event source
- Event source typeから「Scheduled Event」を選択
- Nameを入力
- Descriptionは空でもOK
- schedule expressionを入力 ※時間はUTC
- 「Submit」ボタンをクリック
処理の失敗をCloudWatchで監視してSNSでメールを送る
Create Alarm
- CloudWatchのページのサイドメニューから「Alarms」をクリック
- 「Create Alarm」ボタンをクリック
Select Metric-1
- Lambda Metricsの下の「By Function Name」をクリック
Select Metric-2
- Metric Nameが「Errors」にチェック
- 「Next」ボタンをクリック
Define Alarm
Alarm Threshold
- Nameを入力
- Wheneverのisを「>」を選択
Alarm Preview
- Periodを「1 Day」を選択(※Lambdaのスケジュールイベントの設定に合わせて適宜変更)
- Statisticを「Sum」を選択
Actions
- Send notification toの「New list」をクリック(※既存の通知用のSNSがある場合はそれを選択)
- topic nameを入力
- Email listに通知するメールアドレスを入力
- 「Create Alarm」ボタンをクリック
- 承認メールが送信されるのでメール本文の「Confirm subscription」をクリック