参考URL
- S3 + Lambdaを使って、バックアップ完了をhipchatに通知する - Qiita
- S3にPUTしたらAWS LambdaからYoする | Developers.IO
- S3にアップロードしたCSVファイルをLambda経由でAmazon RedshiftにCOPYする – AWS Lambda Advent Calendar 2014:2日目 | Developers.IO
- 初めてのJavaScript、初めてのAWS Lambda | Developers.IO
- node.jsのいろいろなモジュール17 – asyncで非同期処理のフロー制御 | Developers.IO
- The Open Universe: Basic Authentication with Node.js Request Module
- (祝)AWS LambdaのVPC利用が可能になりました | Developers.IO
- AWS Lambda編~S3イベント通知機能を実装する~|Event Notification ストレージ監視 CloudWatch logs| | ナレコムAWSレシピ
- LambdaからセキュアにRDSに接続する | ナレコムAWSレシピ
- [node]AWS LambdaをJavascriptから呼び出す | Developers.IO
これをやった経緯
- 他システムとのデータ連携をCSVファイルを介して、データ連携する要件が発生
- そのデータをS3のような「耐久性」、「可用性」、「安全性」の高いストレージを用意して受け渡す必要があった
- 担当システムではバッチ処理をJenkinsで管理しており、今回もそれを利用したかった
- 定期実行でも対応はできたが、それだとかっこ悪いなーと思ったので、S3にファイル配置されたときだけ自動実行したかった
手段検討
参考URL1つ目のS3のイベントをLambdaで拾って、hipchatに通知できるなら、Jenkinsのジョブもたたけるじゃないか!
ということで、下記のような仕組みを妄想w
- 他システムより、CSVファイルがS3にアップロードされる
- S3のイベントにて、オブジェクトがCreateされた際に、イベント発行
- 発行されたイベントをLambdaで受取り、Jenkinsのジョブをキック。ついでにHipchatにも通知する。
実現方法
-
まずは、該当のCSVファイルを取込むバッチ処理を作ることだが、それは割愛します。
-
今回は言語は「Node.js」、コードはインラインで作成せず、ローカルで作成して、ZIPファイルでアップしました。
また、Lambda用のロールの作成がちょっとてこずりました。そこについては後述しますが、今は一旦、「lambda_s3_exec_role」で作成してください。
-
ここまできたら、後はLambdaで動かすコードを作成しましょう!下記のコードについては、、NodeJSが初めてのため、ソースが初心者コードになってます。。ご指摘、改善内容提案などいただけると嬉しいです!
console.log('Loading event');
var prodaws = require('aws-sdk');
var s3 = new prodaws.S3({apiVersion: '2006-03-01'});
var request = require('request');
var async = require('async');
var auth = "Basic " + new Buffer("XXXXXX:XXXXXX").toString("base64");
exports.handler = function(event, context) {
// *************************************************************************
// * Lambdaをno VPC以外で動かす場合には必要になりそう
// * VPC指定だと、privateサブネット経由だと、NATインスタンスのGIPになるっぽい
// *************************************************************************
/*
var exec = require('child_process').exec;
var cmd = "curl inet-ip.info";
var child = exec(cmd, function(error, stdout, stderr) {
if (!error) {
// IP => CIDR
var ipAddress = stdout.replace(/[\n\r]/g,"") + "/32";
console.log('Lambda Global Ip: ' + ipAddress);
var params = {
GroupId: 'sg-XXXXXX',
IpPermissions: [
{
FromPort: 80,
ToPort: 80,
IpProtocol: 'tcp',
IpRanges: [
{
CidrIp: ipAddress
},
]
},
],
};
ec2.authorizeSecurityGroupIngress(params, function(err, data) {
if (err){
console.log(err);
} else {
console.log('Success Add Lambda Global Ip to jenkins security group! ');
}
});
} else {
console.log("error code: " + error.code + ", err: " + error);
context.done(error,'lambda');
}
});
*/
console.log('Received event:');
console.log(JSON.stringify(event, null, ' '));
// Get the object from the event and show its content type
var bucket = event.Records[0].s3.bucket.name;
console.log(bucket);
var key = event.Records[0].s3.object.key;
console.log(key);
console.log(event.Records[0].s3.object);
console.log('Sending hipchat:');
async.waterfall([
function download(callback){
s3.getObject({Bucket:bucket, Key:key},
function(err,data) {
if (err) {
console.log('error getting object ' + key + ' from bucket ' + bucket +
'. Make sure they exist and your bucket is in the same region as this function.');
context.done('error','error getting file'+err);
}
else {
console.log('CONTENT TYPE:',data.ContentType);
callback(null, 1);
}
}
);
},
function postjenkins(arg1, callback){
console.log('Start kick jenkins jobs:');
request.post({
url : 'http://jenkins.example.com/job/test-echo-job/build',
headers: { 'Authorization': auth }, // Basic認証が必要な場合は入れます。
form: { }
},
function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body);
}
if (error) {
console.log(error);
}
}
);
console.log('End kick jenkins jobs:');
callback(null, 2);
},
function posthipchat(arg1, callback){
console.log('Start Sending hipchat:');
request.post(
'https://api.hipchat.com/v1/rooms/message?format=json&auth_token=XXXXXXXXXXX',
{ form: { 'room_id': 'XXXXXXX' , 'from': 'Lambda Script' , 'message': 'おら Lambda.よろしくね。 バッチを起動します' , 'message_format': 'text' } },
function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body);
}
}
);
console.log('End Sending hipchat:');
callback(null, 'done');
}
],
function(err){
if(err){ console.log('Error:' + err); }
console.log('all done....');
context.done(null,'process all done....');
}
);
};
- ひたすらLambdaの画面にて、アップロード、テスト、アップロード、テスト・・・・・
- 動いたら、こんな感じでHipchatで反応があります!
- ここまで動かせたら、後は、S3のイベントでフックして実行されるように設定をするだけです。送信先を作成したLambda関数にして、イベントは「ObjectCreated」でいけます!
注意点
-
今回は、LambdaがAWSのリソースへのアクセスとして、S3とVPCへアクセスをするため、双方がアクセス可能なロールを作成しています。それが、下記。このロールが必要なところに少しハマりました。。。
-
また、上記のコード上でもコメントで記載してますが、今回はVPC内のNATインスタンス経由でJekinsやHipchatへアクセスすることで、Jenkinsのセキュリティグループへのアクセス許可を固定化できましたが、「No VPC」で利用する場合は、Lambdaが実行された際に、GlobalIPがわからないため、GlobalIPを取得し、さらにJenkinsのセキュリティグループでそれを許可させるという処理が必要になります!これもかなり悩みましたが、VPCへのアクセスにすることで、NATのGlobalIPになることがわかり、なんとか解決しました。