3
1

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,RDS Proxy,Lambda を構築

Posted at

AWS CDK を学習していく過程を記録する。

はじめに

[備忘録]AWS CDKでAPI Gateway, Lambda を構築にて、API GatewayをトリガーにLambdaを実行することで、LambdaがRestAPIとして外部ネットワークから利用可能になることを確認した。
今回は、RDS、RDS Proxy を構築し、Lambda の中の処理を書き換えて、API Gateway から Lambda 経由で mysql に接続できることを確認したい。

RDS とは

Amazon Relational Database Service (Amazon RDS) は、AWS クラウド でリレーショナルデータベースを簡単にセットアップし、運用し、スケーリングすることのできるウェブサービスです。

つまり、サーバの設定やOSのインストールなどすることなくすぐにDB構築が可能なサービスであり、RDSでは mysql、Oracle Database、Microsoft SQL Server、PostgreSQL など一般的なDB管理システム(RDBMS)を使用することができる。

RDS Proxy とは

Amazon RDS Proxy は、Amazon Relational Database Service (RDS) 向けの高可用性フルマネージド型データベースプロキシで、アプリケーションのスケーラビリティやデータベース障害に対する回復力と安全性を高めます。

つまり、アプリケーションと RDS の間でアクセスを中継し、DBへの接続を効率的に管理してくれるサービスと言える。

実施したこと

  1. RDSの作成
  2. Lambdaの作成

1. RDSの作成

app-stack.ts
import { BastionHostLinux, InstanceClass, InstanceSize, InstanceType, InterfaceVpcEndpoint, InterfaceVpcEndpointAwsService, Port, SecurityGroup, SubnetType, Vpc } from 'aws-cdk-lib/aws-ec2';
import { Credentials, DatabaseInstance, DatabaseInstanceEngine, MysqlEngineVersion, ParameterGroup } from 'aws-cdk-lib/aws-rds';
import { Secret } from 'aws-cdk-lib/aws-secretsmanager';

// vpc
    const vpc = new Vpc(this, 'AppVpc', {
      cidr: '10.0.0.0/16',
      vpcName: 'app-vpc',
      enableDnsHostnames: true,
      enableDnsSupport: true,
      subnetConfiguration: [
        {
          cidrMask: 24,
          name: 'PublicSubnet',
          subnetType: SubnetType.PUBLIC,
        },
        {
          cidrMask: 24,
          name: 'PrivateSubnet',
          subnetType: SubnetType.PRIVATE_ISOLATED,
        },
      ],
      maxAzs: 2
    });

    const bastionGroup = new SecurityGroup(this, 'Bastion to DB', {
      vpc: vpc
    });
    const lambdaToRDSProxyGroup = new SecurityGroup(this, 'Lambda to RDSProxy', {
      vpc: vpc
    });
    const dbConnectionGroup = new SecurityGroup(this, 'RDSProxy to DB', {
      vpc: vpc
    });
    
    dbConnectionGroup.addIngressRule(
      dbConnectionGroup,
      Port.tcp(3306),
      'allow db connection'
    );
    dbConnectionGroup.addIngressRule(
      lambdaToRDSProxyGroup,
      Port.tcp(3306),
      'allow lambda connection'
    );
    dbConnectionGroup.addIngressRule(
      bastionGroup,
      Port.tcp(3306),
      'allow bastion connection'
    );

    // bastion server
    const host = new BastionHostLinux(this, 'AppBastionHost', {
      vpc: vpc,
      instanceType: InstanceType.of(
        InstanceClass.T2,
        InstanceSize.MICRO
      ),
      securityGroup: bastionGroup,
      subnetSelection: {
        subnetType: SubnetType.PUBLIC
      },
    });
    host.instance.addUserData("yum -y update", "yum install -y mysql jq");

    // credential about RDS
    const databaseCredentialsSecret = new Secret(this, 'AppDbCredentialsSecret', {
      secretName: id + '-rds-credentials',
      generateSecretString: {
        secretStringTemplate: JSON.stringify({
          username: 'syscdk',
        }),
        excludePunctuation: true,
        includeSpace: false,
        generateStringKey: 'password',
      },
    });

    // vpc endpoint to access from lambda to secret manager
    new InterfaceVpcEndpoint(this, 'SecretManagerVpcEndpoint', {
      vpc: vpc,
      service: InterfaceVpcEndpointAwsService.SECRETS_MANAGER,
    });

    // rds
    const rdsInstance = new DatabaseInstance(this, 'DBInstance', {
      engine: DatabaseInstanceEngine.mysql({
        version: MysqlEngineVersion.VER_8_0_33
      }),
      credentials: Credentials.fromSecret(databaseCredentialsSecret),
      instanceType: InstanceType.of(
        InstanceClass.T2,
        InstanceSize.MICRO
      ),
      vpc: vpc,
      vpcSubnets: {
        subnetType: SubnetType.PRIVATE_ISOLATED
      },
      securityGroups: [dbConnectionGroup],
      removalPolicy: RemovalPolicy.DESTROY,
      deletionProtection: false,
      parameterGroup: new ParameterGroup(this, 'ParameterGroup', {
        engine: DatabaseInstanceEngine.mysql({
          version: MysqlEngineVersion.VER_8_0_33
        }),
        parameters: {
          character_set_client: "utf8mb4",
          character_set_server: "utf8mb4",
        },
      }),
    });

    // rds proxy
    const proxy = rdsInstance.addProxy(id + '-proxy', {
      secrets: [databaseCredentialsSecret],
      debugLogging: true,
      vpc: vpc,
      securityGroups: [dbConnectionGroup],
    });

デプロイし、早速AWS System Managerを使用して踏み台サーバー経由でmysqlへの接続を試みた。
すると Session Manager プラグイン がないため踏み台にログインできなかったので、[備忘録] macOS で AWS Session Manager(SSM) を使ってLinuxサーバーに接続できるようになるまでにまとめた手順で Session Manager プラグイン をインストールした。

% aws ssm start-session --target {踏み台サーバーのID}
Starting session with SessionId: *****
sh-4.2$ 

インストール後は無事に踏み台サーバーにログインでき、さらにSecrets Manager にある接続情報でmysqlへの接続も成功。
動作確認のためのテーブル作成まで行った。

mysql -usyscdk -p -h ${DBのエンドポイント}
MySQL [(none)]> CREATE DATABASE app;
MySQL [(none)]> USE app;
MySQL [app]> CREATE TABLE test(id int primary key auto_increment, value int);
MySQL [app]> INSERT INTO test (value) VALUES (100);
MySQL [app]> INSERT INTO test (value) VALUES (200);
MySQL [app]> INSERT INTO test (value) VALUES (300);
MySQL [app]> INSERT INTO test (value) VALUES (400);
MySQL [app]> SELECT * FROM app.test;
+----+-------+
| id | value |
+----+-------+
|  1 |   100 |
|  2 |   200 |
|  3 |   300 |
|  4 |   400 |
+----+-------+
4 rows in set (0.00 sec)

2. Lambdaの作成

app-lambda.ts を下記のように書き換えた。

app-lambda.ts
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
import * as AWS from 'aws-sdk'
import * as mysql from 'mysql2/promise';

const cert =`
-----BEGIN CERTIFICATE-----
クライアント証明書
-----END CERTIFICATE-----
`.trim()

export const handler = async (
  event: APIGatewayProxyEvent
): Promise<APIGatewayProxyResult> => {

  const RESPONSE_HEADERS = {
    "Content-Type": "application/json",
    "Access-Control-Allow-Origin": "*",
    "Access-Control-Allow-Headers": "Content-Type,Authorization,access-token",
  };

  const secretsManager = new AWS.SecretsManager({
    region: "ap-northeast-1",
  })
  const response = await secretsManager.getSecretValue({
    SecretId: "AppStack-rds-credentials",
  }).promise()

  const {host, username, password} = JSON.parse(response.SecretString ?? '')
  console.log(`username = ${username}`);
  const connection = await mysql.createConnection({
    host: process.env.PROXY_ENDPOINT,
    database: 'app',
    user: username,
    password: password,
    ssl: {
        cert: cert
    },
  }); 

  let result = await connection.query("select * from test")

  return {
    statusCode: 200,
    headers: RESPONSE_HEADERS,
    body: JSON.stringify(result[0])
  };
};

デプロイしlambdaを実行したところ、期待通り test テーブルに保存した内容が返ってきた。
image.png

最後に

AWS CDKでRDS、RDS Proxy を構築し、Lambda から mysql に接続できることを確認できた。
参考にした文献を記す。

3
1
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
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?