作るもの
今回、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にアクセスして疎通確認を行う。
対象のクラスターを選択し、[アクション]→[クエリ]
データベースユーザー名は直接入力不可のため、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
{
"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関数の作成
テーブル項目一覧取得のコード(同じくクラメソのコード)
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"]
メソッドリクエスト
- [アクション]→[メソッドの作成]から
GET
を選択。 - [メソッドリクエスト]→[URLクエリ文字列パラメータ]→「クエリ文字列を追加」にパラメータ名を入れる。
ここでは名前id
、必須 ✔︎
統合リクエスト
- [統合リクエスト]→[マッピングテンプレート]→リクエスト本文のパススルーに「テンプレートが定義されていない場合(推奨)」を選択。
- Content-Typeには
application/json
を指定。 - マッピングテンプレートに以下を指定。
{
"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