Help us understand the problem. What is going on with this article?

AuroraServerlessを操作するAPI(GET、PUT、POST)+lambdaを作る。

作るもの

image.png

今回、DBに入れるデータは以下のものとして進める。

id name age
1 Apigateway 12
2 lambda 34
3 Dynamodb 56

contents

  • Aurora serverless、シークレットマネージャとは
  • Aurora serverlessのDBの作り方。
  • lambdaからAurora serverlessへアクセスする方法。
  • APIでGET、PUT、POSTする方法

Aurora serverlessとは?

DBのインスタンス部分とストレージ部分が分離した構造を取っており、アクセスがない時にはインスタンス部分は起動しておらず、ストレージ部分のみが存在する。アクセスを受けると、負荷に応じてインスタンスがスケールアウトしクエリを実行する。アクセスがなくなると最終的にはインスタンスはゼロになるため、DBのランニングコストを抑えることが可能である。(RDSは停止させても7日で自動で起動する。)

クラスタとは?
まだよくわかっていないがAuroraのインスタンスやストレージの全体のことかと思われる。
https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/Aurora.Overview.html

Aurora キャパシティーユニット (ACU) とは?
処理キャパシティーとメモリキャパシティーの組み合わせ。
最大と最小のACUを指定でき、それぞれDBクラスタをスケールダウン/アップできる最小/最大の値を示している。

シークレットマネージャとは?

シークレットを一元的に管理するシステム。これまでは認証情報を用いる箇所にそれらをベタ書きしていたが、シークレットマネージャを用いることでセキュリティを高めることができる。

シークレットとは?
データベース認証情報、パスワード、サードパーティーのAPIキーなどの任意のテキスト。

ローテーションとは?_
一定期間がたつと自動的にパスワードを変更する仕組み。ONにすると自動的にlambdaが生成され、対象の認証情報のパスワードを定期的にローテーションさせる。なお、この定期的な変更はユーザ側は気にすることなくアプリケーションを開発することができる。

1. Aurora serverlessクラスター・データベースの作成。

RDSコンソールへ移動し、ナビゲーションペインのデータベースを選択→作成へ。

  • データベース作成方法を選択 : 標準作成
  • エンジンのオプション
    • エンジンのタイプ : Aurora
    • エディション : PostgreSQL
  • データベースの機能 : サーバーレス
  • 設定
    • DBクラスター識別子マスターユーザー名マスターパスワード
  • キャパシティーの設定
    • 最小/最大キャパシティーユニット : 任意の値
    • スケーリングの追加設定
    • タイムアウトに達すると、容量を指定された値に強制的にスケーリングします
    • 数分間アイドル状態のままの場合コンピューティング性能を一時停止する : 5 min
  • 接続
    • 追加の接続設定
    • Data API
  • 追加設定
    • 最初のデータベース名

→ 作成する。作成されるまでしばらくかかる。

なお、Data APIにチェックを入れていないと3で接続不可となる。
→その場合でもあとからクラスターの設定を変更できる。

2. Secret Managerへの登録。

  • シークレットの種類を選択 : RDSのデータベース認証情報
  • ユーザー名 : 1で設定したマスターユーザー名
  • パスワード : 1で設定したマスターパスワード
  • 暗号化キーを選択してください : DefaultEncryptionKey (default)
  • このシークレットがアクセスするRDSデータベースを選択してください : 1で設定したDBクラスター識別子
  • シークレットの名前 : シークレット名
  • 自動ローテーションを設定する : 自動ローテーションを無効にする

→ 保存

3. RDSコンソールからクエリ実行。

コンソールからDBにアクセスして疎通確認を行う。

image.png

対象のクラスターを選択し、[アクション]→[クエリ]

データベースユーザー名は直接入力不可のため、2で作成した「Secret Manager ARNと接続する」を指定する。
Secret Managerコンソールから対象のシークレットのARNを取得しペースト。
データベース名には1で設定した「最初のデータベース名」を指定。

##### テーブル作成 #####
CREATE TABLE Staff
(id    CHAR(4)    NOT NULL,
name   TEXT       NOT NULL,
age    INTEGER    ,
PRIMARY KEY (id));
# →実行

##### データ挿入 #####
INSERT INTO Staff VALUES ('0001', 'Aurora', 12);
INSERT INTO Staff VALUES ('0002', 'Lambda', 34);
INSERT INTO Staff VALUES ('0003', 'Dynamo', 56);
# →実行

##### データ取得 #####
SELECT * FROM Staff
#  実行

4. IAMロールの作成

4-1. SecretManagerからRDSにアクセスするIAMポリシー作成

クラメソさんの「Lambda で Aurora Serverless の Data API 使えました!そう、Lambda Layer があればね」に従います。
2020年1月現在でも"arn:aws:secretsmanager:*:*:secret:rds-db-credentials/*"だけでは不可でした。
公式ドキュメント:https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/data-api.html

IAMポリシー
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "SecretsManagerDbCredentialsAccess",
            "Effect": "Allow",
            "Action": [
                "secretsmanager:GetSecretValue",
                "secretsmanager:PutResourcePolicy",
                "secretsmanager:PutSecretValue",
                "secretsmanager:DeleteSecret",
                "secretsmanager:DescribeSecret",
                "secretsmanager:TagResource"
            ],
            "Resource": [
                "arn:aws:secretsmanager:*:*:secret:rds-db-credentials/*",
                " ~~~ここにシークレットのARNを指定~~~ "
            ]
        },
        {
            "Sid": "RDSDataServiceAccess",
            "Effect": "Allow",
            "Action": [
                "dbqms:CreateFavoriteQuery",
                "dbqms:DescribeFavoriteQueries",
                "dbqms:UpdateFavoriteQuery",
                "dbqms:DeleteFavoriteQueries",
                "dbqms:GetQueryString",
                "dbqms:CreateQueryHistory",
                "dbqms:DescribeQueryHistory",
                "dbqms:UpdateQueryHistory",
                "dbqms:DeleteQueryHistory",
                "dbqms:DescribeQueryHistory",
                "rds-data:ExecuteSql",
                "rds-data:ExecuteStatement",
                "rds-data:BatchExecuteStatement",
                "rds-data:BeginTransaction",
                "rds-data:CommitTransaction",
                "rds-data:RollbackTransaction",
                "secretsmanager:CreateSecret",
                "secretsmanager:ListSecrets",
                "secretsmanager:GetRandomPassword",
                "tag:GetResources"
            ],
            "Resource": "*"
        }
    ]
}

4-2. lambdaに当てるIAMロールの作成

4-1で作成したIAMポリシーと、AmazonRDSDataFullAccessを当てたIAMロールを作成する。
そのほかCloudWatchへのログ書き込みなど必要なものは適宜追加。

5. Aurora serverlessに接続するlambda関数の作成

テーブル項目一覧取得のコード(同じくクラメソのコード)

lambda_function.py
import json
import boto3

def lambda_handler(event, context):

    rdsData = boto3.client('rds-data')

    print(boto3.__version__)

    # RDSクラスターのARN
    cluster_arn = 'arn:aws:rds:ap-northeast-1:xxxxxxxxxxxx:cluster:test'
    # シークレットのARN
    secret_arn = 'arn:aws:secretsmanager:ap-northeast-1:xxxxxxxxxxxx:secret:test'

    # ★
    response1 = rdsData.execute_statement(
                resourceArn = cluster_arn, 
                secretArn = secret_arn, 
                database = '1で設定したデータベース名', 
                sql = 'select * from table_name') # 3で作成したテーブル名

    print (response1['records'])
  • 初回のDBアクセスではインスタンス起動に時間がかかるためタイムアウトを1minに指定しておくこと。
  • IAMロールは上記で作成済みのものを使用。
  • RDSクラスターのARNはクラスターの[設定]タブから取得可能
  • 参考としたクラメソさんのページではlambda layerを使用してboto3をzipしてレイヤに設定していましたが、layerを設定せずにimport boto3でもちゃんと取得できた。

6. API Gatewayの作成

6-1. GET

クエリパラメータに指定したidのレコードを返すAPI+lambdaを作成する。

lambdaを一部変更。

    # ★
    sql_str = "select * from table_name where id = '" + event["id"] + "'" # 3で作成したテーブル名
    response1 = rdsData.execute_statement(
                resourceArn = cluster_arn, 
                secretArn = secret_arn, 
                database = '1で設定したデータベース名', 
                sql = sql_str)
    return response1["records"]

メソッドリクエスト
1. [アクション]→[メソッドの作成]からGETを選択。
2. [メソッドリクエスト]→[URLクエリ文字列パラメータ]→「クエリ文字列を追加」にパラメータ名を入れる。

ここでは名前id、必須 ✔︎

統合リクエスト
1. [統合リクエスト]→[マッピングテンプレート]→リクエスト本文のパススルーに「テンプレートが定義されていない場合(推奨)」を選択。
2. Content-Typeにはapplication/jsonを指定。
3. マッピングテンプレートに以下を指定。

{
  "id": "$input.params('id')"
}

この形式でlambdaのeventに渡される。クエリストリングは$input.params('パラメータ名')で取得可能。

最後に[アクション]→[APIのデプロイ]を選択してURIを発行する。

試しにブラウザからid=0001を付してアクセスすると次の値が帰る。

[
  [
    {
      "stringValue": "0001"
    },
    {
      "stringValue": "Aurora"
    },
    {
      "longValue": 12
    }
  ]
]

参考) https://qiita.com/Quantum/items/91ad6b6b788bf4051055

6-2. POST

リクエストボディに含めたjsonで新規にレコードを追加するAPI+lambdaを作成する。

lambdaを一部変更。

    # ★
    sql_str = "insert into table_name values ('" + event['id'] + "', '" + event['name'] + "', " + str(event['age']) + ");"

    response1 = rdsData.execute_statement(
                resourceArn = cluster_arn,
                secretArn = secret_arn,
                database = '1で設定したデータベース名',
                sql = sql_str,
                )

    return response1

同じく[アクション]から[メソッドの作成]でPOSTを選択し、[APIをデプロイ]で作成。
postmanから以下のjsonを投げるとレスポンスが得られる。

リクエスト
{
  "id": "0004",
  "name": "Apigateway",
  "age": 78
}
レスポンス
{
  "ResponseMetadata": {
    "RequestId": "fdc33f8d-2ace-4bd3-b3d6-3c0796068366",
    "HTTPStatusCode": 200,
    "HTTPHeaders": {
      "x-amzn-requestid": "fdc33f8d-2ace-4bd3-b3d6-3c0796068366",
      "content-type": "application/json",
      "content-length": "49",
      "date": "Mon, 20 Jan 2020 14:48:28 GMT"
    },
    "RetryAttempts": 0
  },
  "generatedFields": [],
  "numberOfRecordsUpdated": 1
}

参考) https://qiita.com/edg_aim/items/3989aca53d98e3cafc0f

6-3. PUT

指定したパスのidの情報を更新するAPI+lambdaを作成する。

lambdaを一部変更。

    # ★
    sql_str = "update table_name set name='" + event["name"] + "' where id = '" + str(event["id"]) + "';"

    response1 = rdsData.execute_statement(
                resourceArn = cluster_arn,
                secretArn = secret_arn,
                database = '1で設定したデータベース名',
                sql = sql_str,
                )

    return response1

まずはid毎のパスを切るため、[アクション]から[リソースmの作成]を選択。
ここで、リソース名にidとし、リソースパスに{id}とする。
これにより、{id}の部分はidの番号で置き換わったパス全てを指すことを意味する。

続いて同じく[アクション]から[メソッドの作成]でPUTを選択し、[APIをデプロイ]で作成。

メソッドリクエスト
特に変更梨。リクエストパスを覗くと勝手にリソース名のidと書かれている。

統合リクエスト
マッピングテンプレートの設定が少しややこしい。それまではGETと同じ手順。

URI: https://〜〜〜/{id}

#set($test = '"' + $input.params("id") + '"')
{
    "id": $test,
    "name": $input.json('name')
}

#set()の中身では式を記述できる?と思われる。
"id": $input.params("id")と直接連結してもよいのだが、この場合にidに0001を指定するとクオーテーションで囲われないため以下のエラーが発生。

Invalid numeric value: Leading zeroes not allowed

正しいテンプレートの下でpostmanからcontext-typeを指定してput。

URI: https://〜〜〜/0001

リクエスト
{
    "name": "Aurora"
}

200のレスポンスが得られ、テーブルが更新されていれば成功。

なお、マッピングテンプレートの記述は以下のドキュメントに書かれている。
参考) https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html#input-variable-reference

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした