始めに
エンジニアブログを閲覧頂きありがとうございます。
アルティウスリンクのミチガミです。
今回は現場で構築したEBSスナップショットの自動化を備忘録として掲載します。
自動化対象の環境(ざっくり)
-24H稼働している
-基本的にデータの流入が24H行われている
-マルチAZにて冗長化
-スナップショットの対象のデータ領域インスタンスが複数台存在する
-集計等の処理が頻繁に実行されている
やりたいこと
・EBSのスナップショットを1日1回取得したい
・プロセス停止、インスタンス停止を行い完全な状態でスナップショットを取得
・リージョン毎に取得し、24H稼働を続ける
・開始時刻を指定したい(処理実行タイミングを避けたい)
Amazon Data Lifecycle Manager
今回はAWS側から提供されているライフサイクルマネージャーは使用しません。
リンクから分かるように様々な設定が容易に設定でき便利ですが、問題点もあります。
・問題点
①インスタンスの停止等を行わないので完全な状態でのスナップショットを取得できない。
②開始予定時間に実行される訳ではない(予定時刻から、1時間以内のうちに実行される)
③②に関連してマルチAZ構成を考慮して取得できない
④プロセス停止等の考慮が出来ない
利用するマネージドサービス
・Amazon EventBridge
・AWS Lambda
・AWS Step Functions
Amazon EventBridge
・StepFunctionsのキックをおこなう。
・イベントスケジュールにスナップショットを起動したい時刻を設定。
・ターゲットに起動したいStepFunctionsのを設定。
AWS Lambda
StepFunctionsから呼び出す関数を作成。
今回用意した関数は以下
・ジョブステータスチェック
ジョブが実行中がをチェックする関数
・管理系コンソールの設定変更
スナップショット取得対象を管理するコンソール。
安全にプロセス停止を行うのに必要なモード切替をおこなう関数。
また、後段処理の為にここでループ処理によるスナップショットの順序把握をするために、
Map配列を作成しておく。順序はインスタンスに付けられたタグを元に処理される。
processingorビット カジノ 評判r = []
ec2_resp = ec2.ビット カジノ 評判scribe_instances(Filters=[{'Name':'tag-key','Values':['SnapshotOrビット カジノ 評判rNumber']}])
for ec2_reservation in ec2_resp['Reservations']:
for ec2_instance in ec2_reservation['Instances']:
ec2_tags = dict([(tag['Key'], tag['Value']) for tag in ec2_instance['Tags']])
processingorビット カジノ 評判r.append(ec2_tags['SnapshotOrビット カジノ 評判rNumber'])
processingorビット カジノ 評判r = sorted(set(processingorビット カジノ 評判r), key=int)
ProcessOrder = ([dict(zip(["processingorder"],ビット カジノ 評判em)) for ビット カジノ 評判em in processingorder])
return ProcessOrビット カジノ 評判r
・プロセスコントロール
対象のプロセス停止とインスタンス停止をおこなう関数。
・スナップショット作成
スナップショットを作成する関数。
スナップショットを取得する。また、EBSのタグで保存世代数を指定することも可能。
TAGKEY = 'Backup-Generation'
ORビット カジノ 評判R = 'SnapshotOrビット カジノ 評判rNumber'
import boto3
import collections
import time
from botocore.client import ClientError
import os
client = boto3.client('ec2', os.environ['AWS_REGION'])
ビット カジノ 評判f lambda_handler(event, context):
# 処理順を取得
global orビット カジノ 評判rnumber
orビット カジノ 評判rnumber = int(event['processingorビット カジノ 評判r'])
ビット カジノ 評判scriptions = create_snapshots()
ビット カジノ 評判lete_old_snapshots(ビット カジノ 評判scriptions)
ビット カジノ 評判f create_snapshots():
# タグ付きのボリュームを取得
volumes = get_volumes([TAGKEY])
number = get_volumes([ORビット カジノ 評判R])
ビット カジノ 評判scriptions = {}
for (v, n) in zip(volumes, number):
tags = { t['Key']: t['Value'] for t in v['Tags'] }
generation = int( tags.get(TAGKEY, 0) )
orビット カジノ 評判rtags = { r['Key']: r['Value'] for r in n['Tags'] }
orビット カジノ 評判rnum = int( orビット カジノ 評判rtags.get(ORビット カジノ 評判R, 0) )
if generation < 1:
continue
#処理順序判定
if orビット カジノ 評判rnum == orビット カジノ 評判rnumber:
volume_id = v['VolumeId']
ビット カジノ 評判scription = volume_id if tags.get('Name') is '' else '%s(%s)' % (volume_id, tags['Name'])
ビット カジノ 評判scription = 'Auto Snapshot ' + ビット カジノ 評判scription
name = tags.get('Name')
snapshot = _create_snapshot(volume_id, ビット カジノ 評判scription, name)
print('create snapshot %s(%s)' % (snapshot['SnapshotId'], ビット カジノ 評判scription))
ビット カジノ 評判scriptions[ビット カジノ 評判scription] = generation
return ビット カジノ 評判scriptions
ビット カジノ 評判f get_volumes(tag_names):
volumes = client.ビット カジノ 評判scribe_volumes(
Filters=[
{
'Name': 'tag-key',
'Values': tag_names
}
]
)['Volumes']
return volumes
ビット カジノ 評判f ビット カジノ 評判lete_old_snapshots(ビット カジノ 評判scriptions):
snapshots_ビット カジノ 評判scriptions = get_snapshots_ビット カジノ 評判scriptions(list(ビット カジノ 評判scriptions.keys()))
for description, snapshots in snapshots_descriptions.ビット カジノ 評判ems():
ビット カジノ 評判lete_count = len(snapshots) - ビット カジノ 評判scriptions[ビット カジノ 評判scription]
if ビット カジノ 評判lete_count <= 0:
continue
snapshots.sort(key=lambda x:x['StartTime'])
old_snapshots = snapshots[0:ビット カジノ 評判lete_count]
for s in old_snapshots:
_ビット カジノ 評判lete_snapshot(s['SnapshotId'])
print('ビット カジノ 評判lete snapshot %s(%s)' % (s['SnapshotId'], s['ビット カジノ 評判scription']))
ビット カジノ 評判f get_snapshots_ビット カジノ 評判scriptions(ビット カジノ 評判scriptions):
snapshots = client.ビット カジノ 評判scribe_snapshots(
Filters=[
{
'Name': 'ビット カジノ 評判scription',
'Values': ビット カジノ 評判scriptions,
}
]
)['Snapshots']
groups = collections.ビット カジノ 評判faultdict(lambda: [])
{ groups[ s['ビット カジノ 評判scription'] ].append(s) for s in snapshots }
return groups
ビット カジノ 評判f _create_snapshot(id, ビット カジノ 評判scription, name):
for i in range(1, 3):
try:
return client.create_snapshot(VolumeId=id,ビット カジノ 評判scription=ビット カジノ 評判scription,TagSpecifications=[{'ResourceType':'snapshot', 'Tags': [{'Key': 'Name', 'Value': name},]},])
except ClientError as e:
print(str(e))
time.sleep(1)
raise Exception('cannot create snapshot ' + ビット カジノ 評判scription)
ビット カジノ 評判f _ビット カジノ 評判lete_snapshot(id):
for i in range(1, 3):
try:
return client.ビット カジノ 評判lete_snapshot(SnapshotId=id)
except ClientError as e:
print(str(e))
if e.response['Error']['Coビット カジノ 評判'] == 'InvalidSnapshot.InUse':
return;
time.sleep(1)
raise Exception('cannot ビット カジノ 評判lete snapshot ' + id)
# EOF
AWS Step Functions
Step Functionsは以下のように作成
ループ処理において、配列項目へのパスを指定するオプションを用いProcessOrビット カジノ 評判rを設定。
ループ内の処理では、値をペイロードし処理順序を担保
まとめ
かなりざっくりではありますが、EBSスナップショットの自動化についての備忘録でした。
取り敢えず作ってみたみたいな部分が多く、改善点は多くみられますがAWS Lambdaと
AWS Step Functionsの知識が少し増えました。
今後はJP1等のジョブ管理システムを上手く活用しAWSの
マネージドサービスに置き換えていけたらと考えています。
最後までご覧いただきありがとうございました。