🐋

Serverless OfflineがOOMで落ちるので、dynamo-localをDockerにする

2021/11/11に公開

serverless frameworkをローカルの環境で動作させるにはserverleff-offlineのプラグインを利用します。
serverless-offlineのプラグインは使用していると、メモリを使い切ってOOMで落ちてしまうという残念な特徴があります。Githubのissueにも上がっています。
https://github.com/dherault/serverless-offline/issues/539

基本的にserverlessはステートレスの処理なので、落ちても問題がないことがほとんどですが、1点だけ重要になる点があります。
永続層への書き込み、今回の場合はDynamoDBへの書き込みです。
serverless frameworkのプラグインとして、serverless-dynamodb-localがあり、これを利用することで、dynamoDBをローカルで起動することができます。
https://www.npmjs.com/package/serverless-dynamodb-local

serverless frameworkのプラグインとして動かすということは、serverless frameworkの子プロセスとして起動するということ。つまり、serverless frameworkが上述のOOMで落ちてしまうと、子プロセスも一緒に終了してしまいます。
そのため、テストのためのデータをDynamoDBに書き込みしている途中に落ちてしまって、何度も同じ処理をやり直すということが発生していました。

serverless frameworkが停止しても、データを保持しておく方法は2点あります。dynamodb-localのデータをファイルに保存する(基本はメモリに保存している)か、dynamodb-locaを別のプロセスとして起動するかです。
データをファイルに保存するのは直感的に反する感じがしたので、別のプロセスとして起動する方法を検討しました。

起動などに手間をかけたくないので、dynamodb-localはdockerで起動できるようにします。
serverless-dynamodb-localでは内部的にamazonが提供しているjarファイルを実行しています。
dynamodb-localはありがたいことにdocker hubにコンテナが登録されています。
https://hub.docker.com/r/amazon/dynamodb-local

説明を見るとオプションは特に必要なくportの指定だけで動きそうに見えます。

docker run -p 8000:8000 amazon/dynamodb-local

serverless-dynamodb-localのオプションにnoStart: trueを入れてserverless offlineを起動します。

  dynamodb:
    start:
      port: 8000
+      noStart: true
      inMemory: true
      migrate: true
      seed: true

ちゃんと起動しているように見えるのですが、serverless offlineとdynamodbがつながっていないように見えます。

そこで、調べてみたところ以下のissueが出てきました。
https://github.com/99x/serverless-dynamodb-local/issues/57

デフォルトのdockerではオプションが足りないため起動しないとのこと。そのため、issueにあるものをそのまま利用して、docker-composeを使ってdynamodb-localを起動したところ、意図した通りの挙動になりました。

 dynamodb:
    image: 'amazon/dynamodb-local'
    ports:
      - '8000:8000'
    command: ["-jar", "DynamoDBLocal.jar", "-sharedDb", "-inMemory"]

dynamodb-localもよくよく見てみると、デフォルトでsharedDbのオプションがtrueになっていました。
https://github.com/99x/serverless-dynamodb-local/blob/v1/index.js#L248

index.js
            const config = this.service.custom && this.service.custom.dynamodb || {};
            const options = _.merge({
                    sharedDb: this.options.sharedDb || true,
                    install_path: this.options.localPath
                },
                config && config.start,
                this.options
            );

sharedDbのオプションが必要な理由は公式ドキュメントなどに記載があったので参考にしてみてください。
https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/DynamoDBLocal.UsageNotes.html
https://qiita.com/tamo_breaker/items/f86c9b5fc6991813e3ca

Discussion