🕛

CDK作成のグローバルデータベースリストア簡易化

2025/03/03に公開

CDK作成グローバルデータベースのSnapShotからのリストアを出来る限り簡易化してみた

背景、目的

  • 開発や運用中に時折発生するデータベースのリストア。コンソール上にてぽちぽちする分に簡単ですが、CDK管理下に置いたまま作業をするのは時間が掛かるため、面倒な印象でした
  • 同じような境遇の方がどの程度いるかわかりませんが出来る限り、SnapShotからのリストアを簡易化してみましたので、参考にしていただけますと幸いです

前提事項

  • よくある(自分調べ)本番ワークロードの環境を踏まえた構成並びに設定を想定
    • Aurora Postgres
    • Aurora 関連リソース命名固定
    • 削除保護設定

利用サービス

  • VPC
  • RDS
  • CodeBuild(CDK実行用途)
  • CloudFormation

リストア作業

  • Snapshotの実行
  • SnapshotIdentifierをCDKコードに指定
  • CodeBuild(CDK)実行

CDKでのリソース操作の流れ

  1. Aurora の削除保護を解除(CDKコマンド#1)
  2. 大阪リージョン及び東京リージョン Auroraリソースを全て削除(CDKコマンド#2)
  3. 東京リージョン及び大阪リージョン Auroraリソース復元開始(CDKコマンド#3)

コード解説

全量は GitHub を参照ください。
ここではポイントとなる箇所をかいつまんで解説します。

appファイル

コンテキストにて削除フラグを受け付けられるように作成

const app = new cdk.App();

// VPC
const vpcApne1 = new NetworkStack(app, "NetworkApne1", {
  env: { region: "ap-northeast-1" },
});
const vpcApne3 = new NetworkStack(app, "NetworkApne3", {
  env: { region: "ap-northeast-3" },
});

// Database
const databaseApne1 = new DatabaseStack(app, "DatabaseApne1", {
  env: { region: "ap-northeast-1" },
  vpc: vpcApne1.vpc,
  appName: "test",
  enableDeleteProtection:
    app.node.tryGetContext("protection") === "false" ? false : true,
  snapshotIdentifier:
    app.node.tryGetContext("protection") === "false"
      ? undefined
      : "arn:aws:rds:ap-northeast-1:402803229964:cluster-snapshot:test",
});
const databaseApne3 = new DatabaseStack(app, "DatabaseApne3", {
  env: { region: "ap-northeast-3" },
  vpc: vpcApne3.vpc,
  appName: "test",
  enableDeleteProtection:
    app.node.tryGetContext("protection") === "false" ? false : true,
});

// CodeBuild
const codeBuildApne1 = new CodeBuildStack(app, "CodeBuildApne1", {
  env: { region: "ap-northeast-1" },
});

ネットワーク作成

L2 コンストラクトを使用し、極力値を指定せずに作成

    const vpc = new ec2.Vpc(this, "Vpc", {
      maxAzs: 2,
      subnetConfiguration: [
        {
          cidrMask: 24,
          name: "Public",
          subnetType: ec2.SubnetType.PUBLIC,
        },
        {
          cidrMask: 24,
          name: "Private",
          subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
        },
        {
          cidrMask: 24,
          name: "Isolated",
          subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
        },
      ],
    });

データベース作成

削除保護無効化がPropertyにて簡単に切り替え可能に作成
snapshotIdentifierを指定する際はいくつか削除すべきPropertyがあるため、エスケープハッチにて削除

const auroraCluster = new rds.DatabaseCluster(this, "AuroraCluster", {
      clusterIdentifier: `test-${this.region}-cluster-${props.appName}`,
      engine: rds.DatabaseClusterEngine.auroraPostgres({
        version: rds.AuroraPostgresEngineVersion.VER_16_6,
      }),
      writer: rds.ClusterInstance.provisioned("writer", {
        publiclyAccessible: false,
        instanceType: ec2.InstanceType.of(
          ec2.InstanceClass.R5,
          ec2.InstanceSize.LARGE
        ),
        instanceIdentifier: `test-${this.region}-instance`,
      }),
      vpc: props.vpc,
      vpcSubnets: {
        subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
      },
      storageEncrypted: true,
      deletionProtection: props.enableDeleteProtection,
    });

    const cfnDbCluster = auroraCluster.node.defaultChild as rds.CfnDBCluster;
    if (this.region === "ap-northeast-1") {
      const globalCluster = new rds.CfnGlobalCluster(this, "GlobalCluster", {
        globalClusterIdentifier: "global-cluster",
        sourceDbClusterIdentifier: cfnDbCluster.ref,
        deletionProtection: props.enableDeleteProtection,
      });

      if (props.snapshotIdentifier) {
        cfnDbCluster.snapshotIdentifier = props.snapshotIdentifier;
        // ドキュメント指定のSnapshotIdentifier指定時に削除すべきpropertyを削除
        // https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-rds-dbcluster.html#cfn-rds-dbcluster-snapshotidentifier
        cfnDbCluster.addPropertyDeletionOverride("GlobalClusterIdentifier");
        cfnDbCluster.addPropertyDeletionOverride("MasterUsername");
        cfnDbCluster.addPropertyDeletionOverride("MasterUserPassword");
        cfnDbCluster.addPropertyDeletionOverride("ReplicationSourceIdentifier");
        cfnDbCluster.addPropertyDeletionOverride("RestoreType");
        cfnDbCluster.addPropertyDeletionOverride("SourceDBClusterIdentifier");
        cfnDbCluster.addPropertyDeletionOverride("SourceRegion");
        cfnDbCluster.addPropertyDeletionOverride("StorageEncrypted");
        cfnDbCluster.addPropertyDeletionOverride("UseLatestRestorableTime");
      }
    } else if (this.region === "ap-northeast-3") {
      cfnDbCluster.globalClusterIdentifier = "global-cluster";
      cfnDbCluster.addPropertyDeletionOverride("MasterUsername");
      cfnDbCluster.addPropertyDeletionOverride("MasterUserPassword");
      cfnDbCluster.kmsKeyId = kms.Alias.fromAliasName(
        this,
        "DefaultRdsKey",
        "alias/aws/rds"
      ).keyId;
    }

振り返り

  • 最低限の設定かつBuildSpecの内容もベタ書きのため、そのままコードは利用不可かと思いますがひとまずCodeBuildを一度実行するのみで、CDK作成グローバルデータベースのSnapShotからのリストアが可能なことは検証済みのため、参考にいただけますと幸いです

Discussion