ねぎ嫌い

思いついたことをてきとうに。

serverless framework x localstackでSNSトリガーなLambdaを動かす

動機

  • serverless frameworkを使ってSNSトリガーで動作する関数の実装をしていたがローカルで動作確認したくなった
  • serverless-offilne-snsを使っても良いのだけれど、リソースが増えるたびにプラグインを増やしたくなかった

環境

  • Mac OS 14.3
  • Docker v24.0.6
  • node v16.15.0
  • serverless framework v3.22.0
  • LocalStack v3.1 (PRO版未購入です... :bow:)

構成

  • LocalStackはdocker composeで別プロセスで管理し、serverless frameworkからLambdaをDeployする
  • IaCはTerraformを使っているのでSNS等のリソースはLocalStackの起動時スクリプトで生成し、serverless frameworkからは今回作らない

結論

以下のリポジトリで表現されている通り

https://github.com/anizozina/sls_localstack

多少の解説

LocalStackへのリソースの生成

前述した通り一部のリソースはTerraformで生成しているのでserverless framework経由で作らないようにしていた。
./localstack/init-aws.sh に記載の通り明示的にリソースを生成させている。

docker-compose.ymlのvolumesで指定している通り、 /etc/localstack/init/ 以下のパスに実行可能なスクリプトを置いておけば実行してくれる。
lifecycleに応じて走らせるスクリプトを変えたい場合は以下を参照のこと。

docs.localstack.cloud

Deploy

今回はAWSへのDeployをしないので端折りまくっているが、serverless.tsのcustom以下の設定でLocalStackを向き先にするステージを指定している。
defaultStageをlocalにしており、LocalStackを向いているステージにlocalを指定しているため今回のDeployはLocalStackにされたということで、別途ステージを用意する場合は注意が必要。

なんか詰まったところ

やたらDeployが止まる

元々利用するServiceはlambdaとsnsくらいしかないと思っていたので、SERVICESの指定をかなり絞っていた。
serverless frameworkが内部でCloudFormationのStackを作るし、IAMもS3も必要になってくるので結果的に有効化したSERVICESはそれなりの量になった…。

API for service 'lambda' not yet implemented or pro feature

Deploy時に以下のようなエラーが出て動かなかった。

API for service 'lambda' not yet implemented or pro feature

つぎはぎのdocker-compose.ymlを作っていたので当然っちゃ当然なんだけど、全然原因が掴めなかった。。 結論を言うと、参考にしていたファイルがLocalStack v1時代に作られたものだったっぽく、公式に記載の通りのマイグレーションが必要になった。

    environment:
      DEBUG: 1
+      DOCKER_HOST: unix:///var/run/docker.sock
      LS_LOG: trace
      SERVICES: 'cloudformation,lambda,logs,iam,s3,sns,events'
      AWS_ACCESS_KEY_ID: 'localstack-test'
      AWS_SECRET_ACCESS_KEY: 'localstack-test'
      AWS_DEFAULT_REGION: 'ap-northeast-1'
-      PROVIDER_OVERRIDE_LAMBDA: docker-reuse
+      LAMBDA_EXECUTOR: 'docker'
    volumes:
      - ./localstack/init-aws.sh:/etc/localstack/init/ready.d/init-aws.sh
+      - /var/run/docker.sock:/var/run/docker.sock

docs.localstack.cloud

ログがLocalStackのコンテナ内にしか出てなくて時間を浪費しまくってしまった。

SSMの読み込み

今回の構成には入れてないが、詰まったので備忘として残しておく。
たとえばStripeのシークレットキー等をDeploy時に環境変数に持たせたりしたいケースがある。
そのようなケースでは事前にParameter StoreやSecret Managerに入れておくことで露出を可能な限り減らす仕組みを構築していることがある。

例に漏れずそのような構成を取っていたがどうもSecret Managerの読み込みに失敗しまくるので回避策を。

  custom: {
    secrets: '${ssm:/aws/reference/secretsmanager/path_to_parameter}',
  }

こんな感じで指定していることがあると思うが、このままDeployするとssmへのアクセスに失敗してLocalStackへのDeployがこける。
もちろん事前にLocalStackのSecret Managerにはpath_to_parameterは用意しているし、ホストからそのパスでアクセスはできる。

で、最終的な回避策としては、/aws/reference/secretsmanager を取り除くとDeployできるようになった

  custom: {
-     secrets: '${ssm:/aws/reference/secretsmanager/path_to_parameter}',
+    secrets: '${ssm:path_to_parameter}',
  }

なのでちゃんとやろうとすると serverless-plugin-ifelse なんかを入れてStageに応じてParameterへのパスを変えたりしないとダメかも。