初めに
2023/11頃にAWS SAM CLIのリモート実行(remote invoke
)でStep Functionsのステートマシンが実行できるようになりました。
こちらの対応はリモートだけとなり本記事執筆ではローカルモックは用意されていないためAWS SAM CLI単体ではローカルのテスト環境を用意することができません。
AWS公式のドキュメントではStep Functions Localを併用した方法が紹介されていますが、こちらはあくまでSAMは含まれるLambda関数のローカル実行を行うのみでステートマシンの実行は直接担わないものとなりステートマシンは自体はSAM外で実現する形となりそうです。
公式のツールではないですがLocalStackがオールインパッケージという感じで個人的には好んで使っていたので、どうにかしてSAM実行時のエンドポイントをこちらに向けられればやりようはあるのではないかと考えておりました。
AWS SAM CLIはAWS CLIと異なりENDPOINT_URL
のような接続先を差し替えるオプションがないためその部分をどうにかしないといけないのですが、調べていたところLocalStack配下にaws-sam-cli-localというリポジトリが見つかりました。
確認してみるとAWS SAM CLIのラッパーツールで大雑把に読んでみる限り実行の向き先を自由に変更できるもののようです。
(デフォルトは本来の趣旨としてはlocalhost上のLocalStackだがAWS_ENDPOINT_URL
の指定で別にもできそう)
ツールとしてはSAMの呼び出し周りをゴリゴリ書いて何とかしているものではなくでインストールされたSAMのモジュールを呼び出しているだけですので、大きく仕様が変わらない限りはこちら側のツールの対応を待つという必要もなく対応できそうというのも一つの魅力に思えます。
指定もaws-sam-cli>=1.1.0
という形で緩く未指定でインストールすればその時点で最新のものが入ります。
実際にこちらを利用してローカル上でステートマシンを実行してみます。
LocalStack環境の立ち上げ
環境はdocker compose
で立ち上げたいため公式のドキュメントをもとにdocker-compose.yml
を作成します。
docker-compose.yml
version: "3.8"
services:
localstack:
container_name: "${LOCALSTACK_DOCKER_NAME:-localstack-main}"
image: localstack/localstack
ports:
- "127.0.0.1:4566:4566"
- "127.0.0.1:4510-4559:4510-4559"
volumes:
# 今回特に永続化はしないので
# - "${LOCALSTACK_VOLUME_DIR:-./volume}:/var/lib/localstack"
- "/var/run/docker.sock:/var/run/docker.sock"
(しばらく前は個別に起動するサービスを指定したような気がしますがいつの間にか無くなった?)
特別な対応は必要なくdocker compose up
で環境を立ち上げます。
% docker compose up -d
[+] Running 1/1
✔ Container localstack-main Started
aws-sam-cli-localのインストール
pip経由でインストール可能なためそちらでインストールします。
--version
を確認してみると含まれるAWS SAM CLI側のバージョンが表示されるので本当にオプションを含めよしなに中継しているくらいになりそうです。
% pip install aws-sam-cli-local
...
% samlocal --version
SAM CLI, version 1.108.0
# aws-sam-cli-local自体はv1.68.0
% pip freeze | grep aws-sam-cli
aws-sam-cli==1.108.0
aws-sam-cli-local==1.68.0
ローカルスタック環境を対ししてオプションなしでデプロイやリモート実行コマンドを動かしてみるとデフォルトのローカルスタックのエンドポイントに繋がらないエラーが出るので、いい感じにコマンドを継承しつつ向き先だけ変わっていそうです。
% samlocal deploy
Error: Could not connect to the endpoint URL: "http://localhost:4566/"
% samlocal remote invoke
Error: Could not connect to the endpoint URL: "http://localhost:4566/"
デプロイ
今回は以下のサンプルテンプレートを利用します(sam init
の4番目のテンプレートです)。
処理としては主制御をStep Functions側で行いつつLambda関数で処理し引き渡された値をDynamoDBのデータを書き込むという感じです。
趣旨としては全く別の記事ですがStep Functionsのリモート実行対応時の執筆記事でこのテンプレートを利用したリソースの動作を確認しているので一度みていただくのが処理のイメージはつきやすいかなと思います。
samlocal deploy
でデプロイを行います。
% samlocal deploy
Creating the required resources...
Successfully created!
Managed S3 bucket: aws-sam-cli-managed-default-samclisourcebucket-ef0e7958
A different default S3 bucket can be set in samconfig.toml
Or by specifying --s3-bucket explicitly.
Uploading to 45bd9a8dff25082b8f6d078abb1b3581 2839 / 2839 (100.00%)
Uploading to 0966869826df4e1eca6492b5d40629b9 675 / 675 (100.00%)
Uploading to e7a567f676ae4056e7adbd4e84881e50 913 / 913 (100.00%)
Uploading to 992cd4f12a9f3b4c5c38e305bb4d821d 912 / 912 (100.00%)
Deploying with following values
===============================
Stack name : sam-app-sfn
Region : ap-northeast-1
Confirm changeset : True
Disable rollback : False
Deployment s3 bucket : aws-sam-cli-managed-default-samclisourcebucket-ef0e7958
Capabilities : ["CAPABILITY_IAM"]
Parameter overrides : {}
Signing Profiles : {}
...
CloudFormation outputs from deployed stack
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Outputs
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Key StockTradingStateMachineArn
Description Stock Trading State machine ARN
Value arn:aws:states:ap-northeast-1:000000000000:stateMachine:sam-app-sfn-StockTradingStateMachine-0c6f596a
Key StockTradingStateMachineRoleArn
Description IAM Role created for Stock Trading State machine based on the specified SAM Policy Templates
Value arn:aws:iam::000000000000:role/sam-app-sfn-StockTradingStateMachine-386e1e6e
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Outputsの値からローカルにデプロイされていそうな雰囲気は感じますが念の為AWS CLIで明示的にLocalStackのエンドポイントを指定してリソースのデプロイを確認してみます。
% aws stepfunctions list-state-machines --endpoint-url=http://localhost:4566/
{
"stateMachines": [
{
"stateMachineArn": "arn:aws:states:ap-northeast-1:000000000000:stateMachine:sam-app-sfn-StockTradingStateMachine-0c6f596a",
"name": "sam-app-sfn-StockTradingStateMachine-0c6f596a",
"type": "STANDARD",
"creationDate": "2024-01-31T14:47:37.395362+09:00"
}
]
}
% aws lambda list-functions --endpoint-url=http://localhost:4566/
{
"Functions": [
{
"FunctionName": "sam-app-sfn-StockCheckerFunction-1b089825",
"FunctionArn": "arn:aws:lambda:ap-northeast-1:000000000000:function:sam-app-sfn-StockCheckerFunction-1b089825",
"Runtime": "python3.10",
"Role": "arn:aws:iam::000000000000:role/sam-app-sfn-StockCheckerFunctionRole-c72096c3",
"Handler": "app.lambda_handler",
"CodeSize": 675,
"Description": "",
"Timeout": 3,
"MemorySize": 128,
"LastModified": "2024-01-31T05:46:51.288232+0000",
"CodeSha256": "6+F5S7OsQ0/DzPgzsv3Qq3W2gEoE1QzO4BJZEFVfgf0=",
"Version": "$LATEST",
"TracingConfig": {
"Mode": "PassThrough"
},
...
}
% aws dynamodb list-tables --endpoint-url=http://localhost:4566/
{
"TableNames": [
"sam-app-sfn-TransactionTable-06f89e94"
]
}
問題なくLocalStack環境に乗っていそうです。
実行
実行はSAM内部のものではなくLocalStack側のリソースを実行するのでremote invoke
となります。
コマンド的にはリモート実行、実際に実行されるのはローカル言葉尻で笑ってしまうだけで済めば良いですがsam
とsamlocal
を間違えて実行すると実際の環境上のものが実行されることもありますのでご注意ください。
% samlocal remote invoke StockTradingStateMachine
Invoking Step Function StockTradingStateMachine
{}%
DyanamoDBにデータを入っていることで内部のステートマシンが正常に稼働していることを確認します。
現時点ではDynamoDBへのクエリまでSAMはサポートしていないのでここはAWS CLIで行います。
% aws dynamodb scan --table-name sam-app-sfn-TransactionTable-06f89e94 --endpoint-url=http://localhost:4566/
{
"Items": [
{
"Quantity": {
"N": "1"
},
"Type": {
"S": "buy"
},
"Id": {
"S": "64859cd7-2e6b-4325-82a0-86fef6773227"
},
"Price": {
"N": "49"
},
"Timestamp": {
"S": "2024-01-31T05:52:25.781016"
}
}
],
"Count": 1,
"ScannedCount": 1,
"ConsumedCapacity": null
}
処理されたデータが格納されていることができました。
終わりに
LocalStackを併用することで実質的にSAMを使ってローカルでStep Functionsのテストを実行できることを確認することができました。
準備としても事前にdocker-compose.yml
を事前用意しておけば立ち上げもシンプルで、対応していればローカルで完結するのでCIでインテグレーションテストをしたいけどできれば実際の環境を使わず閉じた環境で実現することもできるので良さそうな雰囲気を感じます。
sam local
にStep Functionsを利用できるようになってもLocalStackとしてはエミュレートしているサービスも広いことを考えると手法としては息の長いようなものではないかなと思います。
補足: LocalStack対応サービスについて
LocalStackは全てのサービスをサポートしているわけではなく、また対応していても機能によっては無料プランでは利用できないものが含まれるためご注意ください。
対応している機能やカバレッジ等は以下のページに記載がございますのでごちらもご参照ください。