それサーバレスでできません? 〜④LambdaからのRDSの利用
本作業の目的
LambdaからRDSを利用する場合はRDSのアクセス接続数が上限に達してしまいます。そのため、データベースへの接続プールを維持するRDS Proxyといった機能を追加する必要があります。
※RDS Proxyリリース以前はLambdaからRDSの直接利用はアンチパターンとされてきました。リリース発表時のインパクトは物凄い大きかったです、、
補足:RDSとDynamoDBの違い
「DynamoDB使えば問題ないのでは?」と考える方もいると思いますが、RDSとDynamoDBはそもそもの仕組み・用途が違うため、代替手段とすることはできません。細かい違いは多々ありますが、端的にシステムとしての違いを説明するとRDSはSQLであり、DynamoDBはNoSQLとなります。更に使い分けの説明をすると、「テーブル結合などの複雑のクエリを使用する場合はRDS(SQL)」、「複雑なクエリが必要ないが、パフォーマンスを上げたい場合はDynamoDB(NoSQL)」といった切り分けになります。
そのため、サーバレスにしたいからDynamoDBという判断ではなく、使用想定を基にした選定が必要になります。
※本章では、移行前のシステムがSQLの要件であったものとして進めています。
構築手順
作成済のデータベースへLambdaから接続するための設定手順について説明します。
1. DBへの接続情報の登録(Secrets Manager)
RDS Proxyから参照できるよう、データベースの接続情報(=ユーザー名、パスワード)をSecrets Managerに登録します。
1-1. Secrets Manager管理画面上で、「新しいシークレットを保存する」から新規作成を行います。
1-2. シークレットの種類として「RDSデータベースの認証情報」を選択し、作成済のデータベースの認証情報を入力します。
1-3. 接続情報管理用の名前を入力します。識別しやすい名前としてください。
1-4. 暗号鍵のローテションはデフォルトの設定のままとし、作成を完了します。(※セキュリティポリシーに合致しない場合などは、適宜変更ください。)
2. RDS ProxyからDB接続情報へのアクセス権限設定(=IAMロールの作成)
手順1で作成した認証情報へ、RDS Proxyからアクセスできるよう権限設定を行います。
※手順3の途中で自動生成されるため、説明を割愛します。
3. RDS Proxyの作成
3-1. RDSダッシュボードからプロキシ一覧へ進み、「プロキシを作成」からRDS Proxyの新規作成を行います。
3-2. Proxyの設定を行い、「プロキシを作成」から作成を実行します。
※設定抜粋(下記以外の項目はデフォルトで入力されている設定を利用)
・プロキシ識別子:(任意の名称)
・エンジンの互換性:MySQL
・Secrets Managerシークレット:(手順1で作成したシークレットを指定)
・サブネット:(RDSを配置するサブネットを指定)
4. 処理コードの変更
Lambdaのコード内の接続先情報をRDSからRDS Proxyへ変更します。
RDSのエンドポイントをRDSのエンドポイントに変更します。
import json
import mysql.connector
import os
DB_USER = os.environ["DB_USER"]
DB_PASSWORD = os.environ["DB_PASSWORD"]
DB_HOST = os.environ["DB_HOST"]
DB_NAME = os.environ["DB_NAME"]
def execute_query(event):
config = {
'user': DB_USER,
'password': DB_PASSWORD,
'host': DB_HOST, #<-RDS Proxyのエンドポイント(URL)に変更する
'database' : DB_NAME,
}
cnx = mysql.connector.connect(**config)
cursor = cnx.cursor()
query = ("SELECT SLEEP(10)")
cursor.execute(query)
def lambda_handler(event, context):
execute_query(event)
※RDS Proxyを使用したLambda関数一式は下記サンプルコードを参照ください。
参考:RDSを使用するLambda関数を作成するテンプレート(CloudFormationで作成)
リポジトリのディレクトリ構造
- source
- lambda_function.py
- requirment.txt
- .DS_Store
- buildspec.yml
- template.yml
- README.md
template.yml (CloudFormationテンプレート)
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
#構築時に、自分の環境の情報を入力する
Parameters:
SecurityGroupId01:
Type: String
Subnet01:
Type: String
Subnet02:
Type: String
Subnet03:
Type: String
DBUser:
Type: String
DBPassword:
Type: String
DBHost:
Type: String
DBName:
Type: String
Resources:
SampleFunction:
Type: AWS::Serverless::Function
Properties:
Handler: lambda_function.lambda_handler
Runtime: python3.8
FunctionName: sample-function-userds
CodeUri: ./source
VpcConfig:
SecurityGroupIds:
- !Ref SecurityGroupId01
SubnetIds:
- !Ref Subnet01
- !Ref Subnet02
- !Ref Subnet03
Environment:
Variables:
DB_USER: !Ref DBUser
DB_PASSWORD: !Ref DBPassword
DB_HOST: !Ref DBHost
DB_NAME: !Ref DBName
buildspec.yml (Code Buildのビルド設定)
version: 0.2
phases:
install:
runtime-versions:
python: 3.8
commands:
- sam build
#出力先バケット名はあらかじめ作成しておく(下記"OUTPUT_S3_BUCKET"の部分)
- sam package --s3-bucket OUTPUT_S3_BUCKET --output-template-file outputtemplate.yml
artifacts:
type: zip
files:
- template.yml
- outputtemplate.yml
requirement.txt (Pythonコードで使用するライブラリの指定)
mysql-connector-python==8.0.16
おすすめの一冊
サーバレスよりもDevOpsの話ですが、業務利用に向けて勉強になる書籍があったため、紹介します。
個人、チーム、プロジェクト全体の各レベルでの活用方法が記載されているため、新人からPMの方まで幅広く読める内容だと思います。
(アフィリエイトなので、以下リンクより購入いただけたら嬉しいです、、)