AWS CDK v2.82.0でAurora Serverless v2が正式にL2 Constructに対応しました。
※AWSの方によるCDKのアップデートに関するツイート。いつもこれでキャッチアップできており非常に助かっています。ありがたい。AWS CDK v2.82.0 新機能✨#cdk_releases
— Kenji Kono (@konokenj) 2023年6月2日
⑥Aurora Serverless v2を利用可能に。設計時の判断を記述するArchitecture Decision Record (ADR) も公開されているため興味がある方はご覧ください https://t.co/hVba6946vj
これにともないAWS CDKでRDSのAPIに変更が入っています。 そのため単に確認するのみではなく、旧APIからの移行について検証してみました。
最初に試したこと、および結論を書いておきます。
旧APIで作成したProvisionedインスタンスを持つAuroraクラスターを新APIに移行
- 移行用のプロパティ
isFromLegacyInstanceProps
を使えば問題ない。
- 移行用のプロパティ
Escape Hatchesを使い、旧APIで無理やり作成したServerless v2インスタンスを持つAuroraクラスターを新APIに移行。
目次
1. APIの変更内容
今回対象となるAPIはrds.DatabaseClusterです。
Aurora Serverless v2への対応に伴い、インスタンスの設定内容の定義方法が変わっています。
旧API(v2.82.0より前)
以下のようにinstances
でインスタンス数を指定、instansProps
でインスタンスの設定を指定する形式となっていました。
この時instanceProps
の設定内容は全インスタンス共通であるため、インスタンスごとに異なる設定にはできません。
new rds.DatabaseCluster(scope, 'Database', { // ... 略 // --------------- Old API---------------- // インスタンスの数を指定 instances: 1, // インスタンスのプロパティを設定(全インスタンス共通) instanceProps: { vpc: props.vpcConstruct.vpc, vpcSubnets: { subnets: [props.vpcConstruct.subnetDB1a, props.vpcConstruct.subnetDB1c], }, securityGroups: [props.securityGroupConstruct.databaseSg], instanceType: ec2.InstanceType.of(InstanceClass.T3, InstanceSize.LARGE), },
新API(v2.82.0から)
上記のinstances
とinstanceProps
はdeprecated
となり、代わりにwriter
、reader
においてインスタンスごとに個別の設定を行えるようになりました。
これによりAurora Serverless v2に対応したのみではなく、インスタンスごとに異なる設定を行う柔軟なクラスタ構築が可能となりました。
以下が実装例です。Writerをdb.t3.large
、Readerをdb.t3.large
+Serverless v2
にしています。
なおACUに関してはserverlessV2MaxCapacity
とserverlessV2MinCapacity
で定義しますが、こちらはServerless v2
のインスタンス全てで共通の設定となります。
new rds.DatabaseCluster(scope, 'Database', { // ... 略 // --------------- New API---------------- // VPC, Subnet, SG設定などは共通 vpc: props.vpcConstruct.vpc, vpcSubnets: { subnets: [props.vpcConstruct.subnetDB1a, props.vpcConstruct.subnetDB1c], }, securityGroups: [props.securityGroupConstruct.databaseSg], // Writerインスタンスの設定 writer: rds.ClusterInstance.provisioned('Writer', { instanceType: ec2.InstanceType.of(InstanceClass.T3, InstanceSize.LARGE), }), // Readerインスタンスの設定 readers: [ // Readerインスタンス(Provisioned) rds.ClusterInstance.provisioned('Reader1', { instanceType: ec2.InstanceType.of(InstanceClass.T3, InstanceSize.LARGE), }), // Readerインスタンス(Serverless v2) rds.ClusterInstance.serverlessV2('Reader2', { }) ], // Serverless v2(ACU)の設定 ※Serverless V2インスタンスは共通のACUを適用 serverlessV2MaxCapacity: 2.0, serverlessV2MinCapacity: 0.5, // ... 略 })
2. 旧APIから新APIの移行について
上記のようにAPIに変更が入ったため移行方法の確認および検証をします。
①Provisionedの場合
Provisionedの場合、新APIではrds.ClusterInstance.provisionedで定義する形になりますが、プロパティにisFromLegacyInstanceProps
という項目があります。
Only used for migrating existing clusters from using instanceProps to writer and readers.
要は旧APIから移行する場合はこのフラグをtrue
にせよということのようです。
では実際に試してみます。
まずは旧APIでdb.t3.large
のWriterインスタンスを持つAuroraクラスターをデプロイします。
new rds.DatabaseCluster(scope, 'Database', { // ... 略 // --------------- Old API---------------- // インスタンスの数を指定 instances: 1, // インスタンスのプロパティを設定(全インスタンス共通) instanceProps: { vpc: props.vpcConstruct.vpc, vpcSubnets: { subnets: [props.vpcConstruct.subnetDB1a, props.vpcConstruct.subnetDB1c], }, securityGroups: [props.securityGroupConstruct.databaseSg], instanceType: ec2.InstanceType.of(InstanceClass.T3, InstanceSize.LARGE), }, // ... 略 )
以下のようにWriterのみを持つAuroraクラスターが作成されました。
次に新APIに定義を変更します。
new rds.DatabaseCluster(scope, 'Database', { // ... 略 // --------------- New API---------------- // VPC, Subnet, SG設定などは共通 vpc: props.vpcConstruct.vpc, vpcSubnets: { subnets: [props.vpcConstruct.subnetDB1a, props.vpcConstruct.subnetDB1c], }, securityGroups: [props.securityGroupConstruct.databaseSg], // Writerインスタンスの設定 writer: rds.ClusterInstance.provisioned('Writer', { instanceType: ec2.InstanceType.of(InstanceClass.T3, InstanceSize.LARGE), isFromLegacyInstanceProps: true }), // ... 略 )
上記の後、cdk diff
をとってみました。そうすると以下のようにdestroy
になってしまいました。
Resources [-] AWS::RDS::DBInstance Database/Instance1 DatabaseInstance1844F58FD destroy [+] AWS::RDS::DBInstance Database/Writer DatabaseWriter2C00925F
IDがズレているのが原因と考え、新APIでIDを旧APIで生成したものと揃えてみます。
new rds.DatabaseCluster(scope, 'Database', { // ... 略 // --------------- New API---------------- // VPC, Subnet, SG設定などは共通 vpc: props.vpcConstruct.vpc, vpcSubnets: { subnets: [props.vpcConstruct.subnetDB1a, props.vpcConstruct.subnetDB1c], }, securityGroups: [props.securityGroupConstruct.databaseSg], // Writerインスタンスの設定 writer: rds.ClusterInstance.provisioned('Instance1`', { // ★旧APIにより生成されたWriterインスタンスのIDであるInstance1に変更。 instanceType: ec2.InstanceType.of(InstanceClass.T3, InstanceSize.LARGE), isFromLegacyInstanceProps: true }), // ... 略 )
この状態でcdk diff
をとると差分なしとなりました。この状態でcdk deploy
しても特に変更が入らずに完了します。
There were no differences
ちなみにこのあと、isFromLegacyInstanceProps
をfalse
にするとどうなるかを試してみました。
new rds.DatabaseCluster(scope, 'Database', { // ... 略 // --------------- New API---------------- // VPC, Subnet, SG設定などは共通 vpc: props.vpcConstruct.vpc, vpcSubnets: { subnets: [props.vpcConstruct.subnetDB1a, props.vpcConstruct.subnetDB1c], }, securityGroups: [props.securityGroupConstruct.databaseSg], // Writerインスタンスの設定 writer: rds.ClusterInstance.provisioned('Instance1`', { instanceType: ec2.InstanceType.of(InstanceClass.T3, InstanceSize.LARGE), isFromLegacyInstanceProps: false. // ★falseに変更 }), // ... 略 )
この状態でcdk diff
をとると再度destroy
となりました。旧APIから移行した後にインスタンスの置き換えを避けるためには、isFromLegacyInstanceProps
をtrue
のままとする必要があることがわかりました。
[-] AWS::RDS::DBInstance Database/Instance1 DatabaseInstance1844F58FD destroy [+] AWS::RDS::DBInstance Database/Instance1 DatabaseInstance14A23AADF
②Escape Hatchesで作ったServerless v2の場合
以下の記事でも紹介していますが、旧APIでもEscape Hatchesを使用すれば、(若干強引ではあるものの)Serverless v2のインスタンスが作れました。
ではこの方法でServerless v2のインスタンスを作った状態で、新APIに移行したらどうなるのか試してみました。
まずは以下のようにEscape Hatchesを使用してServerless v2のWriterを持つAurora Clusterを作成します。
new rds.DatabaseCluster(scope, 'Database', { // ... 略 // --------------- Old API---------------- // インスタンスの数を指定 instances: 1, // インスタンスのプロパティを設定(全インスタンス共通) instanceProps: { vpc: props.vpcConstruct.vpc, vpcSubnets: { subnets: [props.vpcConstruct.subnetDB1a, props.vpcConstruct.subnetDB1c], }, securityGroups: [props.securityGroupConstruct.databaseSg], instanceType: new ec2.InstanceType('serverless'), //インスタンスタイプでserverlessを指定 }, // ... 略 ) // Escape Hatchesを使用して設定。 const cfnCluster = this.dbCluster.node.defaultChild as rds.CfnDBCluster; cfnCluster.addPropertyOverride('ServerlessV2ScalingConfiguration', { 'MaxCapacity': 2, 'MinCapacity': 0.5, }); cfnCluster.addPropertyDeletionOverride('EngineMode');
以下のように作成できました。
では次に新APIで同じ内容を定義します。Serverless v2の場合は新APIではrds.ClusterInstance.serverlessV2を使用します。
v2.88.0までの場合
Provisionedと異なる点として、Serverless v2において旧APIからの移行用の項目(isFromLegacyInstanceProps
)は存在しません。
そのため以下のように、旧APIで作成した際と同じ設定値にするのみになります。
new rds.DatabaseCluster(scope, 'Database', { // ... 略 // --------------- New API---------------- // VPC, Subnet, SG設定などは共通 vpc: props.vpcConstruct.vpc, vpcSubnets: { subnets: [props.vpcConstruct.subnetDB1a, props.vpcConstruct.subnetDB1c], }, securityGroups: [props.securityGroupConstruct.databaseSg], // Writerインスタンスの設定 writer: rds.ClusterInstance.serverlessV2('Instance1', { }), // Readerインスタンスは無し readers: [ ], // Serverless v2(ACU)の設定 serverlessV2MaxCapacity: 2.0, serverlessV2MinCapacity: 0.5, // ... 略 )
この状態でcdk diff
をとると、destroy
となりインスタンスの入れ替えが発生します。予想通りと言えば予想通りです。
Resources [-] AWS::RDS::DBInstance Database/Instance1 DatabaseInstance1844F58FD destroy [+] AWS::RDS::DBInstance Database/Instance1 DatabaseWriter7794273E
ではこの状態でcdk deploy
に突き進んでみます。デプロイを開始するとまず入れ替え先(新APIによるServerless v2)のインスタンスがリーダーとして作成されました。
そして入れ替え先(新APIによるServerless v2)のインスタンスの起動が終わるとライターに昇格し、入れ替え元(旧APIによるServerless v2)のインスタンスがリーダーとなり削除されました。
最終的に入れ替え先(新APIによるServerless v2)のインスタンスのみとなり入れ替えが完了しました。
インスタンスの入れ替えは発生するものの、入れ替え先を起動してから入れ替え元を削除するという動作なので、ダウンタイムは抑えられそうです。
v2.89.0以降の場合 ※2023/8/2追加
v2.89.0において、Severless v2用にも旧APIからの移行用のプロパティ(isFromLegacyInstanceProps
)が追加されました。これによりEscape Hatches+旧APIでデプロイしたServerless v2インスタンスも、Provisionedと同様の方法で新APIに移行できます。
以下がv2.89.0以降で、旧API→新APIに移行する際の実装例になります。
v2.88.0以前との違いとしてはrds.ClusterInstance.serverlessV2
においてisFromLegacyInstanceProps
が使えるようになっている点です。
new rds.DatabaseCluster(scope, 'Database', { // ... 略 // --------------- New API---------------- // VPC, Subnet, SG設定などは共通 vpc: props.vpcConstruct.vpc, vpcSubnets: { subnets: [props.vpcConstruct.subnetDB1a, props.vpcConstruct.subnetDB1c], }, securityGroups: [props.securityGroupConstruct.databaseSg], // Writerインスタンスの設定 writer: rds.ClusterInstance.serverlessV2('Instance1', { isFromLegacyInstanceProps: true // ★ここ }), // Readerインスタンスは無し readers: [ ], // Serverless v2(ACU)の設定 serverlessV2MaxCapacity: 2.0, serverlessV2MinCapacity: 0.5, // ... 略 )
ではEscape HatchesでServerless v2インスタンスをデプロイした状態で、上記に書き換えcdk diff
をとってみます。
想定通り差分なしとなりました。当然ですがcdk deploy
をしても何も起こりません。
There were no differences ✨ Number of stacks with differences: 0
ではこの状態でisFromLegacyInstanceProps
をfalse
にし、cdk diff
をとってみます。するとProvisionedの時と同様にdestroy
になりました。
Serverless v2でも同様に旧APIから移行した後にインスタンスの置き換えを避けるためには、isFromLegacyInstanceProps
をtrue
のままとする必要があることがわかりました。
Resources [-] AWS::RDS::DBInstance Database/Instance1 DatabaseInstance1844F58FD destroy [+] AWS::RDS::DBInstance Database/Instance1 DatabaseInstance14A23AADF
3. 終わりに
個人的にServerless v2に対応したことよりも、クラスター内でインスタンスごとに設定を変えられるようになったことの方が嬉しいですね。
Readが圧倒的に多い等のケースもあるので、うまく活用していきたいです。