1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

AWS CDK - RDS環境構築(Aurora PostgreSQL)

Last updated at Posted at 2023-10-30

内容

2023-10-30-08-29-36.png

  • 上記RDS(Aurora PostgreSQL)を使用する構成イメージをCDKで実装
  • マルチAZ対応
  • Lambda(Python)からRDSを操作想定
  • EC2を使用したRDSに対するssh操作

前提知識

RDSのクラスタ、インスタンスとマルチAZ対応

RDSの構成はAurora、非Auroraで異なるため注意が必要
こちらに記載されているように、クラスタはAuroraにのみ存在し、インスタンスの構成、役割は以下のように違いがある

2023-10-30-09-30-39.png

マルチ AZ DB クラスターの概要にも記載されているように、
Writerインスタンスで障害が発生した場合は、Readerインスタンスにフェイルオーバーする

2023-10-30-09-45-13.png

また、AuroraのインスタンスはプロビジョンドDBインスタンスと、サーバーレス DBインスタンスが存在する
プロビジョンドDBインスタンスは、インスタンスタイプを選択する
サーバーレス DBインスタンスは、オンデマンドでキャパシティがスケールする

2023-10-30-11-04-51.png

PostgreSQLのデータベース、スキーマ、テーブル

こちらに記載されているように以下の構成になっている

2023-10-30-09-41-36.png

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に接続する

2023-10-30-10-50-51.png

クライアント用モジュールインストール

以下コマンドで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の基本的なコマンド

参考

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?