これは、なにをしたくて書いたもの?
前に、LocalStackとTerraform、AWS SAMでAWS Step Functionsを作成してみました。
LocalStack × Terraform × AWS SAMでAWS Step Functionsを動かしてみる - CLOVER🍀
ただ、AWS SAMでもAWS Step Functionsのステートマシンを扱えるらしく、AWS Step Functionsを主体のテーマに扱う場合はAWS SAMで
一気に作ってしまうのもよいかなということで試してみます。
AWS SAMのAWS Step Functionsのサポート
全然気にしていませんでしたが、かなり前から使えるようです。
AWS SAM が新たに AWS Step Functions をサポート
よくよく見ると、AWS SAMのテンプレート内にもAWS Step Functionsのものが含まれています。
https://github.com/aws/aws-sam-cli-app-templates/blob/master/manifest-v2.json
AWS SAMでのAWS Step Functionsのステートマシンのリソース定義はこちら。
AWS::Serverless::StateMachine - AWS Serverless Application Model
DefinitionSubstitutionsでプレースホルダーを使うことで、ステートマシンの定義(Definition)の一部を変数化できるようです。
ステートマシン定義のプレースホルダー変数のマッピングを指定する string-to-string マップ。これは、実行時に取得した値 (組み込み関数からの値など) をステートマシン定義に挿入できるようにします。
AWS::Serverless::StateMachine / プロパティ / DefinitionSubstitutions
また、AWS CloudFormationのリソース定義と異なり、DefinitionUriというプロパティが増えているのでローカルファイルを指定できます。
AWS::StepFunctions::StateMachine - AWS CloudFormation
便利そうですね。
というか、AWS SAMのテンプレートにもAWS Step Functions向けのものが含まれていますね。こちらはPython 3.10のテンプレートです。
$ curl -s https://raw.githubusercontent.com/aws/aws-sam-cli-app-templates/master/manifest-v2.json | jq '."python3.10"[] | select(.appTemplate | test("step"))'
{
"directory": "python3.10/step-func",
"displayName": "Step Functions Sample App (Stock Trader)",
"dependencyManager": "pip",
"appTemplate": "step-functions-sample-app",
"packageType": "Zip",
"useCaseName": "Multi-step workflow"
}
{
"directory": "python3.10/step-func-conn",
"displayName": "Step Functions Sample App (Stock Trader) With Connectors",
"dependencyManager": "pip",
"appTemplate": "step-functions-with-connectors",
"packageType": "Zip",
"useCaseName": "Multi-step workflow with Connectors"
}
GitHubリポジトリーを見ると、もっといろいろありそうですけどね…?
https://github.com/aws/aws-sam-cli-app-templates/tree/master/python3.10
今回は、こちらの内容をLocalStackとAWS SAMのみに置き換えていきます。
LocalStack × Terraform × AWS SAMでAWS Step Functionsを動かしてみる - CLOVER🍀
環境
今回の環境はこちら。
$ python3 --version Python 3.10.12 $ pip3 --version pip 22.0.2 from /usr/lib/python3/dist-packages/pip (python 3.10) $ awslocal --version aws-cli/2.19.4 Python/3.12.6 Linux/5.15.0-125-generic exe/x86_64.ubuntu.22 $ samlocal --version SAM CLI, version 1.127.0
$ export AWS_ACCESS_KEY_ID=dummy $ export AWS_SECRET_ACCESS_KEY=dummy $ export AWS_DEFAULT_REGION=us-east-1
LocalStack。
$ localstack --version 3.8.1
起動。
$ localstack start
AWS SAMでAWS LambdaからAWS Step Functionsまでデプロイする
では、AWS SAMプロジェクトを作成します。
$ samlocal init --name sfn-localstack-sam --runtime python3.10 --app-template hello-world --package-type Zip --no-tracing --no-application-insights --structured-logging $ cd sfn-localstack-sam
テンプレートは「hello-world」にして、このあたりを参考にしつつ自分で作成することにしました。
https://github.com/aws/aws-sam-cli-app-templates/tree/master/python3.10/step-func
AWS Lambda関数。
loop/app.py
def lambda_handler(event, context): iterator = event["iterator"] index = iterator["index"] step = iterator["step"] count = iterator["count"] print(f"index = {index}, step = {step}, count = {count}, event = {event}") index = index + step return { "index": index, "step": step, "count": count, "continue": count > index }
ステートマシンの定義。
statemachine/loop-state-machine.asl.json
{ "Comment": "Iterator State Machine Example", "StartAt": "ConfigureCount", "States": { "ConfigureCount": { "Type": "Pass", "Result": { "count": 10, "index": 0, "step": 1 }, "ResultPath": "$.iterator", "Next": "Iterator" }, "Iterator": { "Type": "Task", "Resource": "${LoopFunctionArn}", "ResultPath": "$.iterator", "Next": "IsCountReached" }, "IsCountReached": { "Type": "Choice", "Choices": [ { "Variable": "$.iterator.continue", "BooleanEquals": true, "Next": "ExampleWork" } ], "Default": "Done" }, "ExampleWork": { "Comment": "Your application logic, to run a specific number of times", "Type": "Pass", "Result": { "success": true }, "ResultPath": "$.result", "Next": "Iterator" }, "Done": { "Type": "Pass", "End": true } } }
ここがプレースホルダーになっています。
"Resource": "${LoopFunctionArn}",
AWS SAMテンプレート。
template.yaml
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: > sfn-localstack-sam Sample SAM Template for sfn-localstack-sam Globals: Function: Timeout: 3 LoggingConfig: LogFormat: JSON Resources: LoopFunction: Type: AWS::Serverless::Function Properties: FunctionName: LoopFunction CodeUri: loop/ Handler: app.lambda_handler Runtime: python3.10 Architectures: - x86_64 LoopStateMachine: Type: AWS::Serverless::StateMachine Properties: Name: LoopStateMachine DefinitionUri: statemachine/loop-state-machine.asl.json DefinitionSubstitutions: LoopFunctionArn: !GetAtt LoopFunction.Arn Outputs: LoopFunction: Description: Loop Lambda Function ARN Value: !GetAtt LoopFunction.Arn LoopFunctionIamRole: Description: Implicit IAM Role created for Loop function Value: !GetAtt LoopFunctionRole.Arn LoopStateMachineArn: Description: "Loop State machine ARN" Value: !Ref LoopStateMachine LoopStateMachineRoleArn: Description: "IAM Role created for Loop State machine based on the specified SAM Policy Templates" Value: !GetAtt LoopStateMachineRole.Arn
ステートマシンのリソース定義はここですね。
LoopStateMachine: Type: AWS::Serverless::StateMachine Properties: Name: LoopStateMachine DefinitionUri: statemachine/loop-state-machine.asl.json DefinitionSubstitutions: LoopFunctionArn: !GetAtt LoopFunction.Arn
DefinitionUriでステートマシンのASLを、DefinitionSubstitutionsでプレースホルダーを指定しています。
AWS Lambdaを作成しつつ、そのARNをプレースホルダーの値に指定できるのでよいですね。
デプロイ。
$ samlocal deploy --region us-east-1
できました。
CloudFormation events from stack operations (refresh every 5.0 seconds) --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ResourceStatus ResourceType LogicalResourceId ResourceStatusReason --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- CREATE_IN_PROGRESS AWS::CloudFormation::Stack sfn-localstack-sam - CREATE_IN_PROGRESS AWS::IAM::Role LoopFunctionRole - CREATE_COMPLETE AWS::IAM::Role LoopFunctionRole - CREATE_IN_PROGRESS AWS::IAM::Role LoopStateMachineRole - CREATE_COMPLETE AWS::IAM::Role LoopStateMachineRole - CREATE_IN_PROGRESS AWS::Lambda::Function LoopFunction - CREATE_COMPLETE AWS::Lambda::Function LoopFunction - CREATE_IN_PROGRESS AWS::StepFunctions::StateMachine LoopStateMachine - CREATE_COMPLETE AWS::StepFunctions::StateMachine LoopStateMachine - CREATE_COMPLETE AWS::CloudFormation::Stack sfn-localstack-sam - --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- CloudFormation outputs from deployed stack ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Outputs ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Key LoopFunction Description Loop Lambda Function ARN Value arn:aws:lambda:us-east-1:000000000000:function:LoopFunction Key LoopFunctionIamRole Description Implicit IAM Role created for Loop function Value arn:aws:iam::000000000000:role/sfn-localstack-sam-LoopFunctionRole-9543b55b Key LoopStateMachineArn Description Loop State machine ARN Value arn:aws:states:us-east-1:000000000000:stateMachine:LoopStateMachine Key LoopStateMachineRoleArn Description IAM Role created for Loop State machine based on the specified SAM Policy Templates Value arn:aws:iam::000000000000:role/sfn-localstack-sam-LoopStateMachineRole-da8f4160 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Successfully created/updated stack - sfn-localstack-sam in us-east-1
ステートマシンを実行。
$ awslocal stepfunctions start-execution --state-machine arn:aws:states:us-east-1:000000000000:stateMachine:LoopStateMachine --name loop
{
"executionArn": "arn:aws:states:us-east-1:000000000000:execution:LoopStateMachine:loop",
"startDate": "2024-11-10T00:30:13.247323+09:00"
}
結果の確認。
$ awslocal stepfunctions describe-execution --execution-arn arn:aws:states:us-east-1:000000000000:execution:LoopStateMachine:loop
{
"executionArn": "arn:aws:states:us-east-1:000000000000:execution:LoopStateMachine:loop",
"stateMachineArn": "arn:aws:states:us-east-1:000000000000:stateMachine:LoopStateMachine",
"name": "loop",
"status": "SUCCEEDED",
"startDate": "2024-11-10T00:30:13.247323+09:00",
"stopDate": "2024-11-10T00:30:17.731679+09:00",
"input": "{}",
"inputDetails": {
"included": true
},
"output": "{\"iterator\":{\"index\":10,\"step\":1,\"count\":10,\"continue\":false},\"result\":{\"success\":true}}",
"outputDetails": {
"included": true
}
}
ログ。
$ awslocal logs tail /aws/lambda/LoopFunction
2024-11-09T15:30:15.703000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 START RequestId: da88fab1-56ef-4da0-b14b-767435c8229a Version: $LATEST
2024-11-09T15:30:15.741000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 index = 0, step = 1, count = 10, event = {'iterator': {'count': 10, 'index': 0, 'step': 1}}
2024-11-09T15:30:15.780000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 END RequestId: da88fab1-56ef-4da0-b14b-767435c8229a
2024-11-09T15:30:15.818000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 REPORT RequestId: da88fab1-56ef-4da0-b14b-767435c8229a Duration: 0.88 ms Billed Duration: 1 ms Memory Size: 128 MB Max Memory Used: 128 MB
2024-11-09T15:30:15.929000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 START RequestId: 2378c403-81f9-466a-a3d8-fac5fcedbe3d Version: $LATEST
2024-11-09T15:30:15.977000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 index = 1, step = 1, count = 10, event = {'iterator': {'index': 1, 'step': 1, 'count': 10, 'continue': True}, 'result': {'success': True}}
2024-11-09T15:30:16.025000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 END RequestId: 2378c403-81f9-466a-a3d8-fac5fcedbe3d
2024-11-09T15:30:16.073000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 REPORT RequestId: 2378c403-81f9-466a-a3d8-fac5fcedbe3d Duration: 0.97 ms Billed Duration: 1 ms Memory Size: 128 MB Max Memory Used: 128 MB
2024-11-09T15:30:16.318000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 START RequestId: d1e3976a-6759-4733-a7f2-1f47ffe4305a Version: $LATEST
2024-11-09T15:30:16.321000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 index = 2, step = 1, count = 10, event = {'iterator': {'index': 2, 'step': 1, 'count': 10, 'continue': True}, 'result': {'success': True}}
2024-11-09T15:30:16.324000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 END RequestId: d1e3976a-6759-4733-a7f2-1f47ffe4305a
2024-11-09T15:30:16.327000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 REPORT RequestId: d1e3976a-6759-4733-a7f2-1f47ffe4305a Duration: 0.89 ms Billed Duration: 1 ms Memory Size: 128 MB Max Memory Used: 128 MB
2024-11-09T15:30:16.514000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 START RequestId: aa79a59a-8bc0-461e-b39b-b2e65bcadd2c Version: $LATEST
2024-11-09T15:30:16.517000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 index = 3, step = 1, count = 10, event = {'iterator': {'index': 3, 'step': 1, 'count': 10, 'continue': True}, 'result': {'success': True}}
2024-11-09T15:30:16.520000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 END RequestId: aa79a59a-8bc0-461e-b39b-b2e65bcadd2c
2024-11-09T15:30:16.523000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 REPORT RequestId: aa79a59a-8bc0-461e-b39b-b2e65bcadd2c Duration: 0.73 ms Billed Duration: 1 ms Memory Size: 128 MB Max Memory Used: 128 MB
2024-11-09T15:30:16.688000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 START RequestId: eb5fc091-10d7-4d9e-9004-394aac58e35a Version: $LATEST
2024-11-09T15:30:16.691000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 index = 4, step = 1, count = 10, event = {'iterator': {'index': 4, 'step': 1, 'count': 10, 'continue': True}, 'result': {'success': True}}
2024-11-09T15:30:16.695000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 END RequestId: eb5fc091-10d7-4d9e-9004-394aac58e35a
2024-11-09T15:30:16.699000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 REPORT RequestId: eb5fc091-10d7-4d9e-9004-394aac58e35a Duration: 0.84 ms Billed Duration: 1 ms Memory Size: 128 MB Max Memory Used: 128 MB
2024-11-09T15:30:16.865000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 START RequestId: f80d6cec-1c36-4198-b339-5f22f2805128 Version: $LATEST
2024-11-09T15:30:16.868000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 index = 5, step = 1, count = 10, event = {'iterator': {'index': 5, 'step': 1, 'count': 10, 'continue': True}, 'result': {'success': True}}
2024-11-09T15:30:16.871000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 END RequestId: f80d6cec-1c36-4198-b339-5f22f2805128
2024-11-09T15:30:16.874000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 REPORT RequestId: f80d6cec-1c36-4198-b339-5f22f2805128 Duration: 0.90 ms Billed Duration: 1 ms Memory Size: 128 MB Max Memory Used: 128 MB
2024-11-09T15:30:17.039000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 START RequestId: e8a7f924-65f2-4a24-80b5-7737935e4bcb Version: $LATEST
2024-11-09T15:30:17.042000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 index = 6, step = 1, count = 10, event = {'iterator': {'index': 6, 'step': 1, 'count': 10, 'continue': True}, 'result': {'success': True}}
2024-11-09T15:30:17.045000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 END RequestId: e8a7f924-65f2-4a24-80b5-7737935e4bcb
2024-11-09T15:30:17.048000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 REPORT RequestId: e8a7f924-65f2-4a24-80b5-7737935e4bcb Duration: 0.76 ms Billed Duration: 1 ms Memory Size: 128 MB Max Memory Used: 128 MB
2024-11-09T15:30:17.207000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 START RequestId: 50cebd24-5804-46a5-ac29-0f8ab4bc4594 Version: $LATEST
2024-11-09T15:30:17.210000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 index = 7, step = 1, count = 10, event = {'iterator': {'index': 7, 'step': 1, 'count': 10, 'continue': True}, 'result': {'success': True}}
2024-11-09T15:30:17.213000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 END RequestId: 50cebd24-5804-46a5-ac29-0f8ab4bc4594
2024-11-09T15:30:17.217000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 REPORT RequestId: 50cebd24-5804-46a5-ac29-0f8ab4bc4594 Duration: 0.80 ms Billed Duration: 1 ms Memory Size: 128 MB Max Memory Used: 128 MB
2024-11-09T15:30:17.377000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 START RequestId: f52fbc3a-f635-44d0-b224-527c92bd78fe Version: $LATEST
2024-11-09T15:30:17.380000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 index = 8, step = 1, count = 10, event = {'iterator': {'index': 8, 'step': 1, 'count': 10, 'continue': True}, 'result': {'success': True}}
2024-11-09T15:30:17.384000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 END RequestId: f52fbc3a-f635-44d0-b224-527c92bd78fe
2024-11-09T15:30:17.387000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 REPORT RequestId: f52fbc3a-f635-44d0-b224-527c92bd78fe Duration: 0.77 ms Billed Duration: 1 ms Memory Size: 128 MB Max Memory Used: 128 MB
2024-11-09T15:30:17.573000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 START RequestId: b33f303e-16ea-4acc-a385-adde97e10cbb Version: $LATEST
2024-11-09T15:30:17.576000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 index = 9, step = 1, count = 10, event = {'iterator': {'index': 9, 'step': 1, 'count': 10, 'continue': True}, 'result': {'success': True}}
2024-11-09T15:30:17.579000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 END RequestId: b33f303e-16ea-4acc-a385-adde97e10cbb
2024-11-09T15:30:17.582000+00:00 2024/11/09/[$LATEST]1e73b33beb16d0838ab5a6c31453b241 REPORT RequestId: b33f303e-16ea-4acc-a385-adde97e10cbb Duration: 1.07 ms Billed Duration: 2 ms Memory Size: 128 MB Max Memory Used: 128 MB
OKですね。
おわりに
LocalStackとAWS SAMで、AWS Step Functionsを構築して動かしてみました。
とりあえずAWS Step Functionsを作って動かしたいなら、AWS SAMだけで十分ですね。
勉強する時は本筋以外のところは少なくするにこしたことはないので、AWS SAMでやるのがよさそうです。