🌑

Lambdaの250MB制限でハマりつくした(Node.js)

2022/01/14に公開

Unzipped size must be smaller than 262144000 bytes
って言われたことがありますか?私はあります。
Lambdaには上限緩和ができないハマりポイントとして250MB制限があります。この壁には何度かぶち当たっているのですが、Node.jsだとnode_modulesが大きすぎてひっかかることが多いと思います。Pythonだと機械学習系のライブラリを入れて死ぬとか。そんな時にハマったときの備忘録です。状況としては、NestJSを使ったプロジェクトで、400MBくらいのnode_modulesがありました。

Lambdaの制限について

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/gettingstarted-limits.html
こちらにあるように、

  • zip圧縮後50MB
  • zip圧縮前250MB
    である必要があります。AWS Layersを使う場合はLayersが単体で250MBまでと勘違いしていた時期もありましたが、どうやらLayersを使う時も解凍後の合計が250MBである必要があります。
    これを超過すると冒頭の

Unzipped size must be smaller than 262144000 bytes

が出ます。ちなみにLayersは5レイヤーまで作ることができます。

試したこと

  • 全部zipにまとめる
  • devDependenciesの切り分け
  • その他の手段
    • node-prune
    • いらないパッケージをrm -rfする
      beforeのserverless.yamlを記載します。
serverless.yaml
service: hogehoge
frameworkVersion: "2"

plugins:
  - serverless-layers

custom:
  defaultStage: development
  serverless-layers:
    layersDeploymentBucket: 適度なバケット名

provider:
  name: aws
  runtime: nodejs14.x
  lambdaHashingVersion: 20201221
  region: ap-northeast-1
  stage: ${opt:stage, self:custom.defaultStage}
  environment:
    STAGE: ${self:provider.stage}
  apiGateway:
    shouldStartNameWithService: true

package:
  individually: true
  include:
    - dist/**
  exclude:
    - "**"
  excludeDevDependencies: true

functions:
  index:
    handler: dist/handler.handler
    events:
      - http:
          path: /
          method: any
      - http:
          path: "{proxy+}"
          method: any

デプロイ手順は以下です

デプロイ手順
  1. npm install(普通にインストール)
  2. npm run build(dist/を作る)
  3. rm -rf node_modules
  4. npm install --production(devdependenciedを省きたい)
  5. sls deploy

全部zipにまとめる①serverless-layersをやめる

serverless-layersをやめるとなんとかなるという間違ったお気持ちになって試しました。もちろん失敗します。serverless.yamlからserverless-layersの部分を消して、packagesを以下のように書き換えます。

serverless.yaml
patterns:
    - "!**"
    - "node_modules/**"
    - dist/**

こうするとdistとnode_modulesがzipにまとまります。何度も言いますが意味がありませんでした。

全部zipにまとめる②手動

さらにハマって混乱した結果、node_modulesをzipに固めてマネジメントコンソールから手動でアップロードを試みました。もちろんこれもだめ。

devDependenciesの切り分け

package.jsonの依存モジュールにはdependenciesdevDependenciesがあります。これをうまく切り分けることで軽量化が可能です。切り分け方は

  • パッケージをググって-gでインストールするようなものはdependenciesにはいらない
  • aws-sdkの類はLambdaに乗っているのでdependenciesにはいらない
  • @typesはdependenciesにはいらない
  • swaggerの類やeslint、prettierもdependenciesにはいらない
  • ts-なんとかもdependenciesにはいらない(ts-node、ts-loaderとか)
  • typescript自体もdependenciesにはいらない

こんな感じでどんどんdevDependenciesに切り分けていきます。
du -sh ./node_modules/* | sort -nr | grep '\dM.*'
というコマンドで大きめのファイルを確認できます。また、
du -sh ./node_modules/でファイルの重さを確認できます。この時点で400MBから100MBちょっとまでpackage.jsonが痩せました。やったね。

ちなみに

https://qiita.com/NewGyu/items/73c62db781dc95eb1d64
上を参考にaws-sdkの中で使うものだけをimportしようと試みましたが、私の環境だとうまくいきませんでした。

その他の方法

https://github.com/tj/node-prune
node-pruneを使う。パッケージの中のいらないドキュメントなどを削除してくれます。数十MBくらい削減できそう。

もっと減らしたいときは各パッケージの中のdist以外を消すとか、明らかにいらない依存パッケージを目で見て消すとかはできます。

さらに、webpackの設定をいじってminifyするという方法もありそうです。

参考

https://logmi.jp/tech/articles/324858
https://tsh.io/blog/reduce-node-modules-for-better-performance/

追記
https://aws.amazon.com/jp/blogs/news/new-for-aws-lambda-container-image-support/
DockerImageからデプロイできるようになっているらしい。10GBまでいけるのでいろいろ乗るとか。
悩む必要は…なかったのでは…?

Discussion