DISCOVERY

日々の備忘録

RDSのスナップショットをS3に定期的に保存する

RDSのsnapshotを定期的にS3に保存する必要があったので備忘録

およそ下図のようなことがしたい

f:id:azpiero:20200715214303p:plain
構成図イメージ

1. 古いsnapshotを作成 & 新しいsnapshotを作成し、完成まで待機するlambdaの作成

snapshotの削除・作成は他サイトを参考にしつつ、snapshotが完成したらS3に保存したかったので check_snapshot_createdを追加した。[スナップショットに付けたいprefix名]などは適宜変更する。

iimport json
import boto3
import time
from botocore.client import ClientError
from datetime import datetime, timedelta, tzinfo
import threading

rds = boto3.client('rds')
s3 = boto3.resource('s3') 

class JST(tzinfo):
    def utcoffset(self, dt):
        return timedelta(hours=9)
    def dst(self, dt):
        return timedelta(0)
    def tzname(self, dt):
        return 'JST'

def delete_snapshots(prefix, instanceid, days):
    snapshots = rds.describe_db_snapshots(DBInstanceIdentifier=instanceid)
    now = datetime.utcnow().replace(tzinfo=None)
    for snapshot in snapshots['DBSnapshots']:
        if 'SnapshotCreateTime'not in snapshot:
            continue
        delta = now - snapshot['SnapshotCreateTime'].replace(tzinfo=None)
        if snapshot['DBSnapshotIdentifier'].startswith(prefix) and delta.days >= days:
            rds.delete_db_snapshot(DBSnapshotIdentifier=snapshot['DBSnapshotIdentifier'])


def create_snapshot(prefix, instanceid):
    newsnapshotid = "-".join([prefix, datetime.now().strftime("%Y-%m-%d-%H-%M")])
    rds.create_db_snapshot(
        DBSnapshotIdentifier=newsnapshotid,
        DBInstanceIdentifier=instanceid
    )
        

def check_snapshot_created(prefix, instanceid):
    snapshots = rds.describe_db_snapshots(DBInstanceIdentifier=instanceid)['DBSnapshots']
    for snapshot in snapshots:
        if snapshot['Status'] != "available":
            time.sleep(20)
            check_snapshot_created(prefix, instanceid)
    

def lambda_handler(event, context):
    snapshot_prefix = [スナップショットに付けたいprefix名]
    instance = [DBのインスタンス名]
    delete_days = 50
    create_snapshot(snapshot_prefix, instance)
    delete_snapshots(snapshot_prefix, instance, delete_days)
    check_snapshot_created(snapshot_prefix, instance)

今回のlambdaはスナップショットが完成してないと20秒sleepする。 標準のlambdaのタイムアウトに一発で引っかかるので、lambdaのタイムアウト時間は適宜延長する。

2. lambda発火イベントと完了イベント追加

発火イベントはCloudwatch eventsをcronで実施。日本だと9時間ズレなので時刻を設定するときは気を付ける。完了イベントはいったんここまで完了したらメールを送ってもらうようテストしたかったのでSNSのトピックへ投げるように設定した。

3. S3へスナップショット

boto3のstart_export_taskを利用。 ここで指定するIAMにはS3の操作の権限を付ける。 最新のスナップショットをエクスポートするために取得してきたスナップショット一覧からSnapshotCreateTimeでソートして一つだけエクスポートを実施する。

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

rds = boto3.client('rds')
s3 = boto3.resource('s3') 
client = boto3.client('kms')

def export_snapshot(prefix, instanceid):
    snapshots = rds.describe_db_snapshots(DBInstanceIdentifier=instanceid,SnapshotType='manual')['DBSnapshots']
    snapshots= sorted(snapshots , key=lambda x: x['SnapshotCreateTime'],reverse=True)
    snapshot = snapshots[0]  
    newsnapshotid = "-".join([prefix, datetime.now().strftime("%Y-%m-%d-%H-%M")])
    
    response = rds.start_export_task(
        ExportTaskIdentifier= newsnapshotid,
        SourceArn= snapshot['DBSnapshotArn'],
        S3BucketName=[S3のバケット名],
        IamRoleArn=[IAMロール]
        KmsKeyId=[KMSkey],
        S3Prefix=[S3に付けたいプレフィックス名],
        )
    return(response)

def lambda_handler(event, context):
    snapshot_prefix = [スナップショットのprefix名]
    instance = [RDSのインスタンス名]
    export_snapshot(snapshot_prefix, instance)

4. 発火イベントの設定

2で設定したSNSのトピックからの発火を設定して完了 これで今回は朝5時になったらRDSのスナップショットを作成し、 それをS3へエクスポートする仕組みを自動化できた。