[AWS CDK] Aurora Serverless v2をL2 Constructで定義できるようになりました

AWS CDKで簡単にAurora Serveless v2を含むDBクラスターを定義できるようになった
2023.06.04

Aurora Serverless v2をL2 Constuctで定義したいな

こんにちは、のんピ(@non____97)です。

皆さんはAWS CDKのAurora Serverless v2のL2 Constructが欲しいなと思ったことはありますか? 私はあります。

今までAWS CDKでAurora Serverless v2のインスタンスを作成する際は、Escape hatcheを使う必要がありました。

AWS CDK v2.82.0からAurora Serverless v2をL2 Constructで定義できるようになりました。

これに関連してWriterとReaderで異なる設定を定義できるようになりました。

これにより、WriterはProvisioned Instance、ReaderはAurora Serverless v2というような構成も簡単に定義できます。

実際に試してみたので紹介します。

いきなりまとめ

  • Aurora Serverless v2インスタンスを含むDBクラスターを作成できるようになった
  • Writer / Readerの各インスタンス毎に設定を定義可能になった
    • instancePropsinstancesは非推奨になった
  • instancePropsを使って作成したDBインスタンスを新記法に移行して、引き続き使用することも可能
    • isFromLegacyInstancePropstrueにする
    • instanceIdentifierBaseが動作しなくなるため、DBインスタンス識別子instanceIdentifierを明示的に指定すること
    • 拡張モニタリングを有効化した場合に自動で拡張モニタリング用のIAMロールが作成されなくなるので注意
  • AWS CDKでWriter / Readerを入れ替えて定義してもフェイルオーバーは発生しない
    • 手動でのフェイルオーバーが必要

ADRとAPI Referenceを眺める

Architecture Decision Record (ADR)とAPI Referenceを眺めてみます。

Provisioned InstanceとAurora Serverless v2が混在するDBクラスターを作成できるようになったと記載がありますね。

Recently RDS Aurora clusters added support for Aurora Serverless V2. Prior to that there were two types of clusters that you could create.

  1. A Serverless v1 cluster
  2. A provisioned cluster

Each of these clusters only supported a single type of DB instance (serverless or provisioned) and it was not possible to mix the two types of instances together. With the addition of Serverless V2 it is now possible to create a cluster which has both provisioned instances and serverless v2 instances.

aws-cdk/aurora-serverless-v2.md at main · aws/aws-cdk · GitHub

具体的にはWriterはwriter、Readerはreadersという配列に各インスタンスの定義をします。

new rds.DatabaseCluster(this, 'Cluster', {
  engine: rds.DatabaseClusterEngine.auroraMysql({ version: rds.AuroraMysqlEngineVersion.VER_3_03_0 }),
  // capacity applies to all serverless instances in the cluster
  serverlessV2MaxCapacity: 1,
  serverlessV2MinCapacity: 0.5,
  writer: ClusterInstance.provisioned('writer', { ...props }),
  readers: [
    // puts it in promition tier 0-1
    ClusterInstance.serverlessV2('reader1', { scaleWithWriter: true, ...additionalProps }),
    ClusterInstance.serverlessV2('reader2'),
    ClusterInstance.serverlessV2('reader3'),
    // illustrating how it might be possible to add support for groups in the future.
    // currently not supported by CFN
    ClusterInstance.fromReaderGroup('analytics', { ...readerProps }),
  },
});

これにより従来はinstancePropsで全てのDBインスタンスの設定をしていましたが、各DBインスタンスごとに定義できるようになりました。

それに伴いinstancePropsinstancesは非推奨となっています。

writer、もしくはreadersをProvisioned Instanceにする場合はClusterInstance.provisionedを指定します。プロパティはProvisionedClusterInstancePropsです。

Aurora Serverless v2にする場合はClusterInstance. serverlessV2を指定します。プロパティはServerlessV2ClusterInstancePropsです。

やってみる

Aurora Provisioned Instanceが1つのみDBクラスターを作成

実際に試してみましょう。

まず、Aurora Provisioned Instanceが1つのみDBクラスターを作成を作成します。

./lib/constructs/aurora.ts

import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";

export interface AuroraProps {
  vpc: cdk.aws_ec2.IVpc;
}

export class Aurora extends Construct {
  constructor(scope: Construct, id: string, props: AuroraProps) {
    super(scope, id);

    // DB Cluster Parameter Group
    const dbClusterParameterGroup = new cdk.aws_rds.ParameterGroup(
      this,
      "DbClusterParameterGroup",
      {
        engine: cdk.aws_rds.DatabaseClusterEngine.auroraPostgres({
          version: cdk.aws_rds.AuroraPostgresEngineVersion.VER_15_2,
        }),
        description: "aurora-postgresql15",
        parameters: {
          log_statement: "none",
          "pgaudit.log": "all",
          "pgaudit.role": "rds_pgaudit",
          shared_preload_libraries: "pgaudit",
        },
      }
    );

    // DB Parameter Group
    const dbParameterGroup = new cdk.aws_rds.ParameterGroup(
      this,
      "DbParameterGroup",
      {
        engine: cdk.aws_rds.DatabaseClusterEngine.auroraPostgres({
          version: cdk.aws_rds.AuroraPostgresEngineVersion.VER_15_2,
        }),
        description: "aurora-postgresql15",
      }
    );

    // Subnet Group
    const subnetGroup = new cdk.aws_rds.SubnetGroup(this, "SubnetGroup", {
      description: "description",
      vpc: props.vpc,
      subnetGroupName: "SubnetGroup",
      vpcSubnets: props.vpc.selectSubnets({
        onePerAz: true,
        subnetType: cdk.aws_ec2.SubnetType.PRIVATE_ISOLATED,
      }),
    });

    // DB Cluster
    const clusterIdentifier = "db-cluster";
    new cdk.aws_rds.DatabaseCluster(this, "Default", {
      engine: cdk.aws_rds.DatabaseClusterEngine.auroraPostgres({
        version: cdk.aws_rds.AuroraPostgresEngineVersion.VER_15_2,
      }),
      instanceProps: {
        vpc: props.vpc,
        allowMajorVersionUpgrade: false,
        autoMinorVersionUpgrade: true,
        deleteAutomatedBackups: true,
        enablePerformanceInsights: true,
        instanceType: cdk.aws_ec2.InstanceType.of(
          cdk.aws_ec2.InstanceClass.BURSTABLE3,
          cdk.aws_ec2.InstanceSize.MEDIUM
        ),
        parameterGroup: dbParameterGroup,
        performanceInsightRetention:
          cdk.aws_rds.PerformanceInsightRetention.DEFAULT,
        publiclyAccessible: false,
      },
      backup: {
        retention: cdk.Duration.days(7),
        preferredWindow: "16:00-16:30",
      },
      cloudwatchLogsExports: ["postgresql"],
      cloudwatchLogsRetention: cdk.aws_logs.RetentionDays.ONE_YEAR,
      clusterIdentifier,
      copyTagsToSnapshot: true,
      credentials: {
        username: "postgresAdmin",
        excludeCharacters: ":@/\" '",
        secretName: `${clusterIdentifier}/postgresAdmin`,
      },
      defaultDatabaseName: "testDB",
      deletionProtection: false,
      iamAuthentication: false,
      instances: 1,
      monitoringInterval: cdk.Duration.minutes(1),
      parameterGroup: dbClusterParameterGroup,
      preferredMaintenanceWindow: "Sat:17:00-Sat:17:30",
      storageEncrypted: true,
      instanceIdentifierBase: "db-instance",
      subnetGroup,
    });
  }
}

デプロイ後、DBクラスターを確認します。

Aurora Provisioned Instanceが1つのみDBクラスターを作成

DBインスタンスが1台のみのDBクラスターが作成されていますね。

また、DBインスタンスのリソースIDを控えておきましょう。リソースIDはdb-PEHAJGCIDZXBVLLCX3DNCTH4F4です。

DBインスタンスの詳細

Aurora Provisioned InstanceがWriterで、Aurora Serverless v2がReaderのDBクラスターに変更

次にAurora Serveless v2のReaderを追加します。

Writerは先ほど作成したDBインスタンスをそのまま使用します。

Writerは元々InstancePropsで指定していた内容を設定します。また、ReaderにはあえてautoMinorVersionUpgrade: falseを設定するなど、Writerと設定を変えてみました。

./lib/constructs/aurora.ts

    // DB Cluster
    const clusterIdentifier = "db-cluster";
    new cdk.aws_rds.DatabaseCluster(this, "Default", {
      engine: cdk.aws_rds.DatabaseClusterEngine.auroraPostgres({
        version: cdk.aws_rds.AuroraPostgresEngineVersion.VER_15_2,
      }),
      writer: cdk.aws_rds.ClusterInstance.provisioned("Instance1", {
        instanceType: cdk.aws_ec2.InstanceType.of(
          cdk.aws_ec2.InstanceClass.T3,
          cdk.aws_ec2.InstanceSize.MEDIUM
        ),
        allowMajorVersionUpgrade: false,
        autoMinorVersionUpgrade: true,
        enablePerformanceInsights: true,
        parameterGroup: dbParameterGroup,
        performanceInsightRetention:
          cdk.aws_rds.PerformanceInsightRetention.DEFAULT,
        publiclyAccessible: false,
      }),
      readers: [
        cdk.aws_rds.ClusterInstance.serverlessV2("Instance2", {
          autoMinorVersionUpgrade: false,
        }),
      ],
      serverlessV2MaxCapacity: 1.0,
      serverlessV2MinCapacity: 0.5,
      backup: {
        retention: cdk.Duration.days(7),
        preferredWindow: "16:00-16:30",
      },
      cloudwatchLogsExports: ["postgresql"],
      cloudwatchLogsRetention: cdk.aws_logs.RetentionDays.ONE_YEAR,
      clusterIdentifier,
      copyTagsToSnapshot: true,
      credentials: {
        username: "postgresAdmin",
        excludeCharacters: ":@/\" '",
        secretName: `${clusterIdentifier}/postgresAdmin`,
      },
      defaultDatabaseName: "testDB",
      deletionProtection: false,
      iamAuthentication: false,
      monitoringInterval: cdk.Duration.minutes(1),
      parameterGroup: dbClusterParameterGroup,
      preferredMaintenanceWindow: "Sat:17:00-Sat:17:30",
      storageEncrypted: true,
      instanceIdentifierBase: "db-instance",
      vpc: props.vpc,
      subnetGroup,
    });

この状態でdiffを確認します。

$ npx cdk diff
[Warning at /AuroraStack/Aurora/Default] Cluster Default only has serverless readers and no reader is in promotion tier 0-1.Serverless readers in promotion tiers >= 2 will NOT scale with the writer, which can lead to availability issues if a failover event occurs. It is recommended that at least one reader has `scaleWithWriter` set to true
Stack AuroraStack
Resources
[-] AWS::IAM::Role Aurora/Default/MonitoringRole AuroraMonitoringRoleA20D2144 destroy
[-] AWS::RDS::DBInstance Aurora/Default/Instance1 AuroraInstance10510C5D1 destroy
[+] AWS::RDS::DBInstance Aurora/Default/Instance1 AuroraInstance166EB641A
[+] AWS::RDS::DBInstance Aurora/Default/Instance2 AuroraInstance2E52621EF
[~] AWS::RDS::DBCluster Aurora/Default Aurora2CBAB212
 └─ [+] ServerlessV2ScalingConfiguration
     └─ {"MaxCapacity":1,"MinCapacity":0.5}

Writerの論理IDが変更され、このままデプロイすると削除されそうです。

ClusterInstancePropsのプロパティを確認するとisFromLegacyInstancePropstrueにすることで、instancePropsからwriterreadersを使った定義方法に移行することが可能なようです。

isFromLegacyInstancePropstrueにしてあげます。

./lib/constructs/aurora.ts

      writer: cdk.aws_rds.ClusterInstance.provisioned("Instance1", {
        instanceType: cdk.aws_ec2.InstanceType.of(
          cdk.aws_ec2.InstanceClass.T3,
          cdk.aws_ec2.InstanceSize.MEDIUM
        ),
        allowMajorVersionUpgrade: false,
        autoMinorVersionUpgrade: true,
        enablePerformanceInsights: true,
        parameterGroup: dbParameterGroup,
        performanceInsightRetention:
          cdk.aws_rds.PerformanceInsightRetention.DEFAULT,
        publiclyAccessible: false,
        isFromLegacyInstanceProps: true,
      }),

この状態でdiffを確認します。

$ npx cdk diff
[Warning at /AuroraStack/Aurora/Default] Cluster Default only has serverless readers and no reader is in promotion tier 0-1.Serverless readers in promotion tiers >= 2 will NOT scale with the writer, which can lead to availability issues if a failover event occurs. It is recommended that at least one reader has `scaleWithWriter` set to true
Stack AuroraStack
Resources
[-] AWS::IAM::Role Aurora/Default/MonitoringRole AuroraMonitoringRoleA20D2144 destroy
[+] AWS::RDS::DBInstance Aurora/Default/Instance2 AuroraInstance2E52621EF
[~] AWS::RDS::DBCluster Aurora/Default Aurora2CBAB212
 └─ [+] ServerlessV2ScalingConfiguration
     └─ {"MaxCapacity":1,"MinCapacity":0.5}
[~] AWS::RDS::DBInstance Aurora/Default/Instance1 AuroraInstance10510C5D1 replace
 ├─ [-] DBInstanceIdentifier (requires replacement)
 │   └─ db-instance1
 ├─ [-] DeleteAutomatedBackups
 │   └─ true
 ├─ [-] MonitoringRoleArn
 │   └─ {"Fn::GetAtt":["AuroraMonitoringRoleA20D2144","Arn"]}
 └─ [-] PubliclyAccessible
     └─ false

destroyではなくなりましたが、replaceとなっていますね。これはDBインスタンスの識別子が明示的に指定されていないためのようです。

writerreadersを使った定義方法だとinstanceIdentifierBaseが効かないのでしょうか。

AWS CDKのコードを確認してみます。

cluster.tsを確認すると、legacyCreateInstancesという関数内でinstanceIdentifierBaseを使ってDBインスタンス識別子を生成していました。

aws-cdk/packages/aws-cdk-lib/aws-rds/lib/cluster.ts

  for (let i = 0; i < instanceCount; i++) {
    const instanceIndex = i + 1;
    const instanceIdentifier = props.instanceIdentifierBase != null ? `${props.instanceIdentifierBase}${instanceIndex}` :
      props.clusterIdentifier != null ? `${props.clusterIdentifier}instance${instanceIndex}` :
        undefined;

ただし、legacyCreateInstancesを使用してDBインスタンスを作成するのはプロパティにwriterを使用していない場合のようです。

aws-cdk/packages/aws-cdk-lib/aws-rds/lib/cluster.ts

    const createdInstances = props.writer ? this._createInstances(props) : legacyCreateInstances(this, props, this.subnetGroup);
    this.instanceIdentifiers = createdInstances.instanceIdentifiers;
    this.instanceEndpoints = createdInstances.instanceEndpoints;

そのため、writerreadersを使った定義方法だとinstanceIdentifierBaseは意味をなさないようですね。

対応としてWriterのプロパティでDBインスタンス識別子を明示的に指定します。

./lib/constructs/aurora.ts

      writer: cdk.aws_rds.ClusterInstance.provisioned("Instance1", {
        instanceType: cdk.aws_ec2.InstanceType.of(
          cdk.aws_ec2.InstanceClass.T3,
          cdk.aws_ec2.InstanceSize.MEDIUM
        ),
        allowMajorVersionUpgrade: false,
        autoMinorVersionUpgrade: true,
        enablePerformanceInsights: true,
        parameterGroup: dbParameterGroup,
        performanceInsightRetention:
          cdk.aws_rds.PerformanceInsightRetention.DEFAULT,
        publiclyAccessible: false,
        isFromLegacyInstanceProps: true,
        instanceIdentifier: "db-instance1",
      }),

この状態でdiffを確認します。

$ npx cdk diff
[Warning at /AuroraStack/Aurora/Default] Cluster Default only has serverless readers and no reader is in promotion tier 0-1.Serverless readers in promotion tiers >= 2 will NOT scale with the writer, which can lead to availability issues if a failover event occurs. It is recommended that at least one reader has `scaleWithWriter` set to true
Stack AuroraStack
Resources
[-] AWS::IAM::Role Aurora/Default/MonitoringRole AuroraMonitoringRoleA20D2144 destroy
[+] AWS::RDS::DBInstance Aurora/Default/Instance2 AuroraInstance2E52621EF
[~] AWS::RDS::DBCluster Aurora/Default Aurora2CBAB212
 └─ [+] ServerlessV2ScalingConfiguration
     └─ {"MaxCapacity":1,"MinCapacity":0.5}
[~] AWS::RDS::DBInstance Aurora/Default/Instance1 AuroraInstance10510C5D1
 ├─ [-] DeleteAutomatedBackups
 │   └─ true
 ├─ [-] MonitoringRoleArn
 │   └─ {"Fn::GetAtt":["AuroraMonitoringRoleA20D2144","Arn"]}
 └─ [-] PubliclyAccessible
     └─ false

replaceではなくなりました。

しかし、Warningが気になりますね。

これはReaderにPromotion Tierが2以上のAurora Servelrss v2がいないことにより発生します。

Aurora Servelrss v2のDBインスタンスはPriority Tierが0または1の場合、Writerと同等のスペックを保つようスケールします。2以上の場合はWriterの負荷と連動してスケーリングしません。

そのため、「Writerの負荷と連動してスケールしない場合は、フェイルオーバーすると可用性の問題に繋がる可能性がある」とメッセージを出力してくれています。

こちらの挙動は以下記事でも紹介されています。

こちらを回避するためには最低1台のReaderでscaleWithWritertrueとします。

./lib/constructs/aurora.ts

      readers: [
        cdk.aws_rds.ClusterInstance.serverlessV2("Instance2", {
          autoMinorVersionUpgrade: false,
          scaleWithWriter: true,
        }),
      ],

この状態でdiffを確認します。

$ npx cdk diff
Stack AuroraStack
Resources
[-] AWS::IAM::Role Aurora/Default/MonitoringRole AuroraMonitoringRoleA20D2144 destroy
[+] AWS::RDS::DBInstance Aurora/Default/Instance2 AuroraInstance2E52621EF
[~] AWS::RDS::DBCluster Aurora/Default Aurora2CBAB212
 └─ [+] ServerlessV2ScalingConfiguration
     └─ {"MaxCapacity":1,"MinCapacity":0.5}
[~] AWS::RDS::DBInstance Aurora/Default/Instance1 AuroraInstance10510C5D1
 ├─ [-] DeleteAutomatedBackups
 │   └─ true
 ├─ [-] MonitoringRoleArn
 │   └─ {"Fn::GetAtt":["AuroraMonitoringRoleA20D2144","Arn"]}
 └─ [-] PubliclyAccessible
     └─ false

Warningも消えましたね。これでデプロイしようとしてみます。

$ npx cdk deploy

✨  Synthesis time: 10.49s

AuroraStack:  start: Building b00b7e39cdd61b7b0384966b406b06406397d6105e80e50a8fb2ce986e8f6d73:current_account-current_region
AuroraStack:  success: Built b00b7e39cdd61b7b0384966b406b06406397d6105e80e50a8fb2ce986e8f6d73:current_account-current_region
AuroraStack:  start: Publishing b00b7e39cdd61b7b0384966b406b06406397d6105e80e50a8fb2ce986e8f6d73:current_account-current_region
AuroraStack:  success: Published b00b7e39cdd61b7b0384966b406b06406397d6105e80e50a8fb2ce986e8f6d73:current_account-current_region
AuroraStack: deploying... [1/1]
AuroraStack: creating CloudFormation changeset...
10:47:28 | CREATE_FAILED        | AWS::RDS::DBInstance                        | AuroraInstance2E52621EF
Resource handler returned message: "A MonitoringRoleARN value is required if you specify a MonitoringInterval value other th
an 0. (Service: Rds, Status Code: 400, Request ID: 5f3e7862-fdba-4941-9eba-eb2dc15f503e)" (RequestToken: 823bebb6-ec59-68e0-
5fc2-39f609363192, HandlerErrorCode: InvalidRequest)


 ❌  AuroraStack failed: Error: The stack named AuroraStack failed to deploy: UPDATE_ROLLBACK_COMPLETE: Resource handler returned message: "A MonitoringRoleARN value is required if you specify a MonitoringInterval value other than 0. (Service: Rds, Status Code: 400, Request ID: 5f3e7862-fdba-4941-9eba-eb2dc15f503e)" (RequestToken: 823bebb6-ec59-68e0-5fc2-39f609363192, HandlerErrorCode: InvalidRequest)
    at FullCloudFormationDeployment.monitorDeployment (/<ディレクトリパス>aurora/node_modules/aws-cdk/lib/index.js:397:10236)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async Object.deployStack2 [as deployStack] (/<ディレクトリパス>aurora/node_modules/aws-cdk/lib/index.js:400:149977)
    at async /<ディレクトリパス>aurora/node_modules/aws-cdk/lib/index.js:400:135508

 ❌ Deployment failed: Error: The stack named AuroraStack failed to deploy: UPDATE_ROLLBACK_COMPLETE: Resource handler returned message: "A MonitoringRoleARN value is required if you specify a MonitoringInterval value other than 0. (Service: Rds, Status Code: 400, Request ID: 5f3e7862-fdba-4941-9eba-eb2dc15f503e)" (RequestToken: 823bebb6-ec59-68e0-5fc2-39f609363192, HandlerErrorCode: InvalidRequest)
    at FullCloudFormationDeployment.monitorDeployment (/<ディレクトリパス>aurora/node_modules/aws-cdk/lib/index.js:397:10236)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async Object.deployStack2 [as deployStack] (/<ディレクトリパス>aurora/node_modules/aws-cdk/lib/index.js:400:149977)
    at async /<ディレクトリパス>aurora/node_modules/aws-cdk/lib/index.js:400:135508

The stack named AuroraStack failed to deploy: UPDATE_ROLLBACK_COMPLETE: Resource handler returned message: "A MonitoringRoleARN value is required if you specify a MonitoringInterval value other than 0. (Service: Rds, Status Code: 400, Request ID: 5f3e7862-fdba-4941-9eba-eb2dc15f503e)" (RequestToken: 823bebb6-ec59-68e0-5fc2-39f609363192, HandlerErrorCode: InvalidRequest)

はい、とてつもなく怒られました。

どうやら拡張モニタリングで使用するロールが指定されていないようです。

またまた、AWS CDKのコードを確認してみます。

cluster.tsを確認すると、legacyCreateInstancesという関数内でmonitoringRoleが指定されていない場合に、拡張モニタリング用のRoleを作成していました。

aws-cdk/packages/aws-cdk-lib/aws-rds/lib/cluster.ts

  let monitoringRole;
  if (props.monitoringInterval && props.monitoringInterval.toSeconds()) {
    monitoringRole = props.monitoringRole || new Role(cluster, 'MonitoringRole', {
      assumedBy: new ServicePrincipal('monitoring.rds.amazonaws.com'),
      managedPolicies: [
        ManagedPolicy.fromAwsManagedPolicyName('service-role/AmazonRDSEnhancedMonitoringRole'),
      ],
    });
  }

ただし、先述の通りlegacyCreateInstancesを使用してDBインスタンスを作成するのはプロパティにwriterを使用していない場合です。

ということで明示的に拡張モニタリング用のロールを定義します。

./lib/constructs/aurora.ts

    // Monitoring Role
    const monitoringRole = new cdk.aws_iam.Role(this, "MonitoringRole", {
      assumedBy: new cdk.aws_iam.ServicePrincipal(
        "monitoring.rds.amazonaws.com"
      ),
      managedPolicies: [
        cdk.aws_iam.ManagedPolicy.fromAwsManagedPolicyName(
          "AmazonRDSEnhancedMonitoringRole"
        ),
      ],
    });

    // DB Cluster
    const clusterIdentifier = "db-cluster";
    new cdk.aws_rds.DatabaseCluster(this, "Default", {
      engine: cdk.aws_rds.DatabaseClusterEngine.auroraPostgres({
        version: cdk.aws_rds.AuroraPostgresEngineVersion.VER_15_2,
      }),
      writer: cdk.aws_rds.ClusterInstance.provisioned("Instance1", {
        instanceType: cdk.aws_ec2.InstanceType.of(
          cdk.aws_ec2.InstanceClass.T3,
          cdk.aws_ec2.InstanceSize.MEDIUM
        ),
        allowMajorVersionUpgrade: false,
        autoMinorVersionUpgrade: true,
        enablePerformanceInsights: true,
        parameterGroup: dbParameterGroup,
        performanceInsightRetention:
          cdk.aws_rds.PerformanceInsightRetention.DEFAULT,
        publiclyAccessible: false,
        isFromLegacyInstanceProps: true,
        instanceIdentifier: "db-instance1",
      }),
      readers: [
        cdk.aws_rds.ClusterInstance.serverlessV2("Instance2", {
          autoMinorVersionUpgrade: false,
          scaleWithWriter: true,
        }),
      ],
      serverlessV2MaxCapacity: 1.0,
      serverlessV2MinCapacity: 0.5,
      backup: {
        retention: cdk.Duration.days(7),
        preferredWindow: "16:00-16:30",
      },
      cloudwatchLogsExports: ["postgresql"],
      cloudwatchLogsRetention: cdk.aws_logs.RetentionDays.ONE_YEAR,
      clusterIdentifier,
      copyTagsToSnapshot: true,
      credentials: {
        username: "postgresAdmin",
        excludeCharacters: ":@/\" '",
        secretName: `${clusterIdentifier}/postgresAdmin`,
      },
      defaultDatabaseName: "testDB",
      deletionProtection: false,
      iamAuthentication: false,
      monitoringInterval: cdk.Duration.minutes(1),
      monitoringRole,
      parameterGroup: dbClusterParameterGroup,
      preferredMaintenanceWindow: "Sat:17:00-Sat:17:30",
      storageEncrypted: true,
      instanceIdentifierBase: "db-instance",
      vpc: props.vpc,
      subnetGroup,
    });

修正後、再度diffをします。

$ npx cdk diff
Stack AuroraStack
Resources
[+] AWS::RDS::DBInstance Aurora/Default/Instance2 AuroraInstance2E52621EF
[~] AWS::RDS::DBCluster Aurora/Default Aurora2CBAB212
 └─ [+] ServerlessV2ScalingConfiguration
     └─ {"MaxCapacity":1,"MinCapacity":0.5}
[~] AWS::IAM::Role Aurora/MonitoringRole AuroraMonitoringRoleA20D2144
 └─ [~] Metadata
     └─ [~] .aws:cdk:path:
         ├─ [-] AuroraStack/Aurora/Default/MonitoringRole/Resource
         └─ [+] AuroraStack/Aurora/MonitoringRole/Resource
[~] AWS::RDS::DBInstance Aurora/Default/Instance1 AuroraInstance10510C5D1
 ├─ [-] DeleteAutomatedBackups
 │   └─ true
 └─ [-] PubliclyAccessible
     └─ false

AWS::RDS::DBInstance Aurora/Default/Instance1MonitoringRoleArnの参照が削除されないようになりました。

デプロイします。

$ npx cdk deploy

✨  Synthesis time: 8.5s

AuroraStack:  start: Building 01b3478e3d201d88f77e30fc2fc1ac69ee9b63fa6ed07127abbb16809cb2c16a:current_account-current_region
AuroraStack:  success: Built 01b3478e3d201d88f77e30fc2fc1ac69ee9b63fa6ed07127abbb16809cb2c16a:current_account-current_region
AuroraStack:  start: Publishing 01b3478e3d201d88f77e30fc2fc1ac69ee9b63fa6ed07127abbb16809cb2c16a:current_account-current_region
AuroraStack:  success: Published 01b3478e3d201d88f77e30fc2fc1ac69ee9b63fa6ed07127abbb16809cb2c16a:current_account-current_region
AuroraStack: deploying... [1/1]
AuroraStack: creating CloudFormation changeset...

 ✅  AuroraStack

✨  Deployment time: 829.63s

Stack ARN:
arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/AuroraStack/91e136c0-0271-11ee-b9d3-0ab1ec3a48b7

✨  Total time: 838.13s

無事デプロイ完了しました。

マネジメントコンソールを確認すると、Aurora Serverless v2のReaderが追加されていますね。また、WriterのDBインスタンスのリソースIDは再デプロイ前と変わらずdb-PEHAJGCIDZXBVLLCX3DNCTH4F4です。

Aurora Serveless v2が追加されたことを確認

Aurora Serverless v2のDBインスタンスを確認すると、明示的にパラメーターグループを指定しなかったためデフォルトのパラメーターグループを使用しています。また、Performance Insightsはfalseとしたためオフになっていますね。

Aurora Servelrss v2のインスタンスの確認

Aurora Serverless v2がWriterで、Aurora Provisioned InstanceがReaderのDBクラスターに変更

次にWriterとReaderを入れ替えてみます。

./lib/constructs/aurora.ts

      writer: cdk.aws_rds.ClusterInstance.serverlessV2("Instance2", {
        autoMinorVersionUpgrade: false,
        scaleWithWriter: true,
      }),
      readers: [
        cdk.aws_rds.ClusterInstance.provisioned("Instance1", {
          instanceType: cdk.aws_ec2.InstanceType.of(
            cdk.aws_ec2.InstanceClass.T3,
            cdk.aws_ec2.InstanceSize.MEDIUM
          ),
          allowMajorVersionUpgrade: false,
          autoMinorVersionUpgrade: true,
          enablePerformanceInsights: true,
          parameterGroup: dbParameterGroup,
          performanceInsightRetention:
            cdk.aws_rds.PerformanceInsightRetention.DEFAULT,
          publiclyAccessible: false,
          isFromLegacyInstanceProps: true,
          instanceIdentifier: "db-instance1",
        }),
      ],

こちらでフェイルオーバーするのでしょうか。

diffをしてみます。

$ npx cdk diff
Stack AuroraStack
Resources
[~] AWS::RDS::DBInstance Aurora/Default/Instance2 AuroraInstance2E52621EF
 └─ [~] PromotionTier
     ├─ [-] 1
     └─ [+] 0

Aurora Serveless v2のPromotion Tierが1から0に変更するだけのようです。

デプロイ後、マネジメントコンソールを確認します。

フェイルオーバー優先順位が0になったことを確認

フェイルオーバー優先順位は0になっていますが、Writer / Readerでフェイルオーバーは発生していません。

フェイルオーバー

その後十数分待ちましたが、フェイルオーバーしませんでした。手動でのフェイルオーバーが必要そうです。

アクション-フェイルオーバーをクリックします。

フェイルオーバーの実行

対象DBクラスターを確認してフェイルオーバーをクリックします。

すると2分ほどでフェイルオーバーが発生しました。ReaderのイベントA new writer was promoted. Restarting database as a reader.からフェイルオーバーが発生したことを確認できます。

フェイルオーバーしたことを確認

DBクラスターのイベントStarted cross AZ failover to DB instance: aurorastack-aurorainstance2e52621ef-e6wiuyzxbqqaからもフェイルオーバーが発生したことを確認できます。

フェイルオーバーのイベント確認

AWS CDKで簡単にAurora Serveless v2を含むDBクラスターを定義できるようになった

AWS CDK v2.82.0からAurora Serverless v2をL2 Constructで定義できるようになったアップデートを紹介しました。

Aurora Servelrss v2を作成するためにEscape hatcheを使う必要がなくなったのは嬉しいですね。

また、各DBインスタンスごとに設定を定義できるようになったのも非常に嬉しいポイントです。instancePropsinstancesは非推奨となったのでタイミングを見て新記法に移行しましょう。

使用したコードは以下リポジトリに保存しています。

この記事が誰かの助けになれば幸いです。

以上、AWS事業本部 コンサルティング部の のんピ(@non____97)でした!