概要
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/