今から始めるLambda⑦「LambdaからRDS(Aurora)に接続する 後編」
はじめに
前回の記事ではVPC
内にAurora
のDB
インスタンスを立ち上げるところまでを行いました。
今回は同一VPC
内にあるLambda
からDB
に接続をしたいと思います。
そのために使用するRDS Proxy
のセットアップなども含めて紹介していきます。
前提としてRDS
のインスタンスやVPC
の作成は済んでいるものとします。
RDS Proxyとは
Amazon RDS Proxy は、Amazon Relational Database Service (RDS) 向けの高可用性フルマネージド型データベースプロキシで、アプリケーションのスケーラビリティやデータベース障害に対する回復力と安全性を高めます。
文字通りAWS RDS
のプロキシです。
なぜ今回これが必要かというと、Lambda
からRDS
への接続を考えた際に、Lambda
はリクエスト毎に起動し、都度コネクションを貼ってしまうためです。
いわゆる同時接続数問題というやつで、RDS Proxy
が間に入って接続プールを管理してくれることで、これらが解消されます。
RDS Proxy
が登場する以前はLambda
とRDS
の組み合わせはアンチパターンに近い扱いをされていたようです(以下リンク参照)。
RDS Proxyの設定
さっそくRDS Proxy
の設定を行っていきます。
シークレットの作成
RDS Proxy
ではDB
の認証情報をシークレットで管理します。
そのためAWS
のSecrets Manager
を使用します。
前回作成したRDS
のインスタンスの認証情報(デフォではpostgres
)をシークレットに含めます。
名前はrds_proxy_secret
とします。
内容のうちKVS_RESOURCE
の値についてはAWS
のコンソールから「Key Management Service (KMS) > AWS マネージド型キー > aws/secretsmanager」の値を使います。
aws secretsmanager create-secret \
--name "rds_proxy_secret" \
--description "rds_proxy_secret" \
--region us-east-2 \
--secret-string '{"username":"postgres","password":"postgres"}'
--kms-key-id 【KVS_RESOURCE】
レスポンスとしてJSON
が返ってくれば成功です。
ロールの作成
今回使用するロールを作成します。
RDS
アクセスするため、以下のようなJSON
を用意しておきます。
名前はrds-proxy-policy.json
とします。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "rds.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
上記を用いてロールを作成します。
ロール名はrds_proxy_role
とします。
aws iam create-role --role-name rds_proxy_role \
--assume-role-policy-document file://rds-proxy-policy.json
シークレットを利用するためのポリシー追加
作成したロールにさらにポリシーを追加します。
このポリシーは先ほど使用したシークレットの値を利用するためのものです。
以下のようなsecret-reader-policy.json
を作成します。
KVS_RESOURCE
の値についてはcreate-secret
で使用したのと同じ値です。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "secretsmanager:GetSecretValue",
"Resource": [
"【SECRET_ARN】"
]
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": "kms:Decrypt",
"Resource": "arn:aws:kms:【REGION】:【USER_ID】:key/【KVS_RESOURCE】",
"Condition": {
"StringEquals": {
"kms:ViaService": "secretsmanager.【REGION】.amazonaws.com"
}
}
}
]
}
このJSON
を使ってポリシーをロールに追加します。
aws iam put-role-policy --role-name rds_proxy_role \
--policy-name secret-reader-policy --policy-document file://secret-reader-policy.json
CMKを使う
シークレットを暗号化、福号して使用するためCMK
を使います。
CMK
はAWS Key Management Service
(以下KMS
)を用いて生成します。
以下のようなrds-key.json
を用意します。
{
"Id":"rds-kms",
"Version":"2012-10-17",
"Statement":
[
{
"Sid":"Enable IAM User Permissions",
"Effect":"Allow",
"Principal":{"AWS":"arn:aws:iam::【USER_ID】:root"},
"Action":"kms:*","Resource":"*"
},
{
"Sid":"Allow access for Key Administrators",
"Effect":"Allow",
"Principal":
{
"AWS":
["arn:aws:iam::【USER_ID】:user/【USER_NAME】","arn:aws:iam::【USER_ID】:role/rds_proxy_role"]
},
"Action":
[
"kms:Create*",
"kms:Describe*",
"kms:Enable*",
"kms:List*",
"kms:Put*",
"kms:Update*",
"kms:Revoke*",
"kms:Disable*",
"kms:Get*",
"kms:Delete*",
"kms:TagResource",
"kms:UntagResource",
"kms:ScheduleKeyDeletion",
"kms:CancelKeyDeletion"
],
"Resource":"*"
},
{
"Sid":"Allow use of the key",
"Effect":"Allow",
"Principal":{"AWS":"arn:aws:iam::【USER_ID】:role/rds_proxy_role"},
"Action":["kms:Decrypt","kms:DescribeKey"],
"Resource":"*"
}
]
}
以下コマンドでキーを作成します。
aws kms create-key --description "rds-key" --policy file://rds-key.json
プロキシの作成
いよいよRDS Proxy
の作成を行います。
作成したシークレットやロールのARN
、VPC
のサブネットやセキュリティグループなどを指定しましょう。
プロキシの名前はtest-rds_proxy
とします。
aws rds create-db-proxy \
--db-proxy-name test-rds-proxy \
--engine-family POSTGRESQL \
--auth '[{"Description": "test_rds_proxy auth", "AuthScheme": "SECRETS", "SecretArn": "【SECRET_ARN】", "IAMAuth": "DISABLED"}]' \
--role-arn 【ROLE_ARN】 \
--vpc-subnet-ids '["【SUBNET_1】","【SUBNET_2】"]' \
--vpc-security-group-ids 【SECURITY_GROUP_ID】
成功するとJSON
が返ります。
プロキシとDBの紐付け
作成したプロキシとDBの紐付けを行います。
ここではDBはtest-aurora-postgres-clister
というクラスタ名であるとします。
aws rds register-db-proxy-targets \
--db-proxy-name test-rds-proxy \
--db-cluster-identifiers test-aurora-postgresql-cluster
こちらもJSON
が返れば成功です。
状態のチェック
RDS Proxy
とDBの紐付けには時間がかかるため、以下コマンドで状態をチェックします。
aws rds describe-db-proxy-targets --db-proxy-name test-rds-proxy
レスポンスの中のTargetHealth
の値を参照しましょう。
しばらくはPENDING_PROXY_CAPACITY
となっているかと思うので待ちましょう。
AUTH_FAILURE
と出た場合はロールの設定に問題がある場合が多いです。
他にもいくつかエラー時の表記があるので、下記リンクから対処法を確認しましょう。
最終的に値がAVAILABLE
となればOKです。
Lambdaの用意
これでRDS Proxy
の使用準備ができました。
先ほど状態チェックで使用したコマンドのレスポンスのうちEndpoint
にあたる値が、実際のRDS Proxy
のエンドポイントです。
前回の記事でのRDS
インスタンスとの導通確認ではローカルからpsql
を使って接続できることを確認しました。
しかしながらRDS Proxy
はVPC
内からしかアクセスできない(以下参照)ので、今回はVPC
内にあるLambda
からアクセスするものとします。
RDSプロキシは、データベースと同じ仮想プライベートクラウド(VPC)に存在する必要があります。データベースはアクセスできますが、プロキシはパブリックにアクセスできません。
Lambdaの内容
以下のようなLambda
をRDS Proxy
があるVPC
の中に配置します。
環境変数に各種設定がされており、DB_HOST
には前述のEndpoint
の値が入ります。
また、layers
等を用いてpg
を利用できる状態にしておきます。
今回クエリを投げるTASK
テーブルは、前回記事で既に作成済みのものとします。
const {Client} = require('pg');
exports.handler = async (event, context) => {
try{
// client設定
const client = new Client({
host: process.env.DB_HOST,
port: process.env.PORT,
user: process.env.USER,
password: process.env.PASSWORD,
});
// 接続
await client.connect(function(err) {
if (err) {
console.error('connection error', err.stack)
}
});
// クエリ実行
const res = await client.query('SELECT id, name FROM TASK');
console.log(res);
return {
statusCode: 200,
body: JSON.stringify(res.rows),
}
}catch (e) {
console.log("Error...");
console.log(e);
throw e;
}finally{
}
};
このLambda
関数を実行することで、以下のレスポンスが返りました。
{
"statusCode": 200,
"body": "[{\"id\":0,\"name\":\"task1\"},{\"id\":1,\"name\":\"task2\"}]"
}
これでLambda
関数からRDS Proxy
経由で接続できていることがわかります。
まとめ
今回はRDS Proxy
を経由してLambda
からRDS
インスタンスにクエリを投げるところまでを実装しました。
簡略化した実装ですが、今回の内容を用いてAPI Gateway
と組み合わせることでDB
から動的なレスポンスを返すREST API
は作れるようになります。
今回の内容が役立ちましたら幸いです。
参考
Discussion