AWSLambdaの私的Tips

今回はSQSLoggerとその周辺のお話ということで、AWS LambdaについてのTipsを書こうかと思います。

※SQSLoggerは私が以前作成したLaravelフレームワーク上で動くログデータをSQSへプッシュするライブラリです。詳しくはこちらの記事をご覧ください。

www.wantedly.com

AWS Lambdaとは?

aws.amazon.com

AWS Lambdaは、サーバレスでコードを実行できるAWSのサービスです。実行できるコードは現在Node.js, Python2, Java8に限られていますが、特に環境の設定をせず気軽にコードが実行できる点が魅力です。

実行タイミングは、cronのような時間に基づいた定期実行もできますが、AWSの他のサービスが発したイベントをListenして実行することもできます!実行タイミングの指定はWebUI上ででき、手軽です。

料金体系は使用した時間に対する課金で、無料利用枠も十分にあるので「定期的にコードを実行したいけどEC2のインスタンス借りるのはちょっと…」っていう人にもおすすめです。

本題: Node.jsでLambda Functionを書くときのTIPS

自分がLambdaを使うときはjsを使うのですが、以前はLambdaに関する資料や実装例があまりなく、結構苦労しました。そこで、自分がLambdaに触る上で手に入れたTIPSを書き残しておこうと思います。ちなみに筆者の環境はUbuntu15.10ですが、Macならおおよそ流用できるかと思います。

1. aws-sdkはいらない!

Node.jsを用いて書く場合、npmライブラリを使う場合はindex.jsとnode_modulesディレクトリをzipしてアップロードする必要があります。しかし、もし使うライブラリがaws-sdkのみである場合は、必要ありません。

これは、LambdaFunctionが実行されるインスタンス上で、requireできるパス上にすでにaws-sdkが存在するためです。こうすることでデプロイの時間を少し短縮することができます。

2. zipするときはzip -r project.zip index.js node_modules

とはいえ、npmライブラリがどうしても使いたい時があるとおもいます。そういう時はindex.jsとnode_modulesの入ったprojectというディレクトリを以下のようにzipしたくなりますが… $ zip -r project.zip project このzipファイルをアップロードするとLambdaに'index.jsが見つからない'と怒られてしまいます。これはLambdaの仕様で、展開したディレクトリ上に直にindex.jsがないといけません。よって正しいコマンドは

$ cd project
$ zip -r project.zip index.js node_modules

になります。地味にハマるポイントではないでしょうか。

3. Nodeのバージョンは合わせておこう!

自分ではあまりはまったことがないのですが、普段node 6.x系使っているともしかしたら動かない可能性があるかもしれません。(とくにnaitive extensionを使っている場合とか)Lambdaの実行環境は2016/10/13現在では4.3なので、ローカルの環境を確認しておきましょう。

Nodeのバージョン管理といえば、rbenvやpyenvと同様にnodenvがありますが、個人的にはnpmでインストールできて気軽に使えるnがおすすめです。

$ npm install -g n
$ n 4.3.0

これだけで終わり!(Linux環境等では適宜sudoしてinstallしてください)

4. ハンドラに渡される引数を有効に使おう!

Lambda関数を書く時、雛形ファイルとして与えられる書式には、関数の引数として3つの変数が用いられています。

exports.handler = (event, context, callback) => {
    .
    .
    .
};

これらの引数はそれぞれ実行にあたって有効な情報を与えてくれます。

eventは言わずもがな、Lambda関数を発火したイベントの詳細情報が入ってきます。たとえば、S3上のファイル作成がトリガーになったときには、作成されたファイルのkeyやbucketを始めとした様々な情報が入ってきます。

  "Records": [
    {
      "eventVersion": "2.0",
      "eventTime": "1970-01-01T00:00:00.000Z",
      "requestParameters": {
        "sourceIPAddress": "127.0.0.1"
      },
      "s3": {
        "configurationId": "testConfigRule",
        "object": {
          "eTag": "0123456789abcdef0123456789abcdef",
          "sequencer": "0A1B2C3D4E5F678901",
          "key": "HappyFace.jpg",
          "size": 1024
        },
        "bucket": {
          "arn": bucketarn,
          "name": "sourcebucket",
          "ownerIdentity": {
            "principalId": "EXAMPLE"
          }
        },
        "s3SchemaVersion": "1.0"
      },
      "responseElements": {
        "x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH",
        "x-amz-request-id": "EXAMPLE123456789"
      },
      "awsRegion": "us-east-1",
      "eventName": "ObjectCreated:Put",
      "userIdentity": {
        "principalId": "EXAMPLE"
      },
      "eventSource": "aws:s3"
    }
  ]
}

contextにはさまざまな実行時情報が入ってきます。特によく使うのはgetRemainingTimeInMillisで、呼び出した段階での残りコンピュート時間をミリ秒で返してくれます。これをつかえば、残り時間が5秒をきったら処理をやめる、というようなことができます。なにかをループするような場合には以下のようにwhileの条件として使います。

while (context.getRemainingTimeInMillis() > 5000) {
    hogehoge();
    .
    .
    .
}

callbackは最近Lambdaのnodeのバージョンが上がって初めて追加されたもので、関数の実行結果を呼び出し元に返すのに用います。第一引数には関数が失敗した時の実行結果を表し、第二引数は正常終了した場合に用います。

callback(Error error, Object result);

使用は任意なので無理に使うことはないですが、AWS上でデバッグする必要があるときは重宝します。

まとめ

以上、Lambdaを使う上で気になるTIPSをいくつか書かせていただきました。LambdaはEC2やRDSなどの基幹サービスに比べれば迫力は落ちますが、応用できる幅が非常に広いサービスです!イベントが発生した時にSlack等になにか軽いNotificationを送りたい場合や、同じくAWSのサービスであるAPI Gatewayを使ってステートレスなWebAPIを作成できる(!!)など、既にたくさんの応用例があります!

もしあなたがなにかサーバー上でちょっとしたことをやることになったら、まずはLambda上でできるかを考えてみませんか?

私達の会社では、AWS上の様々サービスを使ってWebアプリケーションをより効率的かつスケーラブルに実装することが今ホットな話題の一つになっています。