Edited at

LambdaのLayerをServerless Frameworkで


はじめに

本稿はオープンストリーム Advent Calendar 2018の14日目です。なんとなくで選んだ日程ですが図らずも昨年と同じ日になりました。

先日のre:Inventにて発表・実装されたAWS LambdaのLayer機能ですが、Serverless Frameworkが既に対応していたので早速試してみました。実行した環境はVirtualBoxにCentOS7を載せたものですが、Node.js(npm)が使えれば環境による違いはないと思われます。


導入

Node.jsのバージョンが6.5.0以降であることを確認

node --version

古いバージョンの場合はnvmなりyumなりでインストール

Serverless Frameworkのバージョンが1.34.0以降であることを確認

sls --version

古いバージョンの場合(未インストールの場合も同じコマンド)

npm i -g serverless

aws-nodejsテンプレートを用いてサービスを作成

sls create --template aws-nodejs --path my-service

AWSのクレデンシャルが設定されていることを確認

aws configure list


Layerの実装

上記の手順で単純なLambda関数をデプロイすることができる状態になりました。ここにLayerを追加していきます。


モジュールのソースコード

作成するモジュールは単純に引数を二乗する関数で、powという名前にします。

ドキュメント1によると、この場合下記のような配置になります。

.

├── handler.js
├── my-layer ← 任意の名称のディレクトリ(serverless.ymlに記述する)
│   └── nodejs
│   └── node_modules
│   └── pow
│   └── index.js
└── serverless.yml

my-layer/nodejs/node_modules/pow/index.js は特筆することはない普通の関数です。

module.exports = i => i * i;


設定ファイルの記述

serverless.ymlにLayerの定義を記述します。

service:

provider:
name: aws
runtime: nodejs8.10

functions:
hello:
handler: handler.hello
# ここから↓を追加
layers:
mylayer:
path: my-layer

Layer名を「my-layer」ではなく「mylayer」としている理由は後述します。


デプロイ

デプロイは従来通りの手順です。

sls deploy

Layerができていることを確認します。

aws lambda list-layers

{

"Layers": [
{
"LayerName": "mylayer",
"LayerArn": "arn:aws:lambda:ap-northeast-1:123456789012:layer:mylayer",
"LatestMatchingVersion": {
"LayerVersionArn": "arn:aws:lambda:ap-northeast-1:123456789012:layer:mylayer:1",
"Version": 1,
"CreatedDate": "2018-12-13T23:59:59.999+0000"
}
}
]
}


Layerを使う

この状態ではLayerを作ったもののどこからも呼び出されない孤独な存在です。

これからLambda関数にLayerを呼び出す処理を追加します。


設定ファイルの記述

関数の方の定義にlayersを追加します。

service:

provider:
name: aws
runtime: nodejs8.10

functions:
hello:
handler: handler.hello
# ここから
layers:
- {Ref: MylayerLambdaLayer}
# ここまで追加
layers:
mylayer:
path: my-layer

ここで「mylayer」とした理由が現れます。ドキュメント2には


The name of your layer in the CloudFormation template will be your layer name TitleCased (without spaces) and have LambdaLayer appended to the end.


とあるのですが、期待した変換が行われません。


  • my-layer → MyDashlayerLambdaLayer

  • my_layer → MyUnderscorelayerLambdaLayer

「MyLayer」とすれば「MyLayerLambdaLayer」となるので、好みやポリシーで命名規約を決めてよいと思います。


Lambda関数の変更

テンプレートから生成されたままのLambda関数(handler.js)をLayerで定義したモジュール(pow)を利用するように書き換えます。

'use strict';

const pow = require('pow');
module.exports.hello = async (event, context) => {
return pow(event.value);
};

REST APIにするつもりはないので普通に値を返すだけの関数にしました。


再びデプロイ

変更は以上です。再度デプロイします。手順は変わりません。

sls deploy

デプロイが終えたら、Lambda関数の定義を確認してみます。

aws lambda get-function-configuration --function-name my-service-dev-hello

Layersに作成したLayerが登録されていることが分かります。

{

"Layers": [
{
"CodeSize": 206,
"Arn": "arn:aws:lambda:ap-northeast-1:123456789012:layer:mylayer:2"
}
],
"FunctionName": "my-service-dev-hello",
"LastModified": "2018-12-13T23:59:59.999+0000",
"RevisionId": "00000000-1111-2222-3333-444444444444",
"MemorySize": 1024,
"Version": "$LATEST",
"Role": "arn:aws:iam::123456789012:role/my-service-dev-ap-northeast-1-lambdaRole",
"Timeout": 6,
"Runtime": "nodejs8.10",
"TracingConfig": {
"Mode": "PassThrough"
},
"CodeSha256": "XXXXXXXXXXXXXXXXXXXXX/YYYYYYYYYYYYYYYYYYYYY=",
"Description": "",
"VpcConfig": {
"SubnetIds": [],
"VpcId": "",
"SecurityGroupIds": []
},
"CodeSize": 387,
"FunctionArn": "arn:aws:lambda:ap-northeast-1:123456789012:function:my-service-dev-hello",
"Handler": "handler.hello"
}

LayerのARNの末尾の数字はバージョン番号で、デプロイの度にインクリメントされていきます。


実行

環境は整いました。Layerを利用するLambda関数を実行してみましょう。

sls invoke --function hello --data '{"value": 2}'

4

無事二乗した値が返ってきました。ちゃんとpowが使える状態になっています。


まとめ

最近はAWS SAMに押され気味の感がある3Serverless Frameworkですが、Layerのアーカイブ作成の手間が省けるなど、使いどころはあると思いました。何事も適材適所ですね。


おまけ

下記のようにpackage.jsonを配置したらdependenciesにあるモジュールを自動的にインストールした状態になってくれないかという淡い期待を持って試しましたが、ダメでした。

.

├── handler.js
├── my-layer
│   └── nodejs
│   ├── node_modules
│   │   └── pow
│   │   └── index.js
│   └── package.json
└── serverless.yml