これは、なにをしたくて書いたもの?
AWSを使う代わりに、LocalStackをよく使っているのですが。
特にAWS SAMでデプロイすると2回目以降で困ったことになっていたので、それをなんとか回避したいといろいろ
考えてみました。
1回目はいいのですが、2回目は応答が返らなくなり、スタックを削除しようとしても失敗するのでけっこう困って
いたのですが。そもそも本物のAWSでもないので、これを時間に時間をかけて調べるのもな、と思っていたので。
どのみち短時間しか使わないので、新規にリソースをデプロイしなおすことにしました。
結論
これでいくことにします。
デプロイ。
$ yes | samlocal sync --stack-name $(uuidgen) --region us-east-1
AWS SAM Accelerateを使います。
Amazon API GatewayのREST API IDの取得。
$ REST_API_ID=$(awslocal apigateway get-rest-apis --query 'reverse(sort_by(items[], &createdDate))[0].id' --output text)
デプロイ先にアクセスする時のURL。
http://localhost:4566/restapis/$REST_API_ID/Prod/_user_request_/[path]
環境
確認環境は、こちらです。
$ localstack --version 0.13.2.1 $ python3 -V Python 3.8.10 $ awslocal --version aws-cli/2.4.8 Python/3.8.8 Linux/5.4.0-91-generic exe/x86_64.ubuntu.20 prompt/off $ samlocal --version SAM CLI, version 1.36.0
起動。
$ LAMBDA_EXECUTOR=docker-reuse localstack start
困っていたこと
とりあえず、なにに困っていたかを書いておきます。
再現のために、プロジェクトを作成。
$ samlocal init --name localstack-deploy-sample --runtime nodejs14.x --app-template hello-world --package-type Zip
アプリケーション名をlocalstack-deploy-sample
にして、ランタイムはNode.js 14.xにします。
プロジェクト内に移動。
$ cd localstack-deploy-sample
そのままビルドしてデプロイ。
$ samlocal build $ awslocal s3 mb s3://my-bucket $ samlocal deploy --stack-name my-stack --region us-east-1 --s3-bucket my-bucket
LocalStackの場合、Amazon S3バケットも先に作っておかないといけないようです。ここも面倒といえば面倒です…。
1回目は、問題なくうまくいきます。
2022-01-05 23:30:03 - Waiting for stack create/update to complete CloudFormation events from stack operations --------------------------------------------------------------------------------------------------------------------------------------------- ResourceStatus ResourceType LogicalResourceId ResourceStatusReason --------------------------------------------------------------------------------------------------------------------------------------------- CREATE_COMPLETE AWS::CloudFormation::Stack ServerlessRestApiDeployment510f4c - 1a20 CREATE_COMPLETE AWS::CloudFormation::Stack ServerlessRestApi - CREATE_COMPLETE AWS::CloudFormation::Stack HelloWorldFunctionRole - CREATE_COMPLETE AWS::CloudFormation::Stack ServerlessRestApiProdStage - CREATE_COMPLETE AWS::CloudFormation::Stack my-stack - CREATE_COMPLETE AWS::CloudFormation::Stack HelloWorldFunctionHelloWorldPermi - ssionProd CREATE_COMPLETE AWS::CloudFormation::Stack HelloWorldFunction - --------------------------------------------------------------------------------------------------------------------------------------------- CloudFormation outputs from deployed stack ---------------------------------------------------------------------------------------------------------------------------------------------- Outputs ---------------------------------------------------------------------------------------------------------------------------------------------- Key HelloWorldApi Description API Gateway endpoint URL for Prod stage for Hello World function Value https://nwoc71vnry.execute-api.amazonaws.com:4566/Prod/hello/ Key HelloWorldFunction Description Hello World Lambda Function ARN Value arn:aws:lambda:us-east-1:000000000000:function:my-stack-HelloWorldFunction-55d04da1 Key HelloWorldFunctionIamRole Description Implicit IAM Role created for Hello World function Value arn:aws:iam::000000000000:role/my-stack-HelloWorldFunctionRole-e4d3ca1d ---------------------------------------------------------------------------------------------------------------------------------------------- Successfully created/updated stack - my-stack in us-east-1
ここで、もう1度デプロイしようとしてみます。
$ samlocal deploy --stack-name my-stack --region us-east-1 --s3-bucket my-bucket
すると、今度はここで止まってしまいます。
2022-01-05 23:32:18 - Waiting for stack create/update to complete CloudFormation events from stack operations --------------------------------------------------------------------------------------------------------------------------------------------- ResourceStatus ResourceType LogicalResourceId ResourceStatusReason --------------------------------------------------------------------------------------------------------------------------------------------- UPDATE_COMPLETE AWS::CloudFormation::Stack HelloWorldFunction - UPDATE_COMPLETE AWS::CloudFormation::Stack HelloWorldFunctionHelloWorldPermi - ssionProd UPDATE_COMPLETE AWS::CloudFormation::Stack HelloWorldFunctionRole - CREATE_COMPLETE AWS::CloudFormation::Stack my-stack - UPDATE_COMPLETE AWS::CloudFormation::Stack ServerlessRestApiProdStage - UPDATE_COMPLETE AWS::CloudFormation::Stack ServerlessRestApiDeployment510f4c - 1a20 UPDATE_COMPLETE AWS::CloudFormation::Stack ServerlessRestApi - ---------------------------------------------------------------------------------------------------------------------------------------------
では、スタックを削除したらいいのでは?と思うのですが
$ samlocal delete --stack-name my-stack --region us-east-1 Are you sure you want to delete the stack my-stack in the region us-east-1 ? [y/N]: y
これもうまくいきません。
Traceback (most recent call last): File "/path/to/venv/bin/samlocal", line 41, in <module> main.cli() File "/path/to/venv/lib/python3.8/site-packages/click/core.py", line 1128, in __call__ return self.main(*args, **kwargs) File "/path/to/venv/lib/python3.8/site-packages/click/core.py", line 1053, in main rv = self.invoke(ctx) File "/path/to/venv/lib/python3.8/site-packages/click/core.py", line 1659, in invoke return _process_result(sub_ctx.command.invoke(sub_ctx)) File "/path/to/venv/lib/python3.8/site-packages/click/core.py", line 1395, in invoke return ctx.invoke(self.callback, **ctx.params) File "/path/to/venv/lib/python3.8/site-packages/click/core.py", line 754, in invoke return __callback(*args, **kwargs) File "/path/to/venv/lib/python3.8/site-packages/click/decorators.py", line 84, in new_func return ctx.invoke(f, obj, *args, **kwargs) File "/path/to/venv/lib/python3.8/site-packages/click/core.py", line 754, in invoke return __callback(*args, **kwargs) File "/path/to/venv/lib/python3.8/site-packages/samcli/lib/utils/version_checker.py", line 41, in wrapped actual_result = func(*args, **kwargs) File "/path/to/venv/lib/python3.8/site-packages/samcli/cli/main.py", line 87, in wrapper return func(*args, **kwargs) File "/path/to/venv/lib/python3.8/site-packages/samcli/commands/delete/command.py", line 77, in cli do_cli( File "/path/to/venv/lib/python3.8/site-packages/samcli/commands/delete/command.py", line 101, in do_cli delete_context.run() File "/path/to/venv/lib/python3.8/site-packages/samcli/commands/delete/delete_context.py", line 345, in run is_deployed = self.cf_utils.has_stack(stack_name=self.stack_name) File "/path/to/venv/lib/python3.8/site-packages/samcli/lib/delete/cfn_utils.py", line 33, in has_stack if stack["EnableTerminationProtection"]: KeyError: 'EnableTerminationProtection'
いつもどうしていたかというと、LocalStackを再起動していたのですが。
それで、どうしようかなと思っていたのですが。
まず、sam sync
というコマンドを見つけまして。これを使うとsam build
とsam deploy
を一気にできるみたいです。
AWS SAM Accelerateによるサーバーレス開発の加速 | Amazon Web Services ブログ
Getting started with AWS SAM Accelerate - AWS Serverless Application Model
これはAWS SAM Accelerateというらしく、AWS CloudFormationの変更確認をスキップするので高速らしいです。
オプション無しのsam syncコマンドは、sam deployコマンドと同様に全てのインフラストラクチャとコードをデプロイまたは、アップデートします。ただし、sam deployとは異なり、sam syncはAWS CloudFormationの変更セットプロセスをバイパスします。
試してみましょう。
$ samlocal sync --stack-name sync-stack --region us-east-1
Amazon S3バケットはAWS SAM側で作ってくれるみたいです。
初回は、こういう表示が出ます。
Creating the required resources... Successfully created!
また、プレビュー機能(かつ開発環境のみで使うこと、だそうで)だからか、実行時に確認を求められます。
Managed S3 bucket: aws-sam-cli-managed-default-samclisourcebucket-2d2f9b80 Default capabilities applied: ('CAPABILITY_NAMED_IAM', 'CAPABILITY_AUTO_EXPAND') To override with customized capabilities, use --capabilities flag or set it in samconfig.toml This feature is currently in beta. Visit the docs page to learn more about the AWS Beta terms https://aws.amazon.com/service-terms/. The SAM CLI will use the AWS Lambda, Amazon API Gateway, and AWS StepFunctions APIs to upload your code without performing a CloudFormation deployment. This will cause drift in your CloudFormation stack. **The sync command should only be used against a development stack**. Confirm that you are synchronizing a development stack and want to turn on beta features. Enter Y to proceed with the command, or enter N to cancel: [y/N]:
sam build
の動作が見えた後にデプロイが実行され、完了します。
Initiating deployment ===================== 2022-01-05 23:45:28 - Waiting for stack create/update to complete CloudFormation events from stack operations --------------------------------------------------------------------------------------------------------------------------------------------- ResourceStatus ResourceType LogicalResourceId ResourceStatusReason --------------------------------------------------------------------------------------------------------------------------------------------- CREATE_COMPLETE AWS::CloudFormation::Stack HelloWorldFunctionRole - CREATE_COMPLETE AWS::CloudFormation::Stack AwsSamAutoDependencyLayerNestedSt - ack UPDATE_COMPLETE AWS::CloudFormation::Stack AwsSamAutoDependencyLayerNestedSt - ack CREATE_COMPLETE AWS::CloudFormation::Stack ServerlessRestApiProdStage - CREATE_COMPLETE AWS::CloudFormation::Stack ServerlessRestApiDeployment510f4c - 1a20 CREATE_COMPLETE AWS::CloudFormation::Stack ServerlessRestApi - UPDATE_COMPLETE AWS::CloudFormation::Stack HelloWorldFunctionRole - CREATE_COMPLETE AWS::CloudFormation::Stack HelloWorldFunction - CREATE_COMPLETE AWS::CloudFormation::Stack sync-stack - CREATE_COMPLETE AWS::CloudFormation::Stack HelloWorldFunctionHelloWorldPermi - ssionProd CREATE_COMPLETE AWS::CloudFormation::Stack HelloWorldFunction - UPDATE_COMPLETE AWS::CloudFormation::Stack ServerlessRestApiProdStage - UPDATE_COMPLETE AWS::CloudFormation::Stack ServerlessRestApiDeployment510f4c - 1a20 UPDATE_COMPLETE AWS::CloudFormation::Stack ServerlessRestApi - CREATE_COMPLETE AWS::CloudFormation::Stack HelloWorldFunctionHelloWorldPermi - ssionProd --------------------------------------------------------------------------------------------------------------------------------------------- CloudFormation outputs from deployed stack ---------------------------------------------------------------------------------------------------------------------------------------------- Outputs ---------------------------------------------------------------------------------------------------------------------------------------------- Key HelloWorldFunction19d43fc4DepLayer Description Value Key HelloWorldApi Description API Gateway endpoint URL for Prod stage for Hello World function Value https://rxq2hpk9ab.execute-api.amazonaws.com:4566/Prod/hello/ Key HelloWorldFunction Description Hello World Lambda Function ARN Value arn:aws:lambda:us-east-1:000000000000:function:sync-stack-HelloWorldFunction-bd920b93 Key HelloWorldFunctionIamRole Description Implicit IAM Role created for Hello World function Value arn:aws:iam::000000000000:role/sync-stack-HelloWorldFunctionRole-c1e533fa ---------------------------------------------------------------------------------------------------------------------------------------------- Stack creation succeeded. Sync infra completed. {'StackId': 'arn:aws:cloudformation:us-east-1:000000000000:stack/sync-stack/76e2a488', 'ResponseMetadata': {'HTTPStatusCode': 200, 'HTTPHeaders': {'content-type': 'text/html; charset=utf-8', 'content-length': '254', 'access-control-allow-origin': '*', 'access-control-allow-methods': 'HEAD,GET,PUT,POST,DELETE,OPTIONS,PATCH', 'access-control-allow-headers': 'authorization,cache-control,content-length,content-md5,content-type,etag,location,x-amz-acl,x-amz-content-sha256,x-amz-date,x-amz-request-id,x-amz-security-token,x-amz-tagging,x-amz-target,x-amz-user-agent,x-amz-version-id,x-amzn-requestid,x-localstack-target,amz-sdk-invocation-id,amz-sdk-request', 'access-control-expose-headers': 'etag,x-amz-version-id', 'connection': 'close', 'date': 'Wed, 05 Jan 2022 14:45:28 GMT', 'server': 'hypercorn-h11'}, 'RetryAttempts': 0}}
なお、もう1度実行するとエラーになってしまいます。既存のリソースがあるとダメみたいですね。
Initiating deployment ===================== 2022-01-05 23:46:06 - Waiting for stack create/update to complete CloudFormation events from stack operations --------------------------------------------------------------------------------------------------------------------------------------------- ResourceStatus ResourceType LogicalResourceId ResourceStatusReason --------------------------------------------------------------------------------------------------------------------------------------------- UPDATE_COMPLETE AWS::CloudFormation::Stack AwsSamAutoDependencyLayerNestedSt - ack UPDATE_FAILED AWS::CloudFormation::Stack sync-stack - --------------------------------------------------------------------------------------------------------------------------------------------- Failed to create/update the stack: sync-stack, Waiter StackUpdateComplete failed: Waiter encountered a terminal failure state: For expression "Stacks[].StackStatus" we matched expected path: "UPDATE_FAILED" at least once Error: Failed to create/update the stack: sync-stack, Waiter StackUpdateComplete failed: Waiter encountered a terminal failure state: For expression "Stacks[].StackStatus" we matched expected path: "UPDATE_FAILED" at least once
なので、もう環境を使い捨てることを前提に、スタック名をランダム(今回はUUID)にしてしまいます。
$ samlocal sync --stack-name $(uuidgen) --region us-east-1
どのみち、遊び終わったら終了しますし、リソースが増えて困るようであれば再起動すればいいので…。
そして、毎回確認されるのも面倒なので、yes
コマンドをつなげます(確認を省略する方法はわかりませんでした)。
$ yes | samlocal sync --stack-name $(uuidgen) --region us-east-1
こうすると、Amazon API Gatewayがたくさん作られていくことになりますが、最新のREST APIのIDを抽出します。
$ REST_API_ID=$(awslocal apigateway get-rest-apis --query 'reverse(sort_by(items[], &createdDate))[0].id' --output text)
こちらを使って、デプロイしたAPIにアクセスします。
http://localhost:4566/restapis/$REST_API_ID/Prod/_user_request_/[path]
こんな感じですね。
まとめ
LocalStack+AWS SAMで、いつもデプロイし直す時に困っていたので対処方法を考えてみました。
いや、素直にAWSを使えばいいのではという話もあるのですが、LocalStackとAWS SAMの組み合わせの場合は
これでなんとかしていこうかなと思います。