0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AWS RDSをS3に自動バックアップする方法

Posted at

AWS RDSのスナップショットをS3にエクスポートする自動化の設定を行ったので、備忘録として作業の流れなどを記載します。
作業中に直面した問題とその解決策を記載しています。

1. 目的

AWS RDSのスナップショットをS3へ自動エクスポートし、以下を実現します:

  • アプリデータの保全:RDSスナップショットをS3に保存することで、データを長期的に保管可能。
  • コスト削減:S3のストレージ料金はRDSスナップショットのストレージに保管するより安価で、長期保存でのコスト効率が向上。
  • 運用効率化:古いスナップショットの自動削除で不要なストレージ消費を防止。
  • 完全自動化:LambdaとEventBridgeで、バックアップ作成からエクスポート、削除までを自動化。

2. 使用する技術とその目的

この設定では、以下のAWSサービスを使用します:

  • Amazon RDS

    • 理由:アプリデータを保存するデータベース。スナップショットを作成し、S3にエクスポートする対象。
  • Amazon S3

    • 理由:エクスポートしたスナップショットを低コストで長期間保管するストレージ。
  • AWS Lambda

    • 理由:スナップショットの作成、S3へのエクスポート、古いスナップショットの削除などを自動で実行する関数。
  • Amazon EventBridge

    • 理由:スケジュールベースでLambda関数を定期実行し、バックアップを自動化。
  • AWS KMS

    • 理由:S3にエクスポートするデータを暗号化し、セキュリティを確保。
  • IAM

    • 理由:各サービス間のアクセス権限を管理し、必要なポリシーを付与。

3. 作業手順

コード内の記述補足

<region>: 使用しているAWSリージョンを指定(例: ap-northeast-1)。
<your_account_id>: AWSアカウントIDを指定。
<your_key_id>: 作成したKMSキーのIDを指定。
<your_role_name>: Lambdaで使用するIAMロール名を指定(例: lambda-rds-s3-backup-role)。

Step 1: S3バケットの作成

  1. AWS Management ConsoleS3サービスを開き、新しいバケットを作成する。

    • バケット名は一意の名前を指定(例: my-rds-backup-bucket)。
    • リージョンはRDSと同じリージョンを選択。
    • バケットタイプは汎用を選択。
  2. パブリックアクセスをすべてブロックを有効化する(セキュリティ対策のため推奨)。

補足

  • 新しいバケットを作成する理由
    スナップショット専用バケットを使用することで、他のデータと分離し、管理しやすくなる。
  • フォルダ構造を設定する目的
    データの管理を簡単にするために、exports/のようなプレフィックスを指定することで整理がしやすくなる。
  • KMSキーの設定について
    後で作成するKMSキーを利用してバックアップデータを暗号化するための準備。

Step 2: IAMロールの作成

ロール設定

  1. IAMコンソールで新しいロールを作成。
    • ロール名:lambda-rds-s3-backup-role
    • 信頼されたエンティティタイプ:AWSサービス
    • サービスには以下を指定:
      • Lambda
      • RDS Export Taskexport.rds.amazonaws.com
      • EventBridge Schedulerscheduler.amazonaws.com

信頼関係の設定

作成したロールに以下の信頼関係ポリシーを設定。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "lambda.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        },
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "export.rds.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        },
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "scheduler.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

インラインポリシーの設定

以下のインラインポリシーをロールに追加。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ec2:CreateNetworkInterface",
                "ec2:DescribeNetworkInterfaces",
                "ec2:DeleteNetworkInterface"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:*:*:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "kms:Decrypt",
                "kms:Encrypt",
                "kms:ReEncrypt*",
                "kms:GenerateDataKey*",
                "kms:DescribeKey"
            ],
            "Resource": "arn:aws:kms:<region>:<your_account_id>:key/<your_key_id>"
        },
        {
            "Effect": "Allow",
            "Action": "iam:PassRole",
            "Resource": "arn:aws:iam::<your_account_id>:role/<your_role_name>"
        }
    ]
}

補足

  • なぜiam:PassRoleが必要か
    RDSのS3エクスポートタスクで、IAMロールを引き継ぐために必要。

  • kms:DecryptなどKMS操作が必要な理由
    S3エクスポートで暗号化されたスナップショットを処理するため。

  • logs:*権限の目的
    LambdaがCloudWatch Logsにログを出力するために必要。

  • 信頼関係の重要性
    LambdaやRDS Export Task、EventBridge Schedulerがロールを引き受けられるよう設定する。


Step 3: KMSキーの作成

  1. KMSコンソールを開いて「キーの作成」を選択。

    • キータイプは「対称暗号キー」を選択。
    • 使用法は「暗号化と復号化」を指定。
  2. エイリアスを設定。例:rds-s3-export-key

  3. キーポリシーを設定。以下のポリシーを追加し、Lambdaロールに必要な権限を付与。

    • lambda-rds-s3-backup-roleをキーの使用者として指定。
    • RDS Exportタスクのエクスポート処理でキーを利用するために適切な権限を設定。
{
    "Id": "key-consolepolicy-3",
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Enable IAM User Permissions",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::<your_account_id>:root"
            },
            "Action": "kms:*",
            "Resource": "*"
        },
        {
            "Sid": "Allow access for Key Administrators",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::<your_account_id>:user/<your_user_name>"
            },
            "Action": [
                "kms:Create*",
                "kms:Describe*",
                "kms:Enable*",
                "kms:List*",
                "kms:Put*",
                "kms:Update*",
                "kms:Revoke*",
                "kms:Disable*",
                "kms:Get*",
                "kms:Delete*",
                "kms:TagResource",
                "kms:UntagResource",
                "kms:ScheduleKeyDeletion",
                "kms:CancelKeyDeletion",
                "kms:RotateKeyOnDemand"
            ],
            "Resource": "*"
        },
        {
            "Sid": "Allow use of the key",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::<your_account_id>:role/<your_role_name>"
            },
            "Action": [
                "kms:Encrypt",
                "kms:Decrypt",
                "kms:ReEncrypt*",
                "kms:GenerateDataKey*",
                "kms:DescribeKey"
            ],
            "Resource": "*"
        },
        {
            "Sid": "Allow attachment of persistent resources",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::<your_account_id>:role/<your_role_name>"
            },
            "Action": [
                "kms:CreateGrant",
                "kms:ListGrants",
                "kms:RevokeGrant"
            ],
            "Resource": "*",
            "Condition": {
                "Bool": {
                    "kms:GrantIsForAWSResource": "true"
                }
            }
        }
    ]
}

  1. AWSサービスがキーを利用できるように信頼関係を設定
    • RDS Exportタスク、EventBridge Schedulerの利用を許可。

補足

  • なぜKMSキーが必要か
    RDSのスナップショットを暗号化するため。AWSのセキュリティベストプラクティスに準拠する。

  • どのキーを選ぶべきか
    S3エクスポート専用にカスタマーマネージドキーを作成する。AWS管理キーは利用不可。

  • 許可のポイント
    Lambdaロールに「Decrypt」や「GenerateDataKey」などの操作権限が必要。RDS Exportタスクがキーを利用する権限も設定する。


Step 4: Lambda関数の作成

Lambda関数の設定

  1. AWS Lambdaコンソールで新しい関数を作成。

    • ランタイム:Python 3.x(例: Python 3.9)
    • 実行ロールlambda-rds-s3-backup-role
    • 必要に応じてタイムアウトを延長(推奨: 10分以上)。
  2. コードの記述

以下のようなコードで環境に応じて変数を設定する。<replace_with_...>の部分を適切な値に置き換える。

import boto3
from datetime import datetime as dt

def lambda_handler(event, context):
    rds_client = boto3.client('rds')
    s3_bucket_name = "<replace_with_s3_bucket>"
    s3_prefix = "<replace_with_s3_prefix>"
    kms_key_arn = "<replace_with_kms_key_arn>"
    iam_role_arn = "<replace_with_lambda_role_arn>"
    db_cluster_identifier = "<replace_with_db_cluster_identifier>"
    
    snapshot_identifier = f"{db_cluster_identifier}-snapshot-{dt.now().strftime('%Y%m%d%H%M%S')}"
    now = dt.now()
    
    try:
        # RDSクラスターの状態をチェック
        print("Checking RDS cluster state...")
        cluster_info = rds_client.describe_db_clusters(DBClusterIdentifier=db_cluster_identifier)
        cluster_status = cluster_info['DBClusters'][0]['Status']
        
        if cluster_status != 'available':
            print(f"Cluster is not in available state: {cluster_status}")
            return {
                'statusCode': 400,
                'body': f"Cluster is not in available state: {cluster_status}"
            }

        # 古い手動スナップショットを削除
        print("Checking for old manual snapshots to delete...")
        snapshots = rds_client.describe_db_cluster_snapshots(DBClusterIdentifier=db_cluster_identifier)
        for snapshot in snapshots['DBClusterSnapshots']:
            if snapshot['SnapshotType'] == 'manual':
                creation_time = snapshot['SnapshotCreateTime']
                if (now - creation_time.replace(tzinfo=None)).days > 1:
                    snapshot_id = snapshot['DBClusterSnapshotIdentifier']
                    print(f"Deleting old manual snapshot: {snapshot_id}")
                    rds_client.delete_db_cluster_snapshot(
                        DBClusterSnapshotIdentifier=snapshot_id
                    )

        # 新しいスナップショットを作成
        print(f"Creating new snapshot: {snapshot_identifier}")
        rds_client.create_db_cluster_snapshot(
            DBClusterSnapshotIdentifier=snapshot_identifier,
            DBClusterIdentifier=db_cluster_identifier
        )

        # スナップショットが利用可能になるまで待機
        print("Waiting for snapshot to become available...")
        waiter = rds_client.get_waiter('db_cluster_snapshot_available')
        waiter.wait(
            DBClusterSnapshotIdentifier=snapshot_identifier,
            DBClusterIdentifier=db_cluster_identifier
        )
        print(f"Snapshot {snapshot_identifier} is now available.")

        # スナップショットをS3にエクスポート
        export_identifier = f"{snapshot_identifier}-export"
        print(f"Exporting snapshot {snapshot_identifier} to S3 bucket {s3_bucket_name}...")
        rds_client.start_export_task(
            ExportTaskIdentifier=export_identifier,
            SourceArn=f"arn:aws:rds:<replace_with_region>:<replace_with_account_id>:cluster-snapshot:{snapshot_identifier}",
            S3BucketName=s3_bucket_name,
            IamRoleArn=iam_role_arn,
            KmsKeyId=kms_key_arn,
            S3Prefix=s3_prefix
        )

        print("Export task initiated successfully.")
        return {
            'statusCode': 200,
            'body': f"Snapshot {snapshot_identifier} successfully exported to S3."
        }
    
    except Exception as e:
        print(f"An error occurred: {str(e)}")
        return {
            'statusCode': 500,
            'body': f"Error: {str(e)}"
        }

環境変数の説明

  • <replace_with_s3_bucket>: バックアップ先のS3バケット名。
  • <replace_with_s3_prefix>: 保存時のプレフィックス(例: exports/)。
  • <replace_with_kms_key_arn>: 作成したKMSキーのARN。
  • <replace_with_lambda_role_arn>: 使用するIAMロールのARN。
  • <replace_with_db_cluster_identifier>: RDSクラスターの識別子。
  • <replace_with_region>: AWSリージョン(例: ap-northeast-1)。
  • <replace_with_account_id>: AWSアカウントID。

VPCとセキュリティグループの設定

  1. VPCの設定: LambdaがVPC内のRDSにアクセスする必要がある場合、VPCサブネットを指定する。

    • プライベートサブネットを選択することが推奨される。
  2. セキュリティグループのルール: 必要に応じてインバウンド/アウトバウンドルールを設定する。

    • インバウンドルール: RDSのポート(例: 3306)を許可する。
    • アウトバウンドルール: S3アクセス用にHTTP(80)とHTTPS(443)を許可する。

補足を加えることで、設定ミスや迷いやすいポイントを事前に解消できる。以下の補足を追加すると記事の完成度がさらに向上する。

補足

Lambda関数のタイムアウト設定

スナップショットの作成やエクスポートは時間がかかる場合があるため、Lambdaのタイムアウトを10分以上に設定することを推奨。デフォルトの3秒では処理が途中で終了してしまう可能性が高い。

RDSクラスターの状態チェックの重要性

RDSがavailable以外の状態(例: backing-upstopped)のときにはスナップショット作成が失敗する。このため、コード内で状態を確認し、利用可能でない場合は処理をスキップする仕組みが含まれている。

VPCとセキュリティグループの注意点

Lambda関数がVPC内で動作する場合は、以下に注意する:

  • サブネット選択: プライベートサブネットを選ぶことでセキュリティを確保する。
  • セキュリティグループの設定ミス: RDSへのインバウンドポート(例: 3306)が正しく設定されていないと通信エラーが発生する。アウトバウンドでS3やKMSへのアクセスが必要な点も見落としやすい。

KMSキーの設定について

S3にエクスポートする際、**暗号化キーのARN(KMS Key ARN)**が必須。KMSキーのポリシーでLambdaロールを許可していないとエラーが発生する。具体的にはkms:GenerateDataKeykms:Decryptの許可が必要。

IAMロールのiam:PassRole許可

エラー「User is not authorized to perform: iam:PassRole」が出た場合、LambdaのロールがIAMロールの委任を許可されていない可能性がある。インラインポリシーで適切に設定する必要がある。


Step 5: EventBridgeスケジュールの設定

スケジュールの作成

  1. EventBridgeコンソールで「スケジュール」を選択し、新規スケジュールを作成する。
    名前は「rds-s3-snapshot-schedule」など、目的が分かりやすいものにする。

スケジュールの設定

  • スケジュールの種類: cron式を選択。
  • cron式の例:
    0 18 * * ? *(毎日日本時間の午前3時に実行)。
    • cron式はUTCで記述する必要がある。日本時間を指定する場合、-9時間した時刻を設定する(例: 午前3時はUTCで18時)。

ターゲットの指定

  1. ターゲットに「AWS Lambda」を選択し、Step 4で作成したLambda関数を指定する。
  2. ペイロードは空欄のままで問題ない。

動作確認

  1. 次回トリガー日時を確認
    EventBridgeの「スケジュール詳細」ページで、次回実行日時を確認する。
  2. CloudWatch Logsで実行結果を確認
    スケジュールが実行された際にLambda関数が正常に動作したかをCloudWatch Logsで確認する。

補足

  • cron式の詳細例

    • 0 0 * * ? * : 毎日午前0時(UTC)。
    • 0 9 * * ? * : 毎日午前9時(UTC)。
    • 0 18 * * ? * : 毎日日本時間午前3時(UTCの18時)。
  • トリガーがLambdaコンソールに表示されない場合
    EventBridgeで正しくスケジュールが作成されていても、Lambdaコンソールのトリガー一覧に反映されない場合がある。
    この場合でもスケジュールは正常に動作するため、EventBridgeのスケジュール画面で次回実行日時を確認すること。

  • スケジュール実行の再試行設定
    EventBridgeにはデフォルトで再試行ポリシーが設定されており、一時的なエラー発生時に自動的に再試行が行われる。必要に応じて再試行回数や期間を調整することで、より安定した実行環境を構築できる。

4. 確認手順

  1. CloudWatch LogsでLambda関数のログを確認。
  2. S3バケットにバックアップファイルが保存されていることを確認。
  3. RDS スナップショットで古いスナップショットが削除されていることを確認。
0
0
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
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?