AWS LambdaファンクションをGulpで実行

AmazonLambda

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

以下のブログエントリで、GulpでAWS AWS Lambdaファンクションをデプロイする手順をご紹介しました。

AWS LambdaファンクションをGulpでデプロイ

せっかくなので、このブログエントリで作成したGulpfileにタスクを追加して、AWS Lambdaファンクションの実行もGulpからやってみたいと思います。

Gulpプラグイン?

上記のブログエントリでAWS LambdaファンクションをデプロイするためのGulpプラグインを3つほどご紹介しましたが、いずれもAWS Lambdaファンクションを実行するタスクは含まれていません

そもそもGulpでAWS Lambdaファンクションを実行したいというモチベーションが無いということかもしれませんが、めげずに自前でタスクを作成したいと思います。

AWS SDK for JavaScript in Node.jsClass: AWS.Lambdainovokeというメソッドが用意されているので、これを使いたいと思います。

タスクの実装、その前に

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ファンクションがデプロイされる、みたいなフローの方がなんだか良さそうな気がします。それでは、また。