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に応じて走らせるスクリプトを変えたい場合は以下を参照のこと。
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
ログが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へのパスを変えたりしないとダメかも。