読者です 読者をやめる 読者になる 読者になる

scramble cadenza

技術ネタのガラクタ置き場

serverless + webpack + babel で AWS Lambda をナウく書く

aws AWS Lambda javascript

イントロ

最近 AWS Lamdba を仕事で触っています。
serverless やら Apex なり色々あるわけですが、沢山ありすぎてよくわかりません。

今までは Apex の手軽さに甘えていましたが、そろそろ serverless が本気を出してきたという噂を聞き、serverless を使い始めています。

どうせ serverless を使うなら、もう全部使い倒してやろうということで、babel も意識高く使っていきたいわけです。

この記事では serverless + babel を使うところまでをまとめました。

環境

  • serverless@1.1.0
  • serverless-webpack@1.0.0-rc.2
  • webpack@1.13.3

serverless

まずは雛形作成

# template 指定は必須
# `sls` は `serverless` コマンドの alias 
$ sls create -t aws-nodejs

serverless.yml というファイルが設定ファイル。
これを早速いじっていく。コメントや公式のドキュメントがわかりやすい。

# serverless.yml を編集していく
$ emacs serverless.yml
$ git diff serverless.yml
 provider:
   name: aws
   runtime: nodejs4.3
+  region: ${env:AWS_REGION}
 functions:
-  hello:
-    handler: handler.hello
+  imageResizer:
+    handler: index.handle
# service の deploy
$ sls deploy

# AWS Lambda 関数の deploy
$ sls deploy function -f imageResizer

# AWS Lambda 関数の実行
$ sls invoke -f imageResizer

# Log の tail
$ sls logs -f imageResizer -t

まず最初に sls deploy で Service の deploy を行う。
この Service というのは serverless 用語の一つで、AWS Lambda を実行するための AWS インフラ全体を指す名称。

この Service を管理するために serverless は CloudFormation を暗黙的に使用している。
うっかり region 指定を忘れると、sls deploy を実行した時、serverless デフォルトの region である us-east-1 に、 CloudFormation の Stack が出来上がってしまうので要注意。

region はコマンドラインの option で渡すか serverless.ymlprovider で指定する。

serverless-webpack

serverless を使って deploy はできるようになった。後は書いたコードを babel を使っていく。
serverless-webpack プラグインがあって、これを使えば OK。

elastic-coders/serverless-webpack: Serverless plugin to bundle your lambdas with Webpack

外部の npm パッケージを使う場合

serverless-webpack を使用している場合、任意の package を handler の中で require しようとすると、 webpack コマンドで失敗してしまう。

$ git diff
--- a/index.js
+++ b/index.js
@@ -1,5 +1,6 @@
 'use strict';

+import gm from 'gm';

# sls コマンドは serverless コマンドの alias
$ sls webpack —out .webpack
...
ERROR in ./~/thread-sleep/~/node-pre-gyp/package.json
Module parse failed: /Users/argerich/dev/serverless-image-resizer/node_modules/thread-sleep/node_modules/node-pre-gyp/package.json Unexpected token (2:8)
You may need an appropriate loader to handle this file type.
SyntaxError: Unexpected token (2:8)
    at Parser.pp$4.raise (/Users/argerich/dev/serverless-image-resizer/node_modules/acorn/dist/acorn.js:2221:15)

この場合、webpack-node-externals を使って、設定ファイルを調整する必要がある。
具体的には以下のような修正をする。

# webpack.config.js
+const nodeExternals = require('webpack-node-externals');
+
 module.exports = {
   entry: './index.js',
   target: 'node',
@@ -5,5 +7,6 @@ module.exports = {
     libraryTarget: 'commonjs',
     path: '.webpack',
     filename: 'index.js'
-  }
+  },
+  externals: [nodeExternals()]
 };
# serverless.yml

 custom:
   webpack: webpack.config.js
+  webpackIncludeModules: true

webpack を通した deploy は serverless deploy function -f では行われない。
serverless deploy で行う。

$ sls deploy

Serverless: Deprecation Notice: Starting with the next update, we will drop support for Lambda to implicitly create LogGroups. Please remove your log groups and set "provider.cfLogs: true", for CloudFormation to explicitly create them for you.
Serverless: Bundling with Webpack...
Time: 578ms
   Asset     Size  Chunks             Chunk Names
index.js  2.17 kB       0  [emitted]  main
Serverless: Packing external modules: babel-polyfill@^6.16.0, gm@^1.23.0
Serverless: Packaging service…
Serverless: Uploading CloudFormation file to S3…
Serverless: Uploading service .zip file to S3…
Serverless: Updating Stack…
Serverless: Checking Stack update progress…
.....
Serverless: Stack update finished…
Serverless: Removing old service versions…

Service Information
service: serverless-image-resizer
stage: dev
region: ap-northeast-1
api keys:
  None
endpoints:
  None
functions:
  serverless-image-resizer-dev-imageResizer: arn:aws:lambda:ap-northeast-1:xxxxxx:function:serverless-image-resizer-dev-imageResizer

✨  Done in 27.52s.

まとめ

  • region のデフォルトは us-east-1 なので、指定を忘れないこと。
    • serverless.yml に region を書いておく(ドキュメントの隅っこ にサラッと書いてある)
  • serverless-webpack で外部 package 使う場合は、webpack-node-externals を使う
  • deploy は serverless deploy function ではなく、serverless deploy を使う

という話でした。

Apex と Serverless 両方使ってみての感想

個人的には Apex のほうが学習コスト少なくて、機能もシンプルなので好き。

Serverless はもれなく CloudFormation が付いてきて、管理運用の面で色々思うところがある。AWS Lamda だけ触りたいんや! と思ってる人にとって、いきなり CloudFormation が出てくるとびっくりする。

けどその分 serverless は設定ファイルを柔軟に書けるし、プラグインは充実している。Apex で微妙だと思った環境変数周りを、うまくカバーしているところは好印象。

一方で Apex でしか出来ないことは Terraform との連携だけ。この機能にどれだけ期待するかが、一つの採用基準でもあると思う。

しかし、ただ単に AWS Lambda を使いたい、というだけであれば Apex で十分だし、落とし穴は少ない。 Apex で不満に思うことが増えてきたら serverless を検討する、で良いのかな。

と、当たり前の結論に至りました。

参考になる

おまけ

$ sls webpack —out .

とやると、現在のディレクトリが全て消されて途方にくれるので、 良い子のみんなは絶対に真似をしないでください