AWS LambdaファンクションをGulpで実行
以下のブログエントリで、GulpでAWS AWS Lambdaファンクションをデプロイする手順をご紹介しました。
せっかくなので、このブログエントリで作成したGulpfileにタスクを追加して、AWS Lambdaファンクションの実行もGulpからやってみたいと思います。
Gulpプラグイン?
上記のブログエントリでAWS LambdaファンクションをデプロイするためのGulpプラグインを3つほどご紹介しましたが、いずれもAWS Lambdaファンクションを実行するタスクは含まれていません。
そもそもGulpでAWS Lambdaファンクションを実行したいというモチベーションが無いということかもしれませんが、めげずに自前でタスクを作成したいと思います。
AWS SDK for JavaScript in Node.jsのClass: AWS.Lambdaにinovokeというメソッドが用意されているので、これを使いたいと思います。
タスクの実装、その前に
S3バケットの作成
AWS LambdaファンクションをGulpでデプロイ で作成したAWS Lambdaファンクションは「S3バケットに画像ファイルをアップロードすると、そのサムネイルが別のバケットに保存される」という内容のものでした。
実際のこのAWS Lambdaファンクションを実行するにあたり、事前にS3バケットを2つ作成して、一方のバケットにサムネイルの元となる画像をアップロードしておきます。
- 元画像アップロード用バケット : yy.sourcebucket
- サムネイル画像保存用バケット : yy.sourcebucketresized
- 元画像ファイル : lambda-test.jpg
「元画像アップロード用のバケット」と「元画像ファイル」の名前はイベントデータとしてAWS Lambdaファンクションに渡されるので、任意の名前で作成して構いません。
一方で「サムネイル画像保存用バケット」の名前はAWS Lambdaファンクション内で指定する形となります。今回実行するAWS Lambdaファンクションはサムネイル画像を「<元画像アップロード用バケット> + resized」という名前のバケットに保存する作りとなっているので、このネーミングルールに従ってバケットを作成します。
参考までに、AWS CLIでバケットを作成する手順を記載しておきます。
# バケット作成 # (--profileで指定している"lambda-test"は、region = us-west-2"を定義したプロファイルを指定) $ aws s3 mb s3://yy.sourcebucket --profile lambda-test $ aws s3 mb s3://yy.sourcebucketresized --profile lambda-test # ファイルアップロード $ aws s3 cp lambda-test.jpg s3://yy.sourcebucket --profile lambda-test # ファイル確認 $ aws s3 ls s3://yy.sourcebucket --profile lambda-test
イベントデータの準備
今回実行するAWS Lambdaファンクションですが、本来は、、
- S3にファイルをアップロード(S3イベントが発生)
- イベントデータ(ファイルがアップロードされたバケット名やファイル名)をインプットとしてAWS Lambdaファンクションが実行される
という動きになります。
今回のようにマニュアルでAWS Lambdaファンクションを実行する場合は、S3イベントは発生しません。ので、イベントデータもマニュアルでLambdaに渡してやる必要があります。
今回はイベントデータとして以下のようなjsonファイルを用意しました(ファイル名はpayload_s3.jsonとしました)。中身はAWS Lambda Walkthrough 2: Handling Amazon S3 Events Using the AWS CLI (Node.js)にあるサンプルほぼそのままで、バケット名と画像ファイル名だけ修正しています。
{ "Records":[ { "eventVersion":"2.0", "eventSource":"aws:s3", "awsRegion":"us-west-2", "eventTime":"1970-01-01T00:00:00.000Z", "eventName":"ObjectCreated:Put", "userIdentity":{ "principalId":"AIDAJDPLRKLG7UEXAMPLE" }, "requestParameters":{ "sourceIPAddress":"127.0.0.1" }, "responseElements":{ "x-amz-request-id":"C3D13FE58DE4C810", "x-amz-id-2":"FMyUVURIY8/IgAtTv8xRjskZQpcIZ9KG4V5Wp6S7S/JRWeUWerMUE5JgHvANOjpD" }, "s3":{ "s3SchemaVersion":"1.0", "configurationId":"testConfigRule", "bucket":{ "name":"yy.sourcebucket", "ownerIdentity":{ "principalId":"A3NL1KOZZKExample" }, "arn":"arn:aws:s3:::yy.sourcebucket" }, "object":{ "key":"lambda-test.jpg", "size":1024, "eTag":"d41d8cd98f00b204e9800998ecf8427e", "versionId":"096fKKXTRTtl3on89fVO.nfljtsv6qko" } } } ] }
npmモジュールの追加
aws-sdk-jsと、ログ出力用にgulp-utilを追加でインストールします。
$ npm install aws-sdk gulp-util --save-dev
タスクの実装
準備が整ったので、GulpfileにAWS Lambdaファンクションの実行タスクを追加します。
var gulp = require('gulp'); var zip = require('gulp-zip'); var del = require('del'); var install = require('gulp-install'); var runSequence = require('run-sequence'); var awsLambda = require('node-aws-lambda'); // (追加)aws-sdkとgulp-utilモジュールの読み込み var AWS = require('aws-sdk'); var gutil = require('gulp-util'); // (追加) // aws-sdk - Lambdaのservice interface objectを生成 // イベントデータ(jsonファイル)の読み込み // ここから var lambdaConf, lambda, payload; lambdaConf = require('./lambda-config.js'); AWS.config.credentials = new AWS.SharedIniFileCredentials(); AWS.config.update({ region: lambdaConf.region }); lambda = new AWS.Lambda(); payload = require('./payload_s3.json'); // ここまで gulp.task('clean', function(cb) { del(['./dist', './dist.zip'], cb); }); gulp.task('js', function() { return gulp.src('index.js') .pipe(gulp.dest('dist/')); }); gulp.task('node-mods', function() { return gulp.src('./package.json') .pipe(gulp.dest('dist/')) .pipe(install({production: true})); }); gulp.task('zip', function() { return gulp.src(['dist/**/*', '!dist/package.json']) .pipe(zip('dist.zip')) .pipe(gulp.dest('./')); }); gulp.task('upload', function(callback) { awsLambda.deploy('./dist.zip', lambdaConf, callback); }); // (追加)AWS Lambdaファンクションの実行タスク // ここから gulp.task('invoke', function() { var invoke_params; invoke_params = { FunctionName: lambdaConf.functionName, InvocationType: 'RequestResponse', LogType: 'Tail', Payload: JSON.stringify(payload) }; return lambda.invoke(invoke_params, function(err, data) { if (err) { gutil.log(err, err.stack); } else { if (data.FunctionError) { gutil.log("An error occurred while executing the Lambda function."); gutil.log("Error Type:", data.FunctionError); } else { gutil.log("The Lambda function was successfully executed."); } gutil.log("Status Code:", data.StatusCode); var resultLog = new Buffer(data.LogResult, 'base64'); gutil.log(resultLog.toString()); } }); }); // ここまで gulp.task('deploy', function(callback) { return runSequence( ['clean'], ['js', 'node-mods'], ['zip'], ['upload'], callback ); }); // (追加)デフォルトタスク(AWS Lambdaファンクションのデプロイ&実行) gulp.task('default', function() { return runSequence( ['deploy'], ['invoke'] ); });
追加したAWS Lambdaファンクションの実行タスクですが、SDKを使ってLambdaのinovokeメソッドを呼んでいるだけです。
InvocationType: 'RequestResponse'
でAWS Lambdaファンクションを同期実行LogType: 'Tail'
でAWS Lambdaファンクションの最新の実行ログ(4KB)を取得- ログはBase64でエンコードされているので、デコードしてコンソールに表示
といった感じです。詳しくはAPIドキュメントを参照頂くのが良いかと思います。
Gulpのデフォルトタスクとして、AWS Lambdaファンクションのデプロイ&実行タスクも追加してみました。
AWS Lambdaファンクションの実行
追加したタスクを実際に動かしてみます。
$ gulp invoke [00:45:44] Using gulpfile ~/lambda-gulp-sample/gulpfile.js [00:45:44] Starting 'invoke'... [00:45:44] Finished 'invoke' after 28 ms [00:45:47] The Lambda function was successfully executed. [00:45:47] Status Code: 200 [00:45:47] START RequestId: bd3e3856-1dac-11e5-bc61-cbaeb77d45fa 2015-06-28T15:45:45.946Z bd3e3856-1dac-11e5-bc61-cbaeb77d45fa Reading options from event: { Records: [ { eventVersion: '2.0', eventSource: 'aws:s3', awsRegion: 'us-west-2', eventTime: '1970-01-01T00:00:00.000Z', eventName: 'ObjectCreated:Put', userIdentity: { principalId: 'AIDAJDPLRKLG7UEXAMPLE' }, requestParameters: { sourceIPAddress: '127.0.0.1' }, responseElements: { 'x-amz-request-id': 'C3D13FE58DE4C810', 'x-amz-id-2': 'FMyUVURIY8/IgAtTv8xRjskZQpcIZ9KG4V5Wp6S7S/JRWeUWerMUE5JgHvANOjpD' }, s3: { s3SchemaVersion: '1.0', configurationId: 'testConfigRule', bucket: { name: 'yy.sourcebucket', ownerIdentity: { principalId: 'A3NL1KOZZKExample' }, arn: 'arn:aws:s3:::yy.sourcebucket' }, object: { key: 'lambda-test.jpg', size: 1024, eTag: 'd41d8cd98f00b204e9800998ecf8427e', versionId: '096fKKXTRTtl3on89fVO.nfljtsv6qko' } } } ] } 2015-06-28T15:45:47.844Z bd3e3856-1dac-11e5-bc61-cbaeb77d45fa Successfully resized yy.sourcebucket/lambda-test.jpg and uploaded to yy.sourcebucketresized/resized-lambda-test.jpg END RequestId: bd3e3856-1dac-11e5-bc61-cbaeb77d45fa REPORT RequestId: bd3e3856-1dac-11e5-bc61-cbaeb77d45fa Duration: 1934.13 ms Billed Duration: 2000 ms Memory Size: 128 MB Max Memory Used: 44 M
うまく動いたようです。S3のバケットも確認してみます。
$ aws s3 ls s3://yy.sourcebucketresized --profile lambda-test 2015-06-29 00:45:48 14824 resized-lambda-test.jpg
サムネイル画像保存用バケットに画像が追加されました!
デフォルトタスクも追加したので、単にgulp
と実行するとAWS Lambdaファンクションのデプロイと実行が、セットで行われます。
まとめ
GulpでAWS AWS Lambdaファンクションのデプロイと実行、お試しレベルの実装ではありますが参考になれば幸いです。
今回はローカルのZIPファイルをアップロードし実行する形を取りましたが、git pushするとリモートのリポジトリからAWS Lambdaファンクションがデプロイされる、みたいなフローの方がなんだか良さそうな気がします。それでは、また。