Edited at

LambdaでRedshiftのスナップショット取得&クラスター削除


やりたいこと

Redshiftのランニングコストをできるだけ抑えたい。

AWSのビッグデータ基盤であるRedshiftは、手軽に使えてしかも他のDWHサービスと比べて安価という非常に優良なサービスです。

とはいえ、一番小さいdc2.largeのインスタンスでも一時間あたり最低0.314$、一ヶ月ずっと起動しっぱなしの場合だとおよそ25000円程度なので、個人が学習目的で常用するには少し躊躇われるお値段。

少なくとも、使わない時間は停止しておきたいのですが、このRedshift、RDSとは違って停止という概念がありません。

インスタンスを削除して、必要なときにスナップショット(要するにバックアップですね)から復元するという手段を取るしかない。

まぁこの作業もAWSのコンソール上から数クリックでできてしまうものなので、さほど手間というわけでもないんですが、削除し忘れてそこそこの金額を取られてしまうのもアレなので、できることならこのスナップショットの取得→削除の流れをLambdaで自動でやってしまいたいよね、というのが今回のお題です。


結論

というわけで今回も結論から。言語はPython3.7。

import boto3

import time
from botocore.client import ClientError
from datetime import datetime, timedelta, tzinfo

redshift = boto3.client('redshift')
snapshot_prefix = "testinstance"
clusterid = "testinstance"
generation = 1

def delete_snapshot():
# スナップショット情報取得(手動のみ)
snapshots = redshift.describe_cluster_snapshots(
ClusterIdentifier=clusterid,
SnapshotType='manual'
)

if len(snapshots['Snapshots']) <= generation:
return

snapshot_sorted = sorted(snapshots['Snapshots'], key=lambda x:x['SnapshotCreateTime'], reverse=True)

# 指定世代以前のスナップショットを削除
for snapshot in snapshot_sorted[generation:]:
try:
response = redshift.delete_cluster_snapshot(
SnapshotIdentifier=snapshot['SnapshotIdentifier'],
SnapshotClusterIdentifier=clusterid
)
return

except ClientError as e:
pass

def delete_cluster():
snapshotid = "-".join([snapshot_prefix, datetime.now().strftime("%Y%m%d-%H%M%S")])

for i in range(0, 5):
try:
response = redshift.delete_cluster(
ClusterIdentifier=clusterid,
SkipFinalClusterSnapshot=False,
FinalClusterSnapshotIdentifier=snapshotid
)
return True

except ClientError as e:
print(str(e))

time.sleep(1)

def lambda_handler(event, context):
try:
response = redshift.describe_clusters(ClusterIdentifier=clusterid)

except ClientError as e:
print(str(e))

else:
delete_snapshot()

delete_cluster()


詳細

やっていることは簡単で、

 ・特定のプレフィックスがついたスナップショットを、指定した世代数だけ残して削除

 ・指定したインスタンス名のRedshiftインスタンスを、スナップショットを取得してから削除

だけです。

あとはLambdaのトリガーで、日次で起動してやれば良いだけ。

これで、うっかり削除し忘れても安心ですね!