はじめに
Node.js 4.3でAWS Lambdaファンクションを作成し、MySQL(Amazon RDS for MySQL)へ接続するサンプルです。
Lambdaファンクション内のMySQL接続パスワードは平文ではなく、AWS Key Management Service(KMS)により暗号化したMysQL接続パスワード文字列を記載し、Lambdaファンクション実行時にkms.decryptでパスワードを復号化してMySQLデータベースへ接続します。
また、Lambdaファンクションの実行をAWS SNSでメール通知します。
参考資料
以下のページを参考にさせて頂きました。ありがとうございました。
http://qiita.com/Keisuke69/items/cba4b501e91da95188f8
作業用マシン
Lambdaファンクションのデプロイパッケージ作成はAmazon LinuxのEC2インスタンスで行いました。
Lambdaファンクションのデプロイパッケージ作業用マシンは以下のAMIで作成しました。
・Amazon Linux AMI 2016.03.0 (HVM), SSD Volume Type - ami-f80e0596
テスト用のAmazon RDS for MySQL
まず、Amazon RDS for MySQLインスタンスを作成し、テストデータを追加します。
[ec2-user@example-server ~]$ hostname
example-server
[ec2-user@example-server ~]$ mysql -u root -p -h example-rds-mysql-server.carvmoii2uds.ap-northeast-1.rds.amazonaws.com
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 12
Server version: 5.6.27-log MySQL Community Server (GPL)
Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP ON exampledb.* TO example_user@'192.168.0.0/255.255.0.0' IDENTIFIED BY 'exam
ple_password';
Query OK, 0 rows affected (0.00 sec)
mysql> DELETE FROM exampledb.example_table;
Query OK, 6 rows affected (0.00 sec)
mysql> SELECT * FROM exampledb.example_table;
Empty set (0.00 sec)
mysql> INSERT INTO exampledb.example_table( id, insert_date) VALUES( 1, '2016-03-10 12:30:00' );
Query OK, 1 row affected (0.01 sec)
mysql> INSERT INTO exampledb.example_table( id, insert_date) VALUES( 2, '2016-03-20 12:30:00' );
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO exampledb.example_table( id, insert_date) VALUES( 3, '2016-03-30 13:40:00' );
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO exampledb.example_table( id, insert_date) VALUES( 4, '2016-04-05 14:10:00' );
Query OK, 1 row affected (0.01 sec)
mysql> INSERT INTO exampledb.example_table( id, insert_date) VALUES( 5, '2016-04-10 22:00:00' );
Query OK, 1 row affected (0.01 sec)
mysql> SELECT * FROM exampledb.example_table;
+------+---------------------+
| id | insert_date |
+------+---------------------+
| 1 | 2016-03-10 12:30:00 |
| 2 | 2016-03-20 12:30:00 |
| 3 | 2016-03-30 13:40:00 |
| 4 | 2016-04-05 14:10:00 |
| 5 | 2016-04-10 22:00:00 |
+------+---------------------+
5 rows in set (0.00 sec)
mysql> SELECT * FROM exampledb.example_table WHERE insert_date <= date_sub(curdate(), interval 2 week);
+------+---------------------+
| id | insert_date |
+------+---------------------+
| 1 | 2016-03-10 12:30:00 |
| 2 | 2016-03-20 12:30:00 |
+------+---------------------+
2 rows in set (0.00 sec)
mysql>
Lambda用のIAMロール設定
IAMロール[lambda_basic_vpc_execution]に以下のIAMポリシーを設定する。
・KMSによる暗号化したパスワード復号化用のIAMポリシー
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1448696327000",
"Effect": "Allow",
"Action": [
"kms:*"
],
"Resource": [
"arn:aws:kms:ap-northeast-1:**************:key/KMSの暗号化キーのARNを指定する"
]
}
]
}
・SNS通知用のIAMポリシー
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1460306102000",
"Effect": "Allow",
"Action": [
"sns:Publish"
],
"Resource": [
"arn:aws:sns:ap-northeast-1:***********:example-lambda-mysql-sns"
]
}
]
}
・Lambda VPC用のポリシー
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Effect": "Allow",
"Action": [
"ec2:CreateNetworkInterface",
"ec2:DescribeNetworkInterfaces",
"ec2:DetachNetworkInterface",
"ec2:DeleteNetworkInterface"
],
"Resource": "*"
}
]
}
作業用マシンにNode.jsとnpmインストール
作業用マシン(Amazon Linux)にNode.jsとnpmをインストールします。
[ec2-user@example-server ~]$ hostname
example-server
[ec2-user@example-server ~]$ sudo yum -y update
[ec2-user@example-server ~]$ uname -a
Linux example-server 4.4.5-15.26.amzn1.x86_64 #1 SMP Wed Mar 16 17:15:34 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
[ec2-user@example-server ~]$
[ec2-user@example-server ~]$ sudo yum install nodejs npm --enablerepo=epel
[ec2-user@example-server ~]$ node -v
v0.10.42
[ec2-user@example-server ~]$ npm -v
1.3.6
[ec2-user@example-server ~]$
AWS Lambdaファンクションのデプロイパッケージを作成する
[ec2-user@example-server ~]$ mkdir /home/ec2-user/example-lambda-mysql-select
[ec2-user@example-server ~]$
[ec2-user@example-server ~]$ cd /home/ec2-user/example-lambda-mysql-select
[ec2-user@example-server example-lambda-mysql-select]$ pwd
/home/ec2-user/example-lambda-mysql-select
[ec2-user@example-server example-lambda-mysql-select]$ npm install mysql
Lambdaファンクションのコードを[example.js]ファイルとして保存します。
[example.js]ファイルには後述の[Node.jsのAWS Lambdaファンクションサンプルコード(example.js)]の内容を記述します。
[ec2-user@example-server example-lambda-mysql-select]$ vi /home/ec2-user/example-lambda-mysql-select/example.js
Lambdaファンクションのデプロイパッケージを作成します。zipでexample-lambda-mysql-select配下を固めます。
[ec2-user@example-server example-lambda-mysql-select]$ pwd
/home/ec2-user/example-lambda-mysql-select
[ec2-user@example-server example-lambda-mysql-select]$ zip -r example-lambda-mysql-select.zip .
[ec2-user@example-server example-lambda-mysql-select]$ ls -lrta /home/ec2-user/example-lambda-mysql-select/
total 272
drwxrwxr-x 3 ec2-user ec2-user 4096 Apr 11 00:55 node_modules
-rw-rw-r-- 1 ec2-user ec2-user 2649 Apr 11 00:55 example.js
drwx------ 21 ec2-user ec2-user 4096 Apr 11 00:55 ..
-rw-rw-r-- 1 ec2-user ec2-user 258434 Apr 11 00:56 example-lambda-mysql-select.zip
drwxrwxr-x 3 ec2-user ec2-user 4096 Apr 11 00:56 .
[ec2-user@example-server example-lambda-mysql-select]$
zipファイル化したデプロイパッケージをS3へアップロードします。このzipファイルは後でLambdaファンクション作成時に利用します。
[ec2-user@example-server example-lambda-mysql-select]$ aws s3 cp /home/ec2-user/example-lambda-mysql-select/example-lambda-mysql-select.zip s3://適当なS3バケット/
upload: ./example-lambda-mysql-select.zip to s3://適当なS3バケット/example-lambda-mysql-select.zip
[ec2-user@example-server example-lambda-mysql-select]$
Node.jsのAWS Lambdaファンクションサンプルコード(example.js)
以下がLambdaファンクションのコードです。
var AWS = require('aws-sdk');
var mysql = require('mysql');
// KMSにより暗号化したMySQL接続用のパスワード文字列
var kmsEncyptedToken = "CiC*********(KMSで暗号化したMySQL接続用のパスワード文字列)***********UI=";
// 接続先のMySQLサーバ情報
var mysql_host = "example-rds-mysql-server.carvmoii2uds.ap-northeast-1.rds.amazonaws.com";
var mysql_user = "example_user";
var mysql_dbname = "exampledb";
var mysql_password = "";
var sns_topic_arn = "arn:aws:sns:ap-northeast-1:*******************:example-lambda-mysql-sns";
exports.handler = function(event, context){
// 実行するSQL文
var sql = "SELECT * FROM exampledb.example_table WHERE insert_date <= date_sub(curdate(), interval 2 week)";
if (kmsEncyptedToken && kmsEncyptedToken !== "<kmsEncryptedToken>") {
var encryptedBuf = new Buffer(kmsEncyptedToken, 'base64');
var cipherText = {CiphertextBlob: encryptedBuf};
var kms = new AWS.KMS({ region: 'ap-northeast-1' });
var sns = new AWS.SNS({ region: 'ap-northeast-1' });
// KMSにより暗号化したパスワード文字列の復号化
kms.decrypt(cipherText, function (err, data) {
if (err) {
console.log("CipherText Decrypt error: " + err);
context.fail(err);
} else {
mysql_password = data.Plaintext.toString('ascii');
var connection = mysql.createConnection({
host : mysql_host,
user : mysql_user,
password : mysql_password,
database : mysql_dbname
});
console.log("MySQL Server Name: " + mysql_host);
console.log("MySQL User Name: " + mysql_user);
console.log("MySQL Database Name: " + mysql_dbname);
console.log("MySQL Exec SQL: " + sql);
// MySQLデータベースへの接続
connection.connect();
// MySQLデータベースでSQL実行
connection.query(sql, function(err, rows, fields) {
if (err) {
console.log("MySQL Select Error");
context.fail(err);
sns.publish({
Message: 'Lambda Function Error',
Subject: 'Lambda Function Error',
TopicArn: sns_topic_arn
}, function(err, data){
if(err) throw err;
else context.fail('SNS Publish Error');
});
throw err;
} else {
console.log("MySQL Select Success");
console.log(rows);
console.log(fields);
}
});
connection.end(function(err) {
sns.publish({
Message: 'Lambda Function Success',
Subject: 'Lambda Function Success',
TopicArn: sns_topic_arn
}, function(err, data){
if(err) throw err;
else context.fail('SNS Publish Error');
});
context.done();
});
}
});
} else {
context.fail("kmsEncyptedToken has not been set.");
}
console.log('end');
};
Lambdaファンクション作成
先ほど作成したLambdaのデプロイパッケージ[example-lambda-mysql-select.zip]をLambdaにアップロードして、Lambdaファンクションを作成します。
前述の手順でS3にパッケージアップロードしていると思いますので、それを選択します。
s3://適当なS3バケット/example-lambda-mysql-select.zip
LambdaファンクションのIAMポリシーは[lambda_basic_vpc_execution]を選択、セキュリティグループは、Amazon RDS for MySQLインスタンスへMySQL接続可能な設定のセキュリティグループを選択して、Lambdaファンクションを作成します。
Lambdaファンクション実行
Lambdaファンクションを実行し、正常にMySQLデータベースへ接続出来れば、Lambdaの[Log outPut]に以下のようにMySQLデータベースのSELECT結果が表示されます。
また、ANS SNSで指定したメールアドレス宛に[Lambda Fanction Success]メールが届きます。
START RequestId: 442b53cf-ff43-11e5-9dc8-2123a6cf68bb Version: $LATEST
2016-04-10T17:40:08.926Z 442b53cf-ff43-11e5-9dc8-2123a6cf68bb end
2016-04-10T17:40:10.127Z 442b53cf-ff43-11e5-9dc8-2123a6cf68bb MySQL Server Name: example-rds-mysql-server.carvmoii2uds.ap-northeast-1.rds.amazonaws.com
2016-04-10T17:40:10.183Z 442b53cf-ff43-11e5-9dc8-2123a6cf68bb MySQL User Name: example_user
2016-04-10T17:40:10.183Z 442b53cf-ff43-11e5-9dc8-2123a6cf68bb MySQL Database Name: exampledb
2016-04-10T17:40:10.183Z 442b53cf-ff43-11e5-9dc8-2123a6cf68bb MySQL Exec SQL: SELECT * FROM exampledb.example_table WHERE insert_date <= date_sub(curdate(), interval 2 week)
2016-04-10T17:40:10.346Z 442b53cf-ff43-11e5-9dc8-2123a6cf68bb MySQL Select Success
2016-04-10T17:40:10.346Z 442b53cf-ff43-11e5-9dc8-2123a6cf68bb [ RowDataPacket { id: 1, insert_date: Thu Mar 10 2016 12:30:00 GMT+0000 (UTC) },
RowDataPacket { id: 2, insert_date: Sun Mar 20 2016 12:30:00 GMT+0000 (UTC) } ]
2016-04-10T17:40:10.567Z 442b53cf-ff43-11e5-9dc8-2123a6cf68bb [ FieldPacket {
catalog: 'def',
db: 'exampledb',
table: 'example_table',
orgTable: 'example_table',
name: 'id',
orgName: 'id',
charsetNr: 63,
length: 12,
type: 3,
flags: 0,
decimals: 0,
default: undefined,
zeroFill: false,
protocol41: true },
FieldPacket {
catalog: 'def',
db: 'exampledb',
table: 'example_table',
orgTable: 'example_table',
name: 'insert_date',
orgName: 'insert_date',
charsetNr: 63,
length: 19,
type: 12,
flags: 128,
decimals: 0,
default: undefined,
zeroFill: false,
protocol41: true } ]
END RequestId: 442b53cf-ff43-11e5-9dc8-2123a6cf68bb
REPORT RequestId: 442b53cf-ff43-11e5-9dc8-2123a6cf68bb Duration: 2670.49 ms Billed Duration: 2700 ms Memory Size: 128 MB Max Memory Used: 30 MB
以上になります。