はじめに
Auroraには、マルチリージョンレプリケーションを構成するGlobal Databaseの機能があります。
Amazon Aurora Global Database
目的としては、各リージョンにリードレプリカを置いて、リージョン毎のレイテンシーを低減させるためですが、DR(Disaster Recovery)観点でも活用される機能かと思われます。
この機能自体をCDKで実装する場合、いくつかポイントがあるので、そのポイントをご紹介させて頂きます。
本記事では、Stack間のパラメータ引き渡しがない、1APP2Stack構成でのご紹介となります。
※CDKを用いて実装する場合、Stackコードはリージョンに紐つくため、1Stackコードでは実装することは出来ません。ソースとなるプライマリDBクラスタ(Aurora Writerインスタンス)をDeployするリージョンに紐つくStack、セカンダリDBクラスタ(Aurora Readerインスタンス)をDeployするリージョンに紐つくStackの2つに分割する必要があります。
※ 本ブログに記載した内容は個人の見解であり、所属する会社、組織とは全く関係ありません。
1APP 2Stack構成でのAurora Global Databaseの実装
本サンプルでは、以下シナリオ構成でAurora Serverless V2のGlobal Databaseの実装します。
シナリオ
ソースとなるプライマリDBクラスタ(Aurora Writerインスタンス)を東京Regionに、セカンダリDBクラスタ(Aurora Readerインスタンス)を大阪RegionにDeployする。
このサンプルは以下6つのセクションに分割されます。
- APPコード
- パラメータファイル
- ソースとなるプライマリDBクラスタ(Aurora Writerインスタンス)用Stackコード
- ソースとなるプライマリDBクラスタ(Aurora Writerインスタンス)用Constructsコード
- セカンダリDBクラスタ(Aurora Readerインスタンス)用Stackコード
- セカンダリDBクラスタ(Aurora Readerインスタンス)用Constructsコード
ポイントとしては、プライマリDBクラスタ側でも、セカンダリDBクラスタ側でも同じ"globalClusterIdentifier"を指定する必要があります。
よって、簡略化、ミスの抑制を意識し、このパラメータを外に出し同じパラメータを参照させるようにしています。
※利用しているパラメータの外だし手法に関しては、以下のリンクの記事をご参照ください。
【AWS CDK】「CDKにおけるパラメータの外だし方法 ~自前TypeScript実装編~」
詳細は、それぞれのセクションに分けてご紹介させて頂きます。
APPコード
globalAuroraがプライマリDBクラスタを東京RegionにDeployします。
replicaAuroraがセカンダリDBクラスタを大阪RegionにDeployします。
// プライマリDBクラスタ
const globalAurora = new SampleGlobalAuroraStack(app, 'globalAurpra', {
env: {
region: "ap-northeast-1",
account: "xxxx"
},
tsEnv: envName,
});
// セカンダリDBクラスタ
const replicaAurora = new SampleReplicaAuroraStack(app, 'replicaAurora', {
env: {
region: "ap-northeast-3",
account: "xxxx"
},
tsEnv: envName,
});
APPコードを構成するポイントはStack Constructsに引き渡すprops(引数のセット)です。
env
env propsにてDeployする先のRegionを指定することが出来ます。
そのため、globalAuroraではap-northeast-1(東京)を、replicaAuroraではap-northeast-3(大阪)を指定しています。
パラメータファイル
パラメータは以下のように組んでいます。
nwTokyoの部分はglobalAurora専用、nwOsakaの部分はreplicaAurora専用、globaldbの部分は両Stack共用といった構成にしています。
この共用箇所設けることにより、管理簡略化、ミスの抑制を狙っています。
export const nwTokyo = {
cidr: "10.0.0.0/24",
};
export const nwOsaka = {
cidr: "10.0.1.0/24",
};
export const globaldb = {
globalClusterId: "global-test",
};
ソースとなるプライマリDBクラスタ(Aurora Writerインスタンス)Stackコード
ソースとなる プライマリDBクラスタとAurora Writerインスタンスを構成するStackコードです。
特徴的な箇所は有りませんが、パラメータ外だしに関連する対応を実施しています。
constructor(scope: Construct, id: string, props: SampleGlobalAuroraStackProps) {
super(scope, id, props);
const config = require("../config/" + props.tsEnv);
const globalAurora = new GlobalAurora(this, "globalaurora", {
cidr: config.nwTokyo.cidr,
globalClusterId: config.globaldb.globalClusterId
})
}
ソースとなるプライマリDBクラスタ(Aurora Writerインスタンス)用Constructsコード
ソースとなる プライマリDBクラスタとAurora Writer インスタンスを構成するConstructsコードです。
//Aurora Cluster
const myMainAuroraCluster = new rds.DatabaseCluster(this, "MainAuroraCluster", {
engine: rds.DatabaseClusterEngine.auroraPostgres({ version: rds.AuroraPostgresEngineVersion.VER_14_4 }),
clusterIdentifier: `mainauroracluster`,
writer: rds.ClusterInstance.serverlessV2("writer", {
autoMinorVersionUpgrade: false,
enablePerformanceInsights: true,
instanceIdentifier: `MainAuroraWriter`,
performanceInsightRetention: 93,
scaleWithWriter: true,
}),
serverlessV2MaxCapacity: 2,
serverlessV2MinCapacity: 1,
backup:{
retention: cdk.Duration.days(30),
preferredWindow: "18:00-19:00",
},
cloudwatchLogsExports: ["postgresql"],
cloudwatchLogsRetention: logs.RetentionDays.THREE_MONTHS,
deletionProtection: false,
preferredMaintenanceWindow: "Sat:17:00-sat:18:00",
subnetGroup: myMainAuroraSubnetGp,
storageEncrypted: false,
vpc: myVpc,
});
// Aurora ClusterをGlobal Database化
new rds.CfnGlobalCluster(scope, 'MainGlobalDatabase', {
globalClusterIdentifier: props.globalClusterId,
sourceDbClusterIdentifier: myMainAuroraCluster.clusterIdentifier
})
注意点
本構成では、ストレージ暗号化せずに構成しております。
暗号化する場合は、以下propsを指定してください。
- storageEncrypted
- storageEncryptionKey
また、Global Databaseを利用する場合は、KMSマルチリージョンキーを用いる必要があります。
CDKを用いたマルチリージョンキーの実装に関しては以下をご確認ください。
KMSマルチリージョンキーの実装
ポイントは作成したAurora ClusterをGlobal Databaseにしているところです。
new rds.CfnGlobalCluster(scope, 'MainGlobalDatabase', {
L1 CfnGlobalCluster Constructsを用いてGlobal Database化しています。
globalClusterIdentifier: props.globalClusterId,
グローバルデータベースの識別子です。
パラメータファイルからglobalClusterIdを呼び込みglobalClusterIdentifierに入れ込んでいます。
この値がGlobal Databaseを組むときのKeyとなります。
sourceDbClusterIdentifier: myMainAuroraCluster.clusterIdentifier
作成したAurora Clusterを指定し、このGlobal Databaseのソースとしています。
セカンダリDBクラスタ(Aurora Readerインスタンス)用Stackコード
レプリケーション先のセカンダリDBクラスタとAurora Readerを構成するStackコードです。
特徴的な箇所は有りませんが、パラメータ外だしに関連する対応を実施しています。
constructor(scope: Construct, id: string, props: SampleReplicaAuroraStackProps) {
super(scope, id, props);
const config = require("../config/" + props.tsEnv);
new ReplicaAurora(this, "ReplicaAsurora", {
cidr: config.nwOsaka.cidr,
globalClusterId: config.globaldb.globalClusterId
}
)
}
セカンダリDBクラスタ(Aurora Readerインスタンス)用Constructsコード
レプリケーション先のセカンダリDBクラスタとAurora Reader を構成するConstructsコードです。
//Aurora Cluster
const myMainAuroraCluster = new rds.DatabaseCluster(this, "MainAuroraCluster", {
engine: rds.DatabaseClusterEngine.auroraPostgres({ version: rds.AuroraPostgresEngineVersion.VER_14_4 }),
clusterIdentifier: `mainauroracluster`,
writer: rds.ClusterInstance.serverlessV2("writer", {
autoMinorVersionUpgrade: false,
enablePerformanceInsights: true,
instanceIdentifier: `MainAuroraWriter`,
performanceInsightRetention: 93,
scaleWithWriter: true,
}),
serverlessV2MaxCapacity: 2,
serverlessV2MinCapacity: 1,
backup:{
retention: cdk.Duration.days(30),
preferredWindow: "18:00-19:00",
},
cloudwatchLogsExports: ["postgresql"],
cloudwatchLogsRetention: logs.RetentionDays.THREE_MONTHS,
deletionProtection: false,
preferredMaintenanceWindow: "Sat:17:00-sat:18:00",
subnetGroup: myMainAuroraSubnetGp,
storageEncrypted: false,
vpc: myVpc,
});
//エスケープハッチ
const cfnReplicaCluster = myMainAuroraCluster.node.defaultChild as rds.CfnDBCluster
cfnReplicaCluster.masterUsername = undefined;
cfnReplicaCluster.masterUserPassword = undefined;
cfnReplicaCluster.globalClusterIdentifier = props.globalClusterId
注意点
Writerインスタンスから同期されるため、「defaultDatabaseName」は指定できません。
ポイントはAurora Serverless V2のインスタンスを指定しているところと、エスケープハッチをしているところの2箇所になります。
writer: rds.ClusterInstance.serverlessV2("writer", {
Readerインスタンスなのになぜwriterって感じですよね?
L2 DatabaseCluster Constructsの仕様により、Wtiterインスタンスを必ず配置する必要があります。Readerだけで書いてしまうとエラーが発生します。
しかし、実際GlobalDatabaseでDeployするとReaderインスタンスとしてdeployされるので、問題はありません。
仕様上仕方ありませんが、紛らわしいですね。
エスケープハッチ
L2 DatabaseCluster Constructsでは、GlobalDatabaseに関わる設定をすることが出来ません。
そのため、エスケープハッチで設定を当て込んでいます。
CDK Constructs
エスケープハッチの詳しい検討方法等は以下の記事をご参照ください。
エスケープハッチサンプル
まずは、「tree.json」を確認します。
"Resource": {
"id": "Resource",
"path": "replicaAurora/ReplicaAsurora/MainAuroraCluster/Resource",
"attributes": {
"aws:cdk:cloudformation:type": "AWS::RDS::DBCluster",
replicaAuroraのMainAuroraClusterのPathがResourceになっており、CloudFormation:typeもRDS::DBClusterになっています。
よって、myMainAuroraClusterのdefaultchildはCfnDBClusterにキャスト出来ることがわかります。
const cfnReplicaCluster = myMainAuroraCluster.node.defaultChild as rds.CfnDBCluster
エスケープハッチし、CfnDBClusterにキャストし、cfnReplicaCluster に入れています。
cfnReplicaCluster.masterUsername = undefined;
cfnReplicaCluster.masterUserPassword = undefined;
Read Replicaインスタンスなので、認証情報はWriterと同期します。そのため、undefinedを指定します。
cfnReplicaCluster.globalClusterIdentifier = props.globalClusterId
グローバルデータベースの識別子です。
パラメータファイルからglobalClusterIdを呼び込みglobalClusterIdentifierを指定します。
Writerインスタンス側のDBクラスタと同じパラメータを指定しているので、Global DatabaseのReaderとして構成されます。
Deploy結果
RDSの画面では以下のようになり、無事Global Databaseとして構成されていることがわかります。
まとめ
今回は、CDKを用いたAurora Global Databaseの構成方法をご紹介させて頂きました。
特段難しい箇所があるということではないんですが、ちょこちょこ素直に使えないポイントがあったり、パラメータ外だしに向いていたり、特徴があります。
このようにDeployするリソースにKeyとして同じパラメータを指定しなければいけないケースなんかには、パラメータ外だし構成は向いていますね。
Aurora Global Databaseのご利用をご検討の際は、ご紹介させて頂いた内容を参考にして頂けると幸いです。
※ 本ブログに記載した内容は個人の見解であり、所属する会社、組織とは全く関係ありません。