Node.jsを使用してRDSデータベースに接続するLambda関数を作成しようとしています。データベースは動作しており、Elastic Beanstalk環境からアクセスできます。関数を実行すると、タイムアウトエラーが返されます。
まったく同じ結果で、タイムアウトを最大5分間まで増加させようとしました。
いくつかの研究の後に私が思いついた結論は、おそらくセキュリティの問題であるが、Amazonのドキュメントまたは this answer(このトピックで見つけることができる唯一のもの)で解決策を見つけることができなかったということです。
セキュリティの詳細は次のとおりです。
私のコードは:
'use strict';
console.log("Loading getContacts function");
var AWS = require('aws-sdk');
var mysql = require('mysql');
exports.handler = (event, context, callback) => {
var connection = mysql.createConnection({
Host : '...',
user : '...',
password : '...',
port : 3306,
database: 'ebdb',
debug : false
});
connection.connect(function(err) {
if (err) callback(null, 'error ' +err);
else callback(null, 'Success');
});
};
私が得ている結果は次のとおりです。
"errorMessage": "2017-03-05T05:57:46.851Z 9ae64c49-0168-11e7-b49a-a1e77ae6f56c Task timed out after 10.00 seconds"
助けてくれたすべての人に感謝したい。問題は私が思っていたものとは異なることが判明した。コードのcallback
は、AmazonのOWN DEFAULT SAMPLEにあるにもかかわらず、何らかの理由で機能しません。
作業コードは次のようになります。
'use strict';
console.log("Loading getContacts function");
var AWS = require('aws-sdk');
var mysql = require('mysql');
exports.handler = (event, context) => {
var connection = mysql.createConnection({
Host : '...',
user : '...',
password : '...',
port : 3306,
database: 'ebdb',
debug : false
});
connection.connect(function(err) {
if (err) context.fail();
else context.succeed('Success');
});
};
コンテキストを使用しても機能しますが、追加する必要があるのはcontext.callbackWaitsForEmptyEventLoop = false;
をハンドラに追加し、次のように通常どおりコールバックを使用します。
exports.handler = (event, context) => {
context.callbackWaitsForEmptyEventLoop = false;
var connection = mysql.createConnection({
//connection info
});
connection.connect(function(err) {
if (err) callback(err);
else callback(null, 'Success');
});
};
答えはドキュメントにあります(これを見つけるのに数時間かかりました): http://docs.aws.Amazon.com/lambda/latest/dg/nodejs-prog-model-using-old- runtime.html
「コンテキストメソッドとコールバックメソッドの比較」セクションには、事柄を説明する「重要な」メモがあります。
メモの下部には次のように記載されています。
したがって、コンテキストメソッドと同じ動作が必要な場合は、コンテキストオブジェクトプロパティcallbackWaitsForEmptyEventLoopをfalseに設定する必要があります。
基本的に、コールバックはイベントループを終了するコンテキストとは対照的に、イベントループの最後まで継続します。したがって、callbackWaitsForEmptyEventLoopを設定すると、コールバックがコンテキストのように機能します。
RDSとLambdaは両方とも同じセキュリティグループに属します。
それが鍵です。デフォルトでは、同じセキュリティグループ内の通信は許可されていません。また、明示的に許可する必要があります(E.x sg-xxxxx ALL TCP)。これは、ラムダがプライベートIPでdbにアクセスしようとした場合にのみ機能します。
パブリックIPでアクセスしようとすると、機能しないため、そのために必要な全体をパンチする必要があります。
しかし、より良いアプローチがあります:
3306
で受信トラフィックを許可します。RDSを接続する際の経験を共有しています。
Lambda function
に対してVPC
アクセスを有効にする必要があります。その間に、 セキュリティグループ を割り当てます。
次に、RDSインスタンスに割り当てられたセキュリティグループ内で、Lambda関数に割り当てられたセキュリティグループのアクセスを有効にします。
詳細情報を入手できます こちら
最初にDBをセットアップすると、自動的にセキュリティグループが作成されます。 DBをセットアップしたIPがデフォルトになります。ラムダから実行すると、このルールはトラフィックをブロックします。 dbエラーログを確認すると、接続を拒否していることを確認できます。
***** could not be resolved: Name or service not known
ラムダトラフィックを許可するには、セキュリティグループにルールを作成する必要があります。 RDSインスタンスコンソールに移動し、セキュリティグループをクリックして、[受信]を選択します。そこにルールが表示されます。次に、世界に向けて電話をかけ、AWSラムダIPを見つけるか、VPCを作成します。
connection.end()はコールバックの後でなければなりません:
動作するコード:
'use strict';
var mysql = require('mysql');
var connection = mysql.createConnection({
Host : 'xxxxxx.amazonaws.com',
user : 'testuser',
password : 'testPWD',
port : 3306,
database: 'testDB',
debug : false
});
module.exports.handler = (event, context, callback) => {
// **Connection to database**
connection.connect(function(err) {
if (err) {
console.error('Database connection failed: ' + err.stack);
context.fail();
return;
}
else{
console.log('Connected to database.');
}
});
connection.query('show tables from testDB', function (error, results, fields) {
if (error) {
console.log("error: connection failed with db!");
connection.destroy();
throw error;
} else {
// connected!
console.log("info: connection ok with db!");
console.log(results);
context.succeed("done");
callback(error, results);
}
});
//Send API Response
callback(null, {
statusCode: '200',
body: 'succeed',
headers: {
'Content-Type': 'application/json',
},
});
//Close Connection
connection.end(); // Missing this section will result in timeout***
};
問題の原因はタイムアウトではなく、接続を閉じる方法です。 OR .destroy()
で接続を閉じるときにコールバックを正しく使用するコールバックを待ちたくない場合は、代わりに.end(function(err) { //Now call your callback });
を使用してください。
より詳細な説明については、 this thread を参照してください。
また、同様のタイムアウトシナリオに直面しました。問題はconnection.end()
の後にconnection.connect()
を実行していませんでした。 Connection.end()
はcallback
の前に実行する必要があります。
作業コード:
var mysql = require('mysql');
var connection = mysql.createConnection({
Host : 'Host_name',
user : 'root',
password : 'password'
});
module.exports.handler = (event, context, callback) => {
// **Connection to database**
connection.connect(function(err) {
if (err) {
console.error('Database connection failed: ' + err.stack);
return;
}
console.log('Connected to database.');
});
// **Hit DB Query**
connection.query("Query", function(err, rows, fields) {
console.log(rows);
});
//**Close Connection**
connection.end(); ***// Missing this section will result in timeout***
//**Send API Response**
callback(null, {
statusCode: '200',
body: "Success",
headers: {
'Content-Type': 'application/json',
},
});
};