内容
- 上記RDS(Aurora PostgreSQL)を使用する構成イメージをCDKで実装
- マルチAZ対応
- Lambda(Python)からRDSを操作想定
- EC2を使用したRDSに対するssh操作
前提知識
RDSのクラスタ、インスタンスとマルチAZ対応
RDSの構成はAurora、非Auroraで異なるため注意が必要
こちらに記載されているように、クラスタはAuroraにのみ存在し、インスタンスの構成、役割は以下のように違いがある
マルチ AZ DB クラスターの概要にも記載されているように、
Writerインスタンスで障害が発生した場合は、Readerインスタンスにフェイルオーバーする
また、AuroraのインスタンスはプロビジョンドDBインスタンスと、サーバーレス DBインスタンスが存在する
プロビジョンドDBインスタンスは、インスタンスタイプを選択する
サーバーレス DBインスタンスは、オンデマンドでキャパシティがスケールする
PostgreSQLのデータベース、スキーマ、テーブル
こちらに記載されているように以下の構成になっている
CDK実装
VPC
maxAzsに3以上を指定する場合は、スタック上のアカウントとリージョンを指定する必要がある
const vpc = new ec2.Vpc(this, `${stackName}-Vpc`, {
ipAddresses: ec2.IpAddresses.cidr('10.0.0.0/16'),
maxAzs: 2,
subnetConfiguration: [
{ 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,
cidrMask: 24,
},
],
});
上記実装で以下のリソースが生成される
- VPC: 10.0.0.0/16
- InternetGateway
- PublicSubnet1: 10.0.0.0/24
- EIP
- RouteTable
0.0.0.0/0 → InternetGateway
10.0.0.0/16 → local
- NatGateway
- PublicSubnet2: 10.0.1.0/24
- EIP
- RouteTable
0.0.0.0/0 → InternetGateway
10.0.0.0/16 → local
- NatGateway
- PrivateSubnet1: 10.0.2.0/24
- RouteTable
0.0.0.0/0 → NatGateway
10.0.0.0/16 → local
- PrivateSubnet2: 10.0.3.0/24
- RouteTable
0.0.0.0/0 → NatGateway
10.0.0.0/16 → local
- IsolatedSubnet1: 10.0.4.0/24
- RouteTable
10.0.0.0/16 → local
- IsolatedSubnet2: 10.0.5.0/24
- RouteTable
10.0.0.0/16 → local
Security Group
RDSにアタッチするSecurity Group
RDS ProxyにアタッチするSecurity Group
RDSのSecrets rotateにアタッチするSecurity Group
RDSのクライアントにアタッチするSecurity Group
を作成する
/**
* Security Group for RDS client
*/
const rdsClientSg = new ec2.SecurityGroup(this, `${stackName}-RdsClientSg`, {
vpc,
securityGroupName: `${stackName}-RdsClientSg`,
allowAllOutbound: true,
});
/**
* Security Group for RDS Secrets rotate
*/
const rdsRotateSecretsSg = new ec2.SecurityGroup(this, `${stackName}-RdsRotateSecretsSg`, {
vpc,
securityGroupName: `${stackName}-RdsRotateSecretsSg`,
allowAllOutbound: true,
});
/**
* Security Group for RDS Proxy
*/
const rdsProxySg = new ec2.SecurityGroup(this, `${stackName}-RdsProxySg`, {
vpc,
securityGroupName: `${stackName}-RdsProxySg`,
allowAllOutbound: true,
});
rdsProxySg.addIngressRule(ec2.Peer.securityGroupId(rdsClientSg.securityGroupId), ec2.Port.tcp(5432));
/**
* Security Group for RDS
*/
const rdsSg = new ec2.SecurityGroup(this, `${stackName}-RdsSg`, {
vpc,
securityGroupName: `${stackName}-RdsSg`,
allowAllOutbound: true,
});
rdsSg.addIngressRule(ec2.Peer.securityGroupId(rdsClientSg.securityGroupId), ec2.Port.tcp(5432));
rdsSg.addIngressRule(ec2.Peer.securityGroupId(rdsRotateSecretsSg.securityGroupId), ec2.Port.tcp(5432));
rdsSg.addIngressRule(ec2.Peer.securityGroupId(rdsProxySg.securityGroupId), ec2.Port.tcp(5432));
RDS
Postgres Versionと、EC2インスタンスタイプを文字列で指定できるようにしている
Parameter Groupに名前付けがCDK未対応のため、addPropertyOverride
でCFNのプロパティを上書きしている、その際bindToInstance
で即時リソースを作成している
RDSのインスタンスはプロビジョンドDBインスタンスを使用
const auroraPostgresVersion = '15.3';
const ec2InstanceType = 'r5.large';
const vpc = ec2.Vpc.fromLookup(this, `${stackName}-RdsVpc`, { vpcId });
const privateSubnets = vpc.selectSubnets({
subnetType: SubnetType.PRIVATE_WITH_EGRESS,
});
const isolatedSubnets = vpc.selectSubnets({
subnetType: SubnetType.PRIVATE_ISOLATED,
});
/**
* RDS Admin User Secret
*/
const secretManagerName = '/test/postgres/admin';
const EXCLUDE_CHARACTERS = ':@/" \'';
const rdsAdminSecret = new sm.Secret(this, `${stackName}-RdsAdminSecret`, {
secretName: secretManagerName,
generateSecretString: {
excludeCharacters: EXCLUDE_CHARACTERS,
generateStringKey: 'password',
passwordLength: 32,
requireEachIncludedType: true,
secretStringTemplate: '{"username": "postgresAdmin"}',
},
});
/**
* RDS Subnet Group
*/
const subnetGroupName = `${stackName}-subnet-group`.toLowerCase();
const subnetGroup = new rds.SubnetGroup(this, subnetGroupName, {
description: subnetGroupName,
vpc,
subnetGroupName: subnetGroupName,
vpcSubnets: isolatedSubnets,
});
/**
* RDS Parameter Group
*/
const auroraPostgresMajorVersion = auroraPostgresVersion.split('.')[0];
const parameterGroupName = `${stackName}-parameter-group`.toLowerCase();
const parameterGroup = new rds.ParameterGroup(this, parameterGroupName, {
engine: rds.DatabaseClusterEngine.auroraPostgres({
version: rds.AuroraPostgresEngineVersion.of(auroraPostgresVersion, auroraPostgresMajorVersion),
}),
description: `${parameterGroupName}: aurora-postgresql`,
});
parameterGroup.bindToInstance({});
const cfnParameterGroup = parameterGroup.node.defaultChild as rds.CfnDBParameterGroup;
cfnParameterGroup.addPropertyOverride('DBParameterGroupName', parameterGroupName);
/**
* RDS Cluster
*/
const [instanceClass, instanceSize] = ec2InstanceType.split('.');
const rdsClusterName = `${stackName}-rds-cluster`.toLowerCase();
const readerInstanceName = `${stackName}-rds-reader-instance`.toLowerCase();
const writerInstanceName = `${stackName}-rds-writer-instance`.toLowerCase();
const rdsCluster = new rds.DatabaseCluster(this, rdsClusterName, {
engine: rds.DatabaseClusterEngine.auroraPostgres({
version: rds.AuroraPostgresEngineVersion.of(auroraPostgresVersion, auroraPostgresMajorVersion),
}),
credentials: rds.Credentials.fromSecret(rdsAdminSecret),
clusterIdentifier: rdsClusterName,
deletionProtection: true,
iamAuthentication: true,
readers: [
rds.ClusterInstance.provisioned(readerInstanceName, {
instanceIdentifier: readerInstanceName,
instanceType: ec2.InstanceType.of(instanceClass as ec2.InstanceClass, instanceSize as ec2.InstanceSize),
parameterGroup,
}),
],
securityGroups: [rdsSg],
storageEncrypted: true,
subnetGroup,
vpc,
writer: rds.ClusterInstance.provisioned(writerInstanceName, {
instanceIdentifier: writerInstanceName,
instanceType: ec2.InstanceType.of(instanceClass as ec2.InstanceClass, instanceSize as ec2.InstanceSize),
parameterGroup,
}),
});
/**
* RDS Proxy
*/
const rdsProxyName = `${stackName}-rds-proxy`.toLowerCase();
const rdsProxy = new rds.DatabaseProxy(this, rdsProxyName, {
proxyTarget: rds.ProxyTarget.fromCluster(rdsCluster),
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
secrets: [rdsCluster.secret!],
vpc,
dbProxyName: rdsProxyName,
debugLogging: true,
requireTLS: true,
securityGroups: [rdsProxySg],
vpcSubnets: privateSubnets,
});
/**
* RDS Secret rotation
*/
new sm.SecretRotation(this, `${stackName}-DbAdminSecretRotation`, {
application: sm.SecretRotationApplication.POSTGRES_ROTATION_SINGLE_USER,
secret: rdsAdminSecret,
target: rdsCluster,
vpc,
automaticallyAfter: Duration.days(3),
excludeCharacters: EXCLUDE_CHARACTERS,
securityGroup: rdsRotateSecretsSg,
vpcSubnets: privateSubnets,
});
EC2を使用したRDSに対するssh操作
EC2 作成
適当なEC2インスタンス(Amazon Linux使用)をPrivate Subnetに1つと、踏み台としてPublic Subnetに1つ作成する
Security Group 設定
RDSにアタッチしたSG:
インバウンドルールにTCPポート5432でPrivate SubnetのEC2にアタッチしたSGを許可する
Private SubnetのEC2にアタッチしたSG:
インバウンドルールにTCPポート22でPublic SubnetのEC2にアタッチしたSGを許可する
Public SubnetのEC2にアタッチしたSG:
インバウンドルールにTCPポート22でマイIPを許可する
EC2 接続
マネジメントコンソールでEC2インスタンスを選択し、接続ボタンからSSHクライアントの接続方法に従って、
Public SubnetのEC2に接続し、Public SubnetのEC2からPrivate SubnetのEC2に接続する
クライアント用モジュールインストール
以下コマンドでrootユーザに切替
sudo su -
こちらを参考に以下コマンドでモジュールを検索、インストール
yum search postgresql
yum install postgresql15.x86_64
クライアントモジュールでRDSに接続する
psql -h xxxxx.yyyyy.ap-northeast-1.rds.amazonaws.com -U hogeuser -d hogedb
基本的なコマンドは以下を参照
PostgreSQLの基本的なコマンド