💨

RDSリストア完了後に色々やりたいよね

2024/05/23に公開

最近家のAlexaの調子が悪い。

今までCDKTFネタばかりだったので、今回はAWSに特化した話題でいきます。

想定読者

  • RDSの作成をトリガーとして後続処理を自動化したい人

処理概要

  1. RDSの作成完了をEventBridgeでキャッチ
  2. EventBridgeからStep Functionsを実行する

RDSの作成はどのように実施しているか

RDSの管理はCDK For Terraform で管理しており、
RDSのサフィックス名を別ファイルのパラメータとして管理し、変更が入った場合は
再作成するようにしています。

  • 処理フロー
  1. 対象のRDSスナップショットの最新情報取得
    IndividualConfig.rdsConfig.sourceDbName はAuroraCluster名が入ります。
    // snapshotAuroraの設定
    const snapshotAurora = new aws.dataAwsDbClusterSnapshot.DataAwsDbClusterSnapshot(this, "snapshotAurora", {
      dbClusterIdentifier: IndividualConfig.rdsConfig.sourceDbName,
      mostRecent: true
    });

情報を取得しているスナップショットですが、日次で取得しており、内容が変わるため
lifecycleignoreChangessnapshot_identifier を無視しています。

  • AuroraClusterの作成コードサンプル
 const auroraClusterName =  `${config.projectName}-${process.env.ENV_ID}-cluster${IndividualConfig.rdsConfig.suffix}`;
 const AuroraCluster = new aws.rdsCluster.RdsCluster(this, `auroracluster${IndividualConfig.rdsConfig.suffix}`, { 
      engine: "aurora-postgresql",
      backupRetentionPeriod: 1
      skipFinalSnapshot: true,
      tags: {
        Name: auroraClusterName,
      },
      engineVersion: IndividualConfig.rdsConfig.engineVersion, // Aurora PostgreSQL 14.9 に固定
      snapshotIdentifier: snapshotAurora.dbClusterSnapshotArn, // restore パラメータがtrueであれば最新スナップショットからの復元
      clusterIdentifier: auroraClusterName,
      databaseName: projectName,
      dbClusterParameterGroupName: `${projectName}-${process.env.ENV_ID}-cluster-psql14`,
      vpcSecurityGroupIds: [vpcSecurityGroupIds.id],
      dbSubnetGroupName:  `${projectName}-${process.env.ENV_ID}-subnet`,
      applyImmediately: false, // 即時適用
      masterUsername: MasterUsername, // ユーザー名
      manageMasterUserPassword: ManagePassword, // パスワードを自動生成
      deletionProtection: IndividualConfig.rdsConfig.deletionProtection,
      enabledCloudwatchLogsExports: IndividualConfig.rdsConfig.enabledCloudwatchLogsExports,
      storageType: IndividualConfig.rdsConfig.storageType,
      // Snapshotの変更は無視する。
      lifecycle: {
        createBeforeDestroy: true,
        preventDestroy: false,
        ignoreChanges: ['snapshot_identifier'],
      },
    });

IndividualConfig.rdsConfig.suffixの値を変更することで再作成するようにしていますが、
一工夫としてaws.rdsCluster.RdsCluster のリソース名にも
IndividualConfig.rdsConfig.suffixを含めることで
削除と作成を同時に行うようにし時短を図っています。

含めないと削除が完了してから作成を行うため、接続できない時間が長くなってしまうため。

RDSの作成 をどのようにキャッチしているか

EventBridgeでRDSの作成をキャッチするようにしています。
データベース名の箇所は、AuroraCluster内のインスタンス名が入ります。

{
  "detail": {
    "EventID": ["RDS-EVENT-0005"],
    "SourceIdentifier": ["データベース名"],
    "SourceType": ["DB_INSTANCE"]
  },
  "detail-type": ["RDS DB Instance Event"],
  "source": ["aws.rds"]
}

CDKTFで実装しインスタンス名が変わった場合はSourceIdentifierの値も更新するようにしています。

  • 実装例参考
const auroraInstanceNameBase = sample-auroracluster-instance;
const auroraRule = new aws.cloudwatchEventRule.CloudwatchEventRule(this, "rdsEventRule", {
        name: `aurora-recreate`,
        eventPattern: JSON.stringify({
          source: [
            "aws.rds"
          ],
          "detail-type": [
            "RDS DB Instance Event"
          ],
          detail: {
            "SourceIdentifier": [
              auroraInstanceNameBase
            ],
            "SourceType": [
              "DB_INSTANCE"
            ],
            "EventID": [
              "RDS-EVENT-0005"
            ]
          }
        }),
      });

あとはこのEventBridgeのターゲットにDB作成完了後に実施したい処理を指定するだけなのですが、
問題点がありまして、DBが作成完了しても利用可能な状態になるまで時間がかかるため、
そのまま処理を実行するとエラーが発生してしまいます。

そのためAuroraClusterが実際に利用可能かどうかをチェックする必要がありました。

そのチェック自体はStep Functionsを利用することで実装できました。

Step Functionsでの実装

  • サンプルコード
{
 "Comment": "A description of my state machine",
 "StartAt": "Wait Instance Create",
 "States": {

  "Wait Instance Create": {
   "Type": "Wait",
   "Seconds": 60,
   "Next": "DescribeDBInstances"
  },
  "DescribeDBInstances": {
   "Type": "Task",
   "Parameters": {
    "DbInstanceIdentifier.$": "States.Format('sample-{}-instance{}', $.Env, $.UniqId)"
   },
   "Resource": "arn:aws:states:::aws-sdk:rds:describeDBInstances",
   "ResultPath": "$.describeInstanceResult",
   "Next": "Create Complete?"
  },
  "Create Complete?": {
   "Type": "Choice",
   "Choices": [
    {
     "Variable": "$.describeInstanceResult.DbInstances[0].DbInstanceStatus",
     "StringEquals": "available",
     "Next": "ModifyDBCluster"
    }
   ],
   "Default": "Wait Instance Create"
  },

  "ModifyDBCluster": {
   "Type": "Task",
   "End": true,
   "Parameters": {
    "DbClusterIdentifier.$": "States.Format('sample-{}-cluster{}', $.Env, $.UniqId)",
    "StorageType": "aurora",
    "ApplyImmediately": true
   },
   "Resource": "arn:aws:states:::aws-sdk:rds:modifyDBCluster"
  }
 }
}
  • インプットサンプル
{
  "Env": "test",
  "UniqId": "suffix"
}

処理内容としては
sample-test-clustersuffix というAuroraClusterが
available になるまでループします。

available になったら対象のAuroraClusterのI/O最適化を無効にします。

ちなみにスナップショット元のAuroraClusterのI/O最適化が有効だった場合は
リストア後、I/O最適化が有効になっているので留意です。

今後の改善点

ループ処理回数に制限をかけていないため何十回もループし続けないように
追加実装を検討しています。

あと、一度AuroraClusterがavailable になったとしても再起動処理されることがあったため
リトライ処理などの追加実装も考えています。

まとめ

色々はまりどころがありましたが、
AuroraClusterの仕様の知識やStep Functionsのスキルが向上したので
いい経験になったと感じています。

Goals Tech Blog

Discussion