Serverless Framework+TypeScriptのプロジェクトでnode_modulesをAWS Lambda Layers化しよう

Serverless Framework+TypeScriptのプロジェクトで、serverless-layersプラグインを使ってみました。簡単に依存モジュールをLambda Layersとしてデプロイできます。
2019.10.03

こんにちは。サービスグループの武田です。

先日Serverless Framework+JavaScript環境に、serverless-layersプラグインを導入し、お手軽にnode_modulesをLambda Layers化しました。

Serverless Frameworkのserverless-layersプラグインを使って超お手軽にnode_modulesをAWS Lambda Layers化する

今回は同様のことを、TypeScript(+webpack)の環境でもやってみました。

検証環境

次のような環境で検証しています。

$ sw_vers
ProductName:	Mac OS X
ProductVersion:	10.14.6
BuildVersion:	18G103

$ node -v
v10.16.3

$ npm ls serverless serverless-layers
example-serverless-ts@1.0.0 /path/to/example-serverless-ts
├── serverless@1.53.0
└── serverless-layers@1.4.2

Lambdaのランタイムはnodejs10.xです。またAWSにデプロイするためのIAMなどは用意されていることとします。

やってみた

プロジェクト作成

さっそくプロジェクトを作っていきます。テンプレートにaws-nodejs-typescriptを指定すると、TypeScriptが使える環境で作成されます。

$ npx -p serverless sls create -t aws-nodejs-typescript -p example-serverless-ts
$ cd $_ && npm install && npm install -D serverless
$ npx sls invoke local -f hello
{
    "statusCode": 200,
    "body": "{\n  \"message\": \"Go Serverless Webpack (Typescript) v1.0! Your function executed successfully!\",\n  \"input\": \"\"\n}"
}

またデプロイ先を東京リージョンにするため、provider.regionを追記しておきます。

serverless.yml

provider:
  name: aws
  runtime: nodejs10.x
  region: ap-northeast-1

次にmomentとunderscore.stringをインストールし、モジュールを使うようにhandler.jsを少し改造します。

$ npm install moment underscore.string

handler.ts

import { APIGatewayProxyHandler } from 'aws-lambda';
import * as moment from 'moment';
import { prune } from 'underscore.string';
import 'source-map-support/register';

export const hello: APIGatewayProxyHandler = async (event, _context) => {
  return {
    statusCode: 200,
    body: JSON.stringify({
      message: 'Go Serverless Webpack (Typescript) v1.0! Your function executed successfully!' + prune(moment().format(), 12),
      input: event,
    }, null, 2),
  };
}

確認のため、もう一度ローカル実行してみます。

$ npx sls invoke local -f hello
{
    "statusCode": 200,
    "body": "{\n  \"message\": \"Go Serverless Webpack (Typescript) v1.0! Your function executed successfully!2019-10-02T...\",\n  \"input\": \"\"\n}"
}

問題なく実行できました。

Layer化せずにデプロイ

Layer化した後とのサイズが比較できるように、一度そのままデプロイしてみます。

$ npx sls deploy

...
Serverless: Uploading service example-serverless-ts.zip file to S3 (366.21 KB)...

$ npx sls invoke -f hello
{
    "statusCode": 200,
    "body": "{\n  \"message\": \"Go Serverless Webpack (Typescript) v1.0! Your function executed successfully!2019-10-02T...\",\n  \"input\": {}\n}"
}

366.21 KB というサイズでした。実行も問題なくできています。マネジメントコンソールにアクセスしてデプロイされたファイルを確認してみたところ、次のようになっていました。

serverless-layersを導入してデプロイ

続いてserverless-layersを導入していきます。前回とほぼ同じ手順ですが、今回はwebpackのビルド対象からnode_modulesを除外する必要があります。

  1. Lambda Layersデプロイ用のS3バケットを用意
  2. serverless-layersプラグインをインストール
  3. 設定ファイルに設定を追加
  4. webpackの設定変更

1の手順は省略します。ここではバケット名をlambda-layers-deploy-123456789012としておきます。

次にsls plugin installでプラグインをインストールします。

$ npx sls plugin install --name serverless-layers

先ほど作成したバケットを設定ファイルに追加します。

serverless.yml

custom:
  serverless-layers:
    layersDeploymentBucket: lambda-layers-deploy-123456789012

node_modulesをwebpackのビルド対象から除外します。

$ npm install -D webpack-node-externals

webpack.config.js

const path = require('path');
const slsw = require('serverless-webpack');
const nodeExternals = require('webpack-node-externals');

module.exports = {
  mode: slsw.lib.webpack.isLocal ? 'development' : 'production',
  entry: slsw.lib.entries,
  devtool: 'source-map',
  resolve: {
    extensions: ['.js', '.jsx', '.json', '.ts', '.tsx'],
  },
  output: {
    libraryTarget: 'commonjs',
    path: path.join(__dirname, '.webpack'),
    filename: '[name].js',
  },
  target: 'node',
  module: {
    rules: [
      // all files with a `.ts` or `.tsx` extension will be handled by `ts-loader`
      { test: /\.tsx?$/, loader: 'ts-loader' },
    ],
  },
  externals: [nodeExternals()],
};

以上で必要な変更は完了です。デプロイしてみましょう。

$ npx sls deploy
Serverless: [LayersPlugin]: Downloading package.json from bucket...
Serverless: [LayersPlugin]: package.json does not exists at bucket...
Serverless: [LayersPlugin]: Dependencies has changed! Re-installing...

...
Serverless: Uploading service example-serverless-ts.zip file to S3 (3.25 KB)...

$ npx sls invoke -f hello
{
    "statusCode": 200,
    "body": "{\n  \"message\": \"Go Serverless Webpack (Typescript) v1.0! Your function executed successfully!2019-10-02T...\",\n  \"input\": {}\n}"
}

Lambda本体は 3.25 KB とかなり軽量化されました。マネジメントコンソールでファイルを確認すると、きちんと依存モジュールが除外されていることも確認できました。

まとめ

Serverless Framework+TypeScriptの環境でserverless-layersを試してみました。webpackの設定変更が若干見落としがちになりそうです(というか見落としてました)。それではよいServerless生活を!