はじめに
RDS Proxy 経由で MySQL にアクセスするLambdaをVPC内で実行させましたので、手順をまとめます。
以前、mysql
モジュールを使用して同様の処理をしましたが、mysql2
モジュールの方がコード量が少ないため、今回はmysql2
を使用してみます。
RDS Proxyの作成
RDS Proxyは、以前作成した記事がありますので、こちらを参考にしてください。
private subnetにLambdaを作成
設定
-
IAMロール
-
AWSLambdaENIManagementAccess
を付与します.これによってVPC上でLambdaが実行できます。
-
-
タイムアウト時間
- 30秒にします。実行に数秒かかります。
-
環境変数
- RDS_HOSTNAME:proxyのライターエンドポイント
- RDS_DB_NAME:データベース名
- RDS_USERNAME:ユーザー名
- RDS_PASSWORD:パスワード
-
VPC
- private subnetに設置
- sgの設定は以下の通り
設定 | タイプ | ポート | ソース |
---|---|---|---|
Lambda | All | All | 0.0.0.0/0 |
Proxy | MySQL/Aurora | 3306 | LambdaのSG |
Aurora | MySQL/Aurora | 3306 | ProxyのSG |
mysql2モジュール
Lambdaは、Nodejsを使用します。
mysql2
モジュールを使用するため、ローカルでmysql2
モジュールをインストールします。
LambdaLayerでアップロード
$ mkdir nodejs
$ cd nodejs
$ npm init -y
$ ls
package.json
$ npm install mysql2
$ tree
.nodejs
├── node_modules
├── package-lock.json
└── package.json]
$ cd ..
$ zip -r nodejs.zip nodejs
nodejsフォルダごとzip化すること。
zip化したnodejs.zipをLambdaLayerにアップしましょう。
具体的なアップロード方法は、以前作成した記事を参考にしてください
Lambdaに直接アップロード
LambdaLayerを使用しない場合、以下のように作成します。
$ mkdir nodejs
$ cd nodejs
$ tree
.nodejs
├── package-lock.json
└── package.json
// package name以外はデフォルトにしました
$ npm init -y
$ npm install mysql
$ touch index.js
$ tree
.nodejs
├── index.js
├── node_modules
├── package-lock.json
└── package.json
$ zip -r nodejs.zip *
Lambdaのコード
INSERTの場合
const hostname = process.env.RDS_HOSTNAME;
const user_name = process.env.RDS_USERNAME;
const password = process.env.RDS_PASSWORD;
const databaseName = process.env.RDS_DB_NAME;
const mysql = require('mysql2/promise');
exports.handler = async (event) => {
// console.log('event:', JSON.stringify(event, null, 2));
const connection = await mysql.createConnection({
host: hostname,
user: user_name,
password: password,
database: databaseName,
});
const now = new Date().toLocaleString('ja-JP', { timeZone: 'Asia/Tokyo' });
const sql = 'INSERT INTO database_table (title, created_at) VALUES (?, ?);';
const inserts = ['test', now];
try {
await connection.beginTransaction();
const results = await connection.execute(sql, inserts);
console.log(results);
await connection.commit();
} catch (error) {
await connection.rollback();
console.log(error.message);
throw error;
} finally {
connection.end();
}
};
今回は、database_table
というテーブルに、タイトルと作成日時を登録してみました。
'INSERT INTO databaseTable (title, created_at) VALUES (?, ?);'
以前、下記の記事でmysql
モジュールで書いたコードよりもコード量が少なかったため、mysql2
モジュールの使用をおすすめします。
mysql2の使用メソッド
-
createConnection
- RDSのmysqlと接続します
-
beginTransaction
- トランザクションを開始します
-
query
- クエリを実行します
-
commit
- トランザクション中のデータ処理を確定します
-
rollback
- トランザクション中のデータ処理を取り消され、トランザクション実行前の状態に戻りトランザクションを終了します。
-
end
- データベースと接続を終了します。
SELECT文の場合 ①
INSERT
ではなく、SELECT
文を使った場合、以下のようになります。
usersテーブルからデータを全て取得する、シンプルな内容です。
const dbInfo = {
host: process.env.RDS_HOSTNAME,
user: process.env.RDS_USERNAME,
password: process.env.RDS_PASSWORD,
database: process.env.RDS_DB_NAME,
};
const mysql = require('mysql2/promise');
exports.handler = async (event) => {
const connection = await mysql.createConnection(dbInfo);
const usersSql = 'SELECT * FROM `users`';
try {
await connection.beginTransaction();
const usersSqlResults = await connection.query(usersSql);
console.log('Received usersSqlResults[0]:', JSON.stringify(usersSqlResults[0], null, 2));
await connection.commit();
} catch (error) {
await connection.rollback();
console.log(error.message);
throw error;
} finally {
connection.end();
}
};
SELECT文の場合 ②
複数のお店のIDからそれぞれのユーザーIDを配列に収め、配列を返すという内容になります。
条件は、お店のオープン時間が12時より早く、かつ、閉店時間が20時より遅いことです。
// db情報は、変数にしてもよい
const dbInfo = {
host: process.env.RDS_HOSTNAME,
user: process.env.RDS_USERNAME,
password: process.env.RDS_PASSWORD,
database: process.env.RDS_DB_NAME,
};
const mysql = require('mysql2/promise');
const getStoreIds = [23, 25, 29];
const startTime = 'start_time_Monday';
const endTime = 'end_time_Monday';
exports.handler = async (event) => {
let userId = [];
for (const storeId of getStoreIds) {
const connection = await mysql.createConnection(dbInfo);
const storesInserts = [storeId];
const storesSql =
'SELECT `user_id` FROM `stores` WHERE `store_id` = ? AND ' + startTime + ' < 12:00 AND ' + endTime + ' > 20:00;';
try {
await connection.beginTransaction();
const storesSqlResults = await connection.query(storesSql, storesInserts);
console.log('Received storesSqlResults[0]:', JSON.stringify(storesSqlResults[0], null, 2));
// 値がなければスキップ
if (!storesSqlResults[0][0]) continue;
// 返ったuser_idを配列に加える
storesSqlResults[0].map((item, index) => {
userId.push(item.token);
});
await connection.commit();
} catch (error) {
await connection.rollback();
console.log(error.message);
throw error;
} finally {
connection.end();
}
}
console.log('Received userId:', JSON.stringify(userId, null, 2));
return userId;
};
SQL内のstoresSql
にある?
には、storesInserts
が入ります。
対して、SQLのテーブル名やカラム名に変数を当てたい場合、SQL内に文字列として結合させます。
今回で言いますと、startTime
とendTime
ですね。
参考記事