本篇文章紀錄,從現有的 Express Proejct,在搬上 AWS Lambda 時解決 MySQL 連接所造成的問題。
首先,預備上 Lambda 的專案有以下功能:
- 原本就自帶 Express
- 使用 mysql client 初始化長連接
- RDS (MySQL 8)
- Lambda Function
- API Gateway
- CloudFormation
- 如果 serverless-offline 出錯,那就代表你本身的程式碼有問題,必須注意 serverless 是無狀態的,所以是每次執行 api 都會從 main.js 執行,然後執行到 module.export 的那些 function。
- 如果 serverless-offline 正常,那表示可能是 VPC / Security Group 設定錯誤,這會在最下面討論。
Express 修正為 Serverless Express
const express = require("express");
const mysql = require('mysql');
const https = require('https');
const bodyParser = require('body-parser');
const cors = require("cors");
const fs = require('fs');
const path = require('path');
const morgan = require('morgan');
var dov = require('dotenv').config();
// AWS Lambda serveless
const sls = require('serverless-http')
// Use connection pool to handle each query
// https://stackoverflow.com/questions/54888061/configuration-mysql-node-js-error-can-not-enqueue-query-after-fatal-error
var db = mysql.createPool({
host: host, //RDS Endpoint
port: port, //MySQL: 3306
user: "admin",
password: passowrd,
database: default_db_name
});
/*
var db = mysql.createConnection({
host: host, //RDS Endpoint
port: port, //MySQL: 3306
user: "admin",
password: passowrd,
database: default_db_name
});
*/
// you're using connection pool, no need to connect on initialization
// connect to database
/*db.connect((err) => {
if (err) {
console.log(err)
throw err;
}
console.log('Connected to database');
});*/
global.db = db;
app = express();
app.use(morgan('dev'));
app.use(cors({
origin:["*"],
"methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
"preflightContinue": false,
"allowedHeaders": ['Content-Type', 'Authorization'],
"optionsSuccessStatus": 204
}));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: true
}));
//Router
app.use('/', require('./myRouter'));
// no need to specific listen
//app.listen(8080)
// serveless export usage
module.exports.server = sls(app)exports.getXXXData = (req,res) => {
//First to activate and get connection from pool
db.getConnection(function(err, connection) {
//Callback connection object can be use to query
connection.query(`SELECT * FROM XXX`,(err, result) => {
//When done by query, release connection immediately
connection.release();
if (!!err){
res.send({setting: err.message});
return;
}
res.send({setting: result});
});
});
};
/*exports.getXXXData = (req,res) => {
db.query(`SELECT * FROM XXX`, (err, result) => {
if (!!err){
res.send({setting: err.message});
return;
}
res.send({setting: result});
});
};*/
新增 AWS Lambda Config 檔 (serverless.yml)
service: Your Service Name
provider:
name: aws
runtime: nodejs12.x
stage: dev # api gateway stage
region: us-east-2 #Ohio
memorySize: 512
functions:
app:
handler: app.server # app.js exported module: `module.exports.server = sls(app)`, name is `server`
events:
- http:
path: /
method: ANY
cors: true
- http: # all routes get proxied to the Express router
path: /{proxy+}
method: ANY
cors: true
- http:
path: getXXXData
method: post
cors: true
plugins:
- serverless-offline
離線測試與佈署
需要安裝的工具:
npm install serverless --save
npm install serverless-http --savenpm install serverless-mysql --save
npm install serverless-offline --savesudo chmod -R 777 ./
然後,開啟離線測試指令
SLS_DEBUG=* sls offline start
此時就會看到離線測試的 API 清單:
確認正常後,使用 deploy 指令就可以佈署到雲端 AWS Lambda。
SLS_DEBUG=* sls deploy
Lambda Function 上雲後,連上 RDS 必要步驟
RDS 開放白名單或暫時公開全域
開放指定或全域的 Traffic 或 TCP ,才能讓外部連線進來,先到 RDS 選擇到 Security Group,並且把 Inbound, Outbound 設定 All Traffic / 0.0.0.0/0 以及 All Traffic / ::/0。
詳情說明,可以看 Stackoverflow 上其中一篇討論: AWS: can't connect to RDS database from my machine。
總之, RDS 的 Security Group 必須要開放 Inbound, Outbound Rule,而且完全對外的話,一定要有:
- 0.0.0.0/0
- ::/0
Lambda 開啟 VPC 權限及連線 Policy
對 Lambda 來說,Lambda 本身無法訪問 RDS ,是受到 VPC 跟 Policy 限制,因此,需要打開 Security Group Inbound, Outbound Rule 以及必須讓 Lambda 擁有 RDS 權限 (AWSLambdaVPCAccessExecutionRole)。 則有以下必須確認的步驟:
- Lambda 以及 RDS 必須共用同一個 VPC (需自行確認)
- Lambda Security Group 有開通 Inbound, Outbound Rule
- Lambda Policy 套用到 AWSLambdaVPCAccessExecutionRole
首先,到 Lambda 上,選到目標的 Application,然後到 Permissions 進入 Execution role:
然後,在 Policy 頁面增加 AWSLambdaVPCAccessExecutionRole 這個權限:
然後,確認 Lambda 的 VPC 名稱後,到 EC2/VPC 底下,更改 Inbound, Outbound Rule:
詳情說明,可以看 Stackoverflow 上其中一篇討論: Error: connect ETIMEDOUT rds lambda 。
完成後,到 API Gateway 就可以正常測試:
Reference:
https://stackoverflow.com/questions/35880022/error-connect-etimedout-rds-lambda
https://stackoverflow.com/questions/37212945/aws-cant-connect-to-rds-database-from-my-machine
https://docs.aws.amazon.com/lambda/latest/dg/configuration-database.html
https://medium.com/@hk_it_er/create-lambda-and-api-gateway-nodejs-aws-serverless-to-rds-mysql-6a75243e61cc
https://github.com/awsdocs/aws-lambda-developer-guide/blob/master/sample-apps/rds-mysql/dbadmin/index.js
https://www.youtube.com/watch?v=cGYknt3xIvM
https://redstapler.co/aws-lambda-tutorial-rds-mysql/
https://aws.amazon.com/blogs/compute/using-amazon-rds-proxy-with-aws-lambda/
https://docs.aws.amazon.com/zh_tw/lambda/latest/dg/configuration-vpc.html
沒有留言:
張貼留言