0
1

More than 3 years have passed since last update.

RDSを低コストでDRリージョンにバックアップ・リストアする

Last updated at Posted at 2021-03-31

概要

以下サイト内容を実施してみました。
簡単に言うと、RDSの障害時にLambdaを使って、
DRリージョンにスナップショットからRDSを復元します。

構成図

3つのLambdaを使用しています。
image.png

前提条件

RDSはソースリージョンで作成しておきます。
ターゲットリージョンではDBサブネットグループのみ作成しておきます。

目次

  1. Lambda用IAMロールの作成
  2. 【①RDSスナップショット取得】のLambda
    1. Lambda作成
    2. CloudWatch イベントでスケジュールの設定をする
  3. 【②DRリージョンにスナップショットコピー】のLambda
    1. Lambda作成
    2. SNSトピックを作成
    3. RDSイベントサブスクリプションを作成
  4. 【③DRリージョンにRDS復元】のLambda
    1. Lambda作成
    2. SNSトピックを作成
    3. RDSイベントサブスクリプションを作成

手順

1. Lambda用IAMロールの作成

まずはIAMロールを作っておきます。

今回は以下のポリシーを適用します。
 (実際に使う場合は最小権限にしてください)

ロール名を入力してロールの作成を押下
image.png

2.【①RDSスナップショット取得】のLambda

それでは以下の部分を作っていきましょう。

2-1.Lambda作成

Lambda作成の際は、1で作ったIAMロールを付与します。

コードは冒頭のサイト参照

import botocore  
import datetime  
import re  
import logging
import boto3

#以下は自環境に合わせて書き換えてください
region='ap-northeast-1'  
db_instance_class='db.t2.micro'  
db_subnet='default-vpc-077737b0bf23840f2'  
instances = ['irikura-db-1']

print('Loading function')

def lambda_handler(event, context):  
     source = boto3.client('rds', region_name=region)
     for instance in instances:
         try:
             #timestamp1 = '{%Y-%m-%d %H:%M:%S}'.format(datetime.datetime.now())
             timestamp1 = str(datetime.datetime.now().strftime('%Y-%m-%d-%H-%-M-%S')) + "lambda-snap"
             snapshot = "{0}-{1}-{2}".format("mysnapshot", instance,timestamp1)
             response = source.create_db_snapshot(DBSnapshotIdentifier=snapshot, DBInstanceIdentifier=instance)
             print(response)
         except botocore.exceptions.ClientError as e:
             raise Exception("Could not create snapshot: %s" % e)

2-2.CloudWatch イベントでスケジュールの設定をする

CloudWatchのイベント→ルールに移動します。

ルールの作成を押下し、

イベントソースはスケジュールにチェックを入れ、任意の間隔を指定します。
ターゲットは上記で作成したLambda関数を選択します。
image.png

ルールが作成できたら、
指定の間隔でRDSのスナップショットが作成されることを確認しておきます。

3.【②DRリージョンにスナップショットコピー】のLambda

スナップショットが作成された際のRDSイベントから、
SNSでLambdaを起動してDRリージョンにスナップショットをコピーします。

3-1.Lambda作成

Lambda作成の際は、1で作ったIAMロールを付与します。

コードは冒頭のサイト参照

import boto3  
import botocore  
import datetime  
import re
import json

#以下は自環境に合わせて書き換えてください   
SOURCE_REGION = 'ap-northeast-1'  
TARGET_REGION = 'us-east-1'  
iam = boto3.client('iam')  
instances = ['irikura-db-1']

print('Loading function')

def byTimestamp(snap):  
  if 'SnapshotCreateTime' in snap:
    return datetime.datetime.isoformat(snap['SnapshotCreateTime'])
  else:
    return datetime.datetime.isoformat(datetime.datetime.now())

def lambda_handler(event, context):  
    print("Received event: " + json.dumps(event, indent=2))
    account_ids = []
    try:
        iam.get_user()
    except Exception as e:
        account_ids.append(re.search(r'(arn:aws:sts::)([0-9]+)', str(e)).groups()[1])
        account = account_ids[0]

    source = boto3.client('rds', region_name=SOURCE_REGION)

    for instance in instances:
        source_instances = source.describe_db_instances(DBInstanceIdentifier= instance)
        source_snaps = source.describe_db_snapshots(DBInstanceIdentifier=instance)['DBSnapshots']
        source_snap = sorted(source_snaps, key=byTimestamp, reverse=True)[0]['DBSnapshotIdentifier']
        source_snap_arn = 'arn:aws:rds:%s:%s:snapshot:%s' % (SOURCE_REGION, account, source_snap)
        target_snap_id = (re.sub('rds:', '', source_snap))
        print('Will Copy %s to %s' % (source_snap_arn, target_snap_id))
        target = boto3.client('rds', region_name=TARGET_REGION)

        try:
            response = target.copy_db_snapshot(
            SourceDBSnapshotIdentifier=source_snap_arn,
            TargetDBSnapshotIdentifier=target_snap_id,
            CopyTags = True)
            print(response)
        except botocore.exceptions.ClientError as e:
            raise Exception("Could not issue copy command: %s" % e)
        copied_snaps = target.describe_db_snapshots(SnapshotType='manual', DBInstanceIdentifier=instance)['DBSnapshots']

3-2.SNSトピックを作成

以下のSNSトピックを作成し、

サブスクリプションに3-1で作成したLambdaを指定します。

3-3.RDSイベントサブスクリプションを作成

RDSに移動しイベントサブスクリプションを選択する。

イベントサブスクリプションの作成で名前を入力し、
ターゲットに3-2で作成したSNSトピックを指定する。

ソースは以下のように設定することで、スナップショット作成時に
SNSに通知がいき、SNSからLambdaを起動することができます。

4.【③DRリージョンにRDS復元】のLambda

3の手順と同じように障害などのRDSイベントからSNSを利用してLambdaを起動させ、
DRリージョンでスナップショットからRDSを復元します。
image.png

4-1.Lambda作成

Lambda作成の際は、1で作ったIAMロールを付与します。

コードは冒頭のサイト参照

import boto3  
import botocore  
import datetime  
import re  
import logging

#以下は自環境に合わせて書き換えてください   
region='us-east-1'  
db_instance_class='db.t2.micro' 
db_subnet='test'  
#db_subnet='subnet-12345d6a'
instances = ['irikura-db-1']

print('Loading function')

def byTimestamp(snap):  
  if 'SnapshotCreateTime' in snap:
    return datetime.datetime.isoformat(snap['SnapshotCreateTime'])
  else:
    return datetime.datetime.isoformat(datetime.datetime.now())

def lambda_handler(event, context):  
    source = boto3.client('rds', region_name=region)
    for instance in instances:
        try:
            source_snaps = source.describe_db_snapshots(DBInstanceIdentifier = instance)['DBSnapshots']
            print ("DB_Snapshots:", source_snaps)
            source_snap = sorted(source_snaps, key=byTimestamp, reverse=True)[0]['DBSnapshotIdentifier']
            snap_id = (re.sub( '-\d\d-\d\d-\d\d\d\d ?', '', source_snap))
            print('Will restore %s to %s' % (source_snap, snap_id))
            response = source.restore_db_instance_from_db_snapshot(DBInstanceIdentifier=snap_id,DBSnapshotIdentifier=source_snap,DBInstanceClass=db_instance_class,DBSubnetGroupName=db_subnet,MultiAZ=False,PubliclyAccessible=False)
            print(response)

        except botocore.exceptions.ClientError as e:
            raise Exception("Could not restore: %s" % e)

4-2.SNSトピックを作成

以下のSNSトピックを作成し、

サブスクリプションに4-1で作成したLambdaを指定します。

4-3.RDSイベントサブスクリプションを作成

RDSに移動しイベントサブスクリプションを選択する。

イベントサブスクリプションの作成で名前を入力し、
ターゲットに4-2で作成したSNSトピックを指定する。

ソースは以下のように設定することで、スナップショット作成時に
SNSに通知がいき、SNSからLambdaを起動することができます。

詰まったところ

IAMロールはサービスリンクドロールをLambdaに使用すると、
CloudWatch logsにログが出力されていませんでした。
なので最初にIAMロールを作っておきましょう。

以下参照

気になったところ

 RDSのイベント通知が複数回くるので(例えばスナップショット取得開始・完了でそれぞれ)、その度にLambdaが起動している。
 SNSサブスクリプションフィルターで指定の値のみ通知するようなフィルター機能はあるが、部分一致などの指定はできない。Lambda内に処理を加えた方がよいかも。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1