今まではEC2の中でクーロンが動作してスナップショットをとっていましたが、なーんとなく違和感(自分自身でスナップショットを取るってどうなん?いやちょっと意味合いは違うけど…外側からスナップショット取りたい!)があって、なんか方法ないかなぁと思ったら、ビンゴな記事が!(Qittaさんいつもお世話になっております!!)
Lambdaって?
読み方はラムダ(だと思う。)
詳しいことはここに書いてあるけど、サーバ建てなくてもコードが実行できるちょっと便利なやつ。起動契機もいろいろとある。
お値段は、1ヶ月100万件のリクエスト及び400,000 GB-秒のコンピューでィング時間が無料。
相変わらずよくわからんが、今回の用途ぐらいなら無料枠で行ける。
EC2側の設定
モザイクばかりで申し訳ないが、Nameのところにマウスを合わせると、えんぴつマークが出てくるので、インスタンスに名前をつけてあげる。
「タグ」をクリックして、「タグの追加/編集」をクリックすると、
「タグを作成」をクリックして「Backup-Generation」キーにスナップショットを取る件数を値に入力して「保存」をクリックします。
Lambda側で関数を作ってスナップショットを作成する
「今すぐ始める」をクリック
「Python 2.7」を選んで、フィルターに「hello」を入れると「hello-world-python」が出て来るので、そのあたりをクリック。
トリガーについては後ほど設定するので、とりあえず、「次へ」をクリック。
ロールで「カスタムロールを作成」を選択する。
ロール名を適当につけて、「ポリシードキュメント…」をクリックして「編集」ボタンをクリックすると、
確認メッセージが出るので、「OK」ボタンをクリック。
編集する内容は以下を入力する。
{
"Version": "2017-04-07",
"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": [
"*"
]
}
]
}
※あたりまえのことかもだけど、最後に改行があるとエラーでます…
タイムアウトを2秒から10秒に変更して、「次へ」をクリック
確認画面が表示されるので、「関数の作成」をクリック。
コード部分には以下を入力。
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)
で、「テスト」ボタンをクリック。
入力することは無いので、そのまま「保存してテスト」をクリック。
こんな感じになるので、「ここをクリックし」でもクリックしてみると、
こんな感じで、
こんなログが表示される。
ec2のスナップショットを見てみると…
おぉ!!作ってる!!
Lambda側で定期的に実行させる。
「トリガーを追加」をクリック。
空白の点線内をクリックして、「CloudWatch イベント – スケジュール」をクリック。
「ルール名」を適当に入力して「スケジュール式」に「cron(0 22 * * ? *)」を入力して、「送信」ボタンをクリックする。
時刻はUTC時間なので、適当に計算してください…
これで、毎日スナップショットを自動的に取得してくれます。数日後確認したところ、古い履歴は消されて、最新の2件だけスナップショットが取れてました。よかったよかった。
あとは、元の記事にあるようにCloudWatchでエラーを監視したりの設定をしていけばいい感じじゃないでしょうか。
これ、何が便利って、「Backup-Generation」キーがあるものをやってくれるので、インスタンスを追加してもタグ付けだけやってあげれば、自動的にスナップショットとってくれるんですよね。