概要
serverless frameworkで作成するlambda関数から同じくserverless frameworkで作成したRDSのmysqlへ接続を試しました。
今回利用するRDSはこちらの記事で実装したものになります。 serverless framework でバブリックアクセス可能なmysqlを立ててみた | もふもふ技術部
※ 本記事ではRDSProxyに関しては触れていません
実装
流れ
1. mysqlの準備
mysqlへアクセスし、データベースとテーブルを作成します。
$ mysql -h <endpoint> -u <user> -p $ <password> mysql> CREATE DATABASE mydb; mysql> use mydb; mysql> CREATE TABLE customers (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255));
2. mysqlに接続するlambda関数を作成
serverless-mysqlというライブラリを追加
これはserverless frameworkでmysqlを利用するのに特化したパッケージです。
yarn add serverless-mysql
lambda関数を作成
handler.js
import MySQL from "serverless-mysql";
// mysqlモジュールの初期化
// 関数外でモジュールを初期化することで実行間の接続で再利用が可能になる
const mysql = MySQL({
// backoff: 'decorrelated',
// base: 5,
// cap: 200
});
// 接続設定オプションは初期化時でも後でも渡すことができる
// https://github.com/mysqljs/mysql#connection-options
mysql.config({
database: process.env.MYSQL_DATABASE,
user: process.env.MYSQL_USER,
password: process.env.MYSQL_PASSWORD,
port: process.env.MYSQL_PORT,
});
export const customerIndex = async (event) => {
const results = await mysql.query("SELECT * FROM customers");
await mysql.end();
return results;
};
export const customerCreate = async (event) => {
const body = JSON.parse(event.body);
const query = `INSERT INTO customers (name) VALUES ('${body.name || ""}')`;
const results = await mysql.query(query);
await mysql.end();
return results;
};
3. 通常の方法でlambda関数をデプロイする
serverless.ymlにfunctionsを追加する
functions:
customerIndex:
handler: handler.customerIndex
events:
- httpApi:
path: /customers
method: get
customerCreate:
handler: handler.customerCreate
events:
- httpApi:
path: /customers/create
method: post
この状態で一度デプロイを行い、curlにより関数を叩くと Task timed out after 6.01 seconds というエラーが起こります。
これはlambda関数からインターネットへのアクセスができないことで起こるエラーで、解決するにはRDSと同じVPC内に関数を置く必要があるとのことでした。
4. lambda関数をVPCに配置する構成になるよう変更を加える
providerにvpcに関する設定を追加することでlambda関数をvpcの内部に設定します。
provider:
vpc:
securityGroupIds:
- !GetAtt SecurityGroup.GroupId
subnetIds:
- !GetAtt PublicSubnetA.SubnetId
- !GetAtt PublicSubnetB.SubnetId
vpcの内部に設置したlambda関数からRDSへ接続するためのインバウンドルールを追加する。 lambda関数を設置したサブネットのcidrブロックをインバウンドルールに設定することで接続することができるようになります。
resources:
Resources:
...
SecurityGroupIngress:
Type: AWS::EC2::SecurityGroupIngress
Properties:
IpProtocol: tcp
CidrIp: ${self:custom.config.db.myip}
GroupId: !GetAtt SecurityGroup.GroupId
ToPort: 3306
FromPort: 3306
# 以下のを追加する ---
SecurityGroupIngress2:
Type: AWS::EC2::SecurityGroupIngress
Properties:
IpProtocol: tcp
CidrIp: 10.0.64.0/20
GroupId: !GetAtt SecurityGroup.GroupId
ToPort: 3306
FromPort: 3306
SecurityGroupIngress3:
Type: AWS::EC2::SecurityGroupIngress
Properties:
IpProtocol: tcp
CidrIp: 10.0.80.0/20
GroupId: !GetAtt SecurityGroup.GroupId
ToPort: 3306
FromPort: 3306
この状態でデプロイを行うとlambda関数からmysqlへの接続が成功します。
以上で実装は終了となります。
今回のコードはgithubに挙げていますので興味ありましたらご確認ください。
https://github.com/naok1207/serverless-rds-mysql
補足
今回の構成ではlambda関数毎にコネクションが作成され、運用に耐えうる構成とはなっていないので、運用を想定とする場合には、DBProxyでの構成を行うことをお勧めします。
まとめ
最初はlambda関数からパブリックアクセス可能なmysqlへの接続は簡単に行えると考えていましたが、実際にやってみると思ったより難しく、インバウンドルールの設定などで結構詰まってしまいました。最終的にはしっかりと接続ができる形で作成できてよかったです。コストが怖くて挑戦できなかったDBProxyもまたそのうち挑戦したいと思います。
会社の紹介
株式会社 mofmof では一緒に働いてくれるエンジニアを募集しています。 興味のある方は是非こちらのページよりお越しください! https://www.mof-mof.co.jp/ https://www.mof-mof.co.jp/recruit/