こんにちは!
AWS Lambda の NodeJS 開発時に、Lambda にデプロイするソースコードのパッケージサイズの大きさに悩んだことがあるのではないでしょうか。
Lambda はパッケージサイズに比例してコールドスタートが大きくなり、API のレイテンシーに繋がります。
本記事では、Serverless Framework を使用した Lambda Layer の実装方法について解説します。
わたしたちは通常、TypeScript で AWS Lambda の NodeJS を実装しますが、その際 Webpack を使用し TypeScript を NodeJS にビルドしデプロイします。Lambdaへデプロイされるソースコードには、Webpack の実行により生成されたバンドルJSファイルと 一緒に node_modules も含まれ、結果 Lambda のソースコードサイズが肥大します。
しかし、node_modules のパッケージは全ての Lambda で汎用的なものだし、もちろんビジネスロジックでもありません。 node_modules は全ての Lambda 毎にデプロイする必要が本当にあるのでしょうか ?
叶うなら、以下のように共通のモジュール(node_modules)を共通の場所(Layer)に分離したいと考えるはずです。
本記事では、node_modules を全ての Lambda で共通利用出来る Lambda Layer にデプロイする方法について解説します。
Serverless Frameworkでは、Lambda Layer を簡単に実現するためのプラグインが提供されています。
具体的な実装方法は以下です。(細かいソースコードは省略)
serverless-layersをプロジェクトにインストールします。
$ yarn add serverless-layers -D
# or
$ serverless plugin install --name serverless-layers
serverless-layers プラグインで使用するバケットを生成します。バケットは CloudFormation で作成し GetAtt 等で ARN や名称を参照したいところですが、技術検証したところ自動生成される CloudFormation が原因で参照できませんでした。そのため、事前に AWS CLI でバケットを作成しておきます。
$ aws s3 mb "s3://my-bucket-name"
serverless.yml を以下のように設定します。
provider:
deploymentBucket:
name: '${self:custom.deploymentBucketName}'
plugins:
- serverless-layer
package:
individually: true
patterns:
- '!node_modules/**'
custom:
deploymentBucketName: 'layer-bucket-name'
serverless-layers:
packageManager: yarn
通常のデプロイコマンドを実行し、レイヤーをデプロイします。
$ sls deploy --stage=dev
これで Lambda Layer が自動的にデプロイ&適用されます。
Lambda のパッケージサイズは大幅に削減されて、数十 MB の Lambda のサイズが数百 KB 程度まで削減できたと思います!
APIGateway や AppSync からキックしている Lambda であれば、API 自体のレイテンシーが大幅に無くなりレスポンスが速くなったことでしょう。
serverless-layersプラグイン適用時は、以下のような順序でブラックボックスに Lambda Layer を適用します。
つまり、既存の Lambda のサイズが大きい状態(node_modulesを含む)で Layer 適用しようと試みる為、プロセス 3 の手前で Lambda の合計サイズが、既存 Lambda パッケージサイズ+Layer サイズ
となり、この合計サイズが250MBを超えるリミットエラーとなります。
Lambda関数は、Layer サイズ+Lambda サイズの合計は250MB以下になるようにデプロイする必要があります。
そのため、既存の Lambda へのデプロイについては、Layer の適用前に以下のいずれかの手順を踏む必要があります。
前述の通り、Lambdaのソースコードサイズは Lambda パッケージサイズ+Layer サイズ
となりますので、250MB対策にはなりません。他の方法を検討しましょう。
node_modules 以外のディレクトリがもし不要であれば、デプロイから確実に除外しておきましょう。除外する方法は以下です。
package:
individually: true
patterns:
- "!**/**"
# 必要なディレクトリーを配列に追加する
- "dist/**"
webpack を使用したプロジェクトであれば、individually を true にすることで必要なソースコードを一つのJSファイルにバンドル出来るため、例えば service 層や repository 層などのディレクトリも一式不要となりますね。
Lambda を使用したプロジェクトであれば、ほぼ必須の調整と言えます。NodeJS でなくても、例えば Python や Java でも同様の調整が可能です。
わたしたちは、ほぼすべての案件で本記事の調整を施します。
サーバーレスの開発相談はお気軽にご相談ください。
スモールスタート開発支援、サーバーレス・NoSQLのことなら
ラーゲイトまでご相談ください
低コスト、サーバーレスの
モダナイズ開発をご検討なら
下請け対応可能
Sler企業様からの依頼も歓迎