webpackとApexを使ったLambda開発 – ClassmethodサーバーレスAdvent Calendar 2017 #serverless #adventcalendar
背景
Lambda Functionでサーバーレスアプリケーションを作ったり、公式のAlexa Skills Kit SDKを使ってコードを書く機会が増えています。
ある程度の規模や複雑さを超えると、以下のような問題に直面するでしょう。
- FlowやBabelなどの開発ツールを使いたい
- npmを使って独自の機能を使おうとすると、Lambda Functionにアプロードするzipファイル容量が大きくなるので、抑制したい
このような問題は、webpackを使うことで解決できます。
今回は、有名なLambda FunctionデプロイツールApexと、webpackを併用する方法をご紹介します。
本文
前提
Lambdaに合わせたNode.js(v6.10.3)と、npmまたはyarnが利用可能であることを前提とします。
今回のファイルは以下のようなディレクトリ構成です。
. ├── .apexignore ├── .eslintrc ├── .gitignore ├── README.md ├── functions │ └── entrypoint │ └── src │ └── index.js <== Lambda Functionのエントリポイント ├── package.json ├── project.json ├── webpack.config.js └── yarn.lock
package.json
webpackはdevDependenciesに加えます。
以下のpackage.json
にあるとおり、今回はeslintの設定も加えてみます。また、実行時エラーでスタックトレースを追えるように、source mapも追加してみます。
{ "private": true, "license": "UNLICENSED", "engines": { "node": "6.10.3" }, "scripts": { "lint": "eslint functions/entrypoint/" }, "dependencies": { "source-map-support": "^0.5.0" }, "devDependencies": { "eslint": "^4.3.0", "eslint-loader": "^1.9.0", "eslint-plugin-node": "^5.1.1", "webpack": "^3.3.0" } }
webpack.config.js
webpack.config.js は、webpackの動作オプションを設定するファイルです。各設定項目の詳細は公式ドキュメントをご覧ください。
const path = require('path'); const Webpack = require('webpack'); exports.default = { entry: './src/index.js', target: 'node', output: { path: path.join(process.cwd(), 'build'), filename: 'index.js', libraryTarget: 'commonjs2' }, externals: ['aws-sdk'], module: { rules: [ { test: /\.js$/, enforce: 'pre', loader: 'eslint-loader', options: { fix: true, failOnError: true, failOnWarning: true, emitError: true }, exclude: [/node_modules/] } ] }, plugins: [ new Webpack.NoEmitOnErrorsPlugin(), new Webpack.DefinePlugin({ 'process.env': { NODE_ENV: JSON.stringify('production') // 環境変数はwebpackで展開されるので、ここで設定します } }), new Webpack.LoaderOptionsPlugin({ minimize: true, debug: false }) ], devtool: 'inline-source-map', bail: true };
Apex設定ファイル
デプロイツールApexの設定ファイルです。詳細は公式ドキュメントをご覧ください。
project.json
{ "name": "myapp", "description": "", "memory": 128, "timeout": 10, "role": "arn:aws:iam::01234567890123:role/lambda_basic_role", "runtime": "nodejs6.10", "handler": "build.handler", "hooks": { "build": "../../node_modules/.bin/webpack --config ../../webpack.config.js", "clean": "rm -rf build" }, "environment": { } }
.apexignore
apexで作成するzipファイルには、webpackを通したファイルのみを含めるので、ソースコードそのものは除外します。
src/
index.js
'use strict'; require('source-map-support').install(); exports.handler = function(event, context, callback) { // do something };
apex deploy
時の動作
apex deploy
コマンドを実行する際、project.json
のhooks.buildで設定したフックが実行されます。今回の場合、「index.jsがあるビルドフック実行時のカレントディレクトリから2つ上の/node_modules/.bin/webpack を実行する」となります。
webpack実行時に指定した設定ファイルのoutputにもとづき、 functions/entrypoint/build/index.js
がビルド結果としてwebpackにより出力されます。
これでビルドフックが終了したので、apexはzipファイルを作成してAWSにアップロードし、バージョンとエイリアスの処理を行います。
その後、cleanフックで指定したとおり、webpackの出力用ディレクトリ(build/)を削除します。
まとめ
私の経験ですが、webpackを使うことで、zipファイルのサイズが10MBから500KB程度に削減され、開発時の快適な動作確認環境を作ることができました。
webpackの設定によっては、FlowTypeやBabelなどのトランスパイラを使用して、複雑なFunctionを作ることができます。
参考: https://github.com/apex/apex/tree/master/_examples/babel-webpack2