AWS Lambda + Quarkusで、AuroraにアクセスするAPIを作成する

# はじめに
当記事では、別記事「[AWS Lambda + QuarkusでのREST API作成](https://qiita.com/hisato_imanishi/items/487f6914ab9a1c9fc692)」で作成したAPIに対して、Amazon Aurora（PostgreSQL）にアクセスする機能を追加する手順を示します。

手順にはAuroraのプロビジョニングを含みます。また、LambdaからAuroraへのアクセスは、RDS Proxyを間に置く構成とします。

# 前提環境
当記事では以下を前提とします（ツールのバージョンは一致しなくても実施は可能）。

- 別記事「[AWS Lambda + QuarkusでのREST API作成](https://qiita.com/hisato_imanishi/items/487f6914ab9a1c9fc692)」記載の内容の実施が完了していること
- Quarkus CLI 3.2.3 Final
- Maven 3.8.7
- AWS CLI 2.13.10


# 実施手順
Aurora（PostgreSQL）を作成するところから始めていきます。

# Auroraの作成
AWSコンソールでRDSのページにアクセスし、「データベースの作成」ボタンをクリックします。
![2023-08-23 at 00.30.22@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/4fc69ef3-6a87-fc39-be7b-f973798bd341.png)

データベースの作成画面で以下の通り設定していきます。「標準作成」を選び、「Aurora（PostgreSQL Compatible）」を選択します。
![2023-08-23 at 00.30.34@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/b33b4f9d-4578-b736-b6fa-32a14209a9a5.png)

AuroraのインスタンスとしてServerless v2を選択するため、「Serverless v2をサポートするバージョンを表示」にチェックを入れ、「利用可能なバージョン」でバージョンを選択します（ここでは14.6）。
「テンプレート」では「開発/テスト」を選択します。
![2023-08-23 at 00.30.47@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/e9995485-8fd5-2c04-a87b-b69d9240fc94.png)

「DBクラスター識別子」で任意の名前を入力します。また、マスターパスワードは適当なパスワードを入力します。
![2023-08-23 at 00.30.55@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/44ad6500-fa2c-1e3e-dece-756aa6d11018.png)

「Aurora Standard」を選択します。
![2023-08-23 at 00.31.05@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/658827b7-3c68-656e-ef8c-db74b46b84f5.png)

「DBインスタンスクラス」で「Serverless v2」を選択し、「最小ACU」「最大ACU」を適当に設定します。
![2023-08-23 at 00.31.17@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/3c928c5c-d48e-9327-c577-406a0ecd35d6.png)

「マルチAZ配置」では、今回可用性は不要のため「Auroraレプリカを作成しない」を選択します。
![2023-08-23 at 00.31.30@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/5610980b-6c73-e002-a70a-d06d8cce133e.png)

「Virtual Private Cloud（VPC）」では「新しいVPCの作成」、「DBサブネットグループ」は「新しいDBサブネットグループの作成」を選択します。
![2023-08-23 at 00.31.39@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/f9350dbb-d2c9-caf3-6c0d-c100a52a0386.png)

ローカルPCからアクセスできるように、「パブリックアクセス」は「あり」を選択します（ただし、セキュリティグループにより、DB作成時の自PCのIPアドレスからのアクセスのみ受け付けるように設定される）。
「VPCセキュリティグループ（ファイアウォール）」は「新規作成」を選び、「新しいVPCセキュリティグループ名」は適当に入力します。
「RDS Proxyを作成」のチェックは外しておきます（後ほど作成する）。
![2023-08-23 at 00.31.48@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/5fb5f405-c9bc-942a-b417-c64218d331c8.png)

他の設定はデフォルトのままで、「データベースの作成」ボタンをクリックします。
![2023-08-23 at 00.32.01@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/a73f2c28-fff1-d5ea-cf8c-5c0410b6a016.png)

作成したデータベースの「ステータス」が「利用可能」になるまで待ちます。
![2023-08-23 at 00.32.11@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/ec304868-a853-b72d-21b8-a8999a8df63a.png)

作成したデータベースの「ライターインスタンス」を選択し、そのVPCを確認・メモします。
![2023-08-23 at 00.32.20@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/26653f6d-be97-2c0f-f32d-2a63e29648d5.png)



## Lambda関数のVPC設定
別記事「[AWS Lambda + QuarkusでのREST API作成](https://qiita.com/hisato_imanishi/items/487f6914ab9a1c9fc692)」で作成したLambda関数をVPC Lambdaにします。

Lambda関数をVPCに接続する前に、Lambda関数の実行ロールに追加のポリシーを設定します（[AWSのドキュメント](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/configuration-vpc.html)）。AWSコンソールでIAMのページにアクセスし、Lambda関数の実行ロール（別記事で作成した`lambda-ex`ロール）を選択します。「許可ポリシー」で`AWSLambdaVPCAccessExecutionRole`ポリシーを付与します。
![2023-08-23 at 00.34.01@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/391d47b9-9e42-078e-9444-496866129dc6.png)

次はLambda関数側の設定です。AWSコンソールで対象のLambda関数（`QuarkusLambdaExample`）を開き、「設定」→「VPC」を選択し、「編集」ボタンをクリックします。
![2023-08-23 at 00.32.36@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/7f922f01-a8dd-71b0-28a2-0865888fb115.png)

「VPC」で、AuroraのVPCを選択します。「サブネット」は選択可能なVPCサブネットを全て選択します。「セキュリティグループ」は、デフォルトのセキュリティグループを選択します。
![2023-08-23 at 00.32.45@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/386ee647-b2b1-c787-7f63-be51d3e80e27.png)

Lambda関数がAuroraのVPCに接続されたことが確認できます。
![2023-08-23 at 00.32.58@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/00c7bd59-a8a0-496a-fd85-f78c14995c8e.png)

## Lambda - RDS Proxy - Auroraの接続設定 & RDS Proxyの作成
AWSコンソールで、再度Auroraのインスタンスのページにアクセスし、「接続されたコンピューティングリソース」セクションの「Set up Lambda connection」ボタンをクリックします。
![2023-08-23 at 00.33.08@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/ddc5d723-8149-20f3-c29f-bf1a35bee54c.png)

「Choose existing function」を選択し、`QuarkusLambdaExample`を選択します。
![2023-08-23 at 00.33.15@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/171722f8-89fc-4082-6272-f768e50f6e51.png)

ここでRDS Proxyを作成します。「Connect using RDS Proxy」にチェックを入れ、「Create new proxy」を選択します。「Username」「Password」では、Auroraのマスタユーザ、マスタパスワードを入力します。
![2023-08-23 at 00.33.24@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/0947b2ac-9219-c981-1e33-92897f1f23d5.png)

「Connection summary」を展開すると、Lambda関数 - RDS Proxy - Aurora(RDS)の関係性が図示されます。
画面最下部の「Set up」ボタンをクリックします。
![2023-08-23 at 00.33.31@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/d093bb73-4140-925f-844d-7045ef8b7c51.png)

RDS Proxyの「ステータス」が「利用可能」になるまで待ちます（20分程度かかります）。
![2023-08-23 at 00.33.41@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/7b615d7e-bae0-1db9-c4b5-f17b07604bad.png)

さらに、以下のコマンドを実行し、出力されたJSONの`Targets[0].TargetHealth.State`が`AVAILABLE`であることを確認します。

```bash
aws rds describe-db-proxy-targets --db-proxy-name <プロキシ識別子>（例：proxy-1692683664021-database-1）
```

作成されたRDS Proxyを表示し、エンドポイントを確認・メモします。
![2023-08-23 at 00.33.51@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/ea07a25d-c4b4-689a-abbb-75ec2e55cc62.png)


## ソースコードの修正
次に、QuarkusのJavaコードを修正します。DB（PostgreSQL）にアクセスし、`current_timestamp`で現在日時を取得し、レスポンスに含めて返すようにします。

Lambda関数のプロジェクトで以下のコマンドを実行し、Quarkusのエクステンションを追加します。

```bash
# DBコネクションプール
quarkus extension add 'agroal'

# PostgreSQLのJDBCドライバ
quarkus extension add 'jdbc-postgresql'

# Hibernate ORM with Panache（不要かもしれない）
quarkus extension add 'hibernate-orm-panache'
```

`application.properties`を以下のように編集します。ここでは、DBとしてPostgreSQLを使用すること、および、接続ユーザ名、コネクションプール数を指定しています。JDBC URL、パスワードは後ほどLambdaの環境変数として設定するためブランクとしています。

```properties:application.properties
quarkus.lambda.handler=test

quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=postgres
quarkus.datasource.password=

quarkus.datasource.jdbc.url=
quarkus.datasource.jdbc.max-size=2
```

Mavenプロジェクト内の`ProcessingService.java`を以下のように編集します。データソースからコネクションを取得し、`select current_timestamp`を実行してAuroraから現在日時を取得し、レスポンスに追加して返しています。

```java:ProcessingService.java
package org.example;

import io.agroal.api.AgroalDataSource;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

@ApplicationScoped
public class ProcessingService {

    public static final String CAN_ONLY_GREET_NICKNAMES = "Can only greet nicknames";

    @Inject
    AgroalDataSource defaultDataSource;

    public OutputObject process(InputObject input) {
        if (input.getName().equals("Stuart")) {
            throw new IllegalArgumentException(CAN_ONLY_GREET_NICKNAMES);
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        String result = input.getGreeting() + " " + input.getName() + ", date: " + sdf.format(new Date());

        try (Connection con = defaultDataSource.getConnection()) {
            PreparedStatement ps = con.prepareStatement("select current_timestamp");
            ResultSet rs = ps.executeQuery();
            rs.next();
            System.out.println("Current timestamp: " + rs.getTimestamp(1));
            result += ", Aurora PostgreSQL current timestamp: " + rs.getTimestamp(1);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }

        OutputObject out = new OutputObject();
        out.setResult(result);

        return out;
    }
}
```

ソースコードの修正が終わったのでビルドします。以下のコマンドを実行します。なお、JUnitテストケースが正常終了しなくなっているので、ここでは一旦テストをスキップするようにしています。

```bash
quarkus build --clean --no-tests
```

ビルドした結果をLambda環境にデプロイします。

```bash
sh target/manage.sh update
```


## Lambda関数の環境変数の設定
AuroraのJDBC URL、パスワードは環境変数で設定します。Lambda関数（`QuarkusLambdaExample`）のページで、「設定」→「環境変数」を選択し、「編集」ボタンをクリックします。
![2023-08-23 at 00.34.09@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/d361030f-0167-8495-6384-7bb68b81884a.png)

`QUARKUS_DATASOURCE_PASSWORD` にDBのパスワード、`QUARKUS_DATASOURCE_JDBC_URL`にJDBC URLを設定します。JDBC URLは以下の通り組み立てます。

```
jdbc:postgresql://<先ほど確認したRDS Proxyのエンドポイント>:5432/postgres
```
![2023-08-23 at 00.34.19@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/37230b30-899f-842d-95e9-9835b27bde36.png)


## アクセス確認
最後にAPIにアクセスして稼働を確認します。`curl`コマンドを以下のように実行します（APIのエンドポイントは置き換える）。Auroraから取得した現在日時が含まれるようになっています。

```bash
❯ curl -X POST -H "Content-Type: application/json" -d '{"name" : "Bill", "greeting" : "hello"}' https://xxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/example
{"result":"hello Bill, date: 2023-08-22 06:51:42.623, Aurora PostgreSQL current timestamp: 2023-08-22 06:51:42.626195","requestId":"6257703d-a50a-4925-9401-1a89b016284f"}
```