こんにちは。AS部の兼安です。
今回はAWS SAMテンプレートを使って、AWS Step Functionsのステートマシンを構築する方法を紹介します。
- 本記事の対象者
- AWS Step FunctionsとAWS SAMとは
- AWS公式のサンプルを使って、AWS Step Functionsのステートマシンを構築する
- 作成したステートマシンを実行する
- ステートマシンの動きを知る
- 入力パラメータが書きかわらないようにするには
- まとめ
本記事の対象者
AWS Step FunctionsはステートマシンをGUIで編集できますが、ヒューマンエラー防止と効率化を考えると、コードで管理することが望ましいです。
本記事では、AWS SAMを使って、Step Functionsのステートマシンを構築します。
AWS Step Functionsを使っているけれど、もう少し深く知り、便利に使いたい方におすすめです。
AWS Step FunctionsとAWS SAMとは
AWS Step Functionsは、AWS上で 処理や条件分岐、エラー処理などのワークフローを作れるサービスです。
昔からあるジョブ管理システムのAWS版とも言えます。
AWS Step Functions 構築したワークフローのことを「ステートマシン」と呼びます。
AWS SAMは、AWSのリソースをテンプレートで管理するツールです。
サーバーレス系のサービスの構築がしやすくなっています。
AWS SAMを使えば、CLIでAWS Step Functionsのステートマシンを構築することができます。
AWS公式のサンプルを使って、AWS Step Functionsのステートマシンを構築する
AWS SAMのインストール
AWS SAMを使う場合は、AWS SAM CLIをインストールする必要があります。
下記サイトに従い、インストール作業を行います。
AWS公式のサンプルに従い、AWS SAMテンプレートを初期化する
AWSの公式サイトにサンプルがあります。
これに従うことで、AWS Lambdaを順次実行するステートマシンを構築することができます。
これを使って説明をしてみます。
サイトの内容に従い、sam init
コマンドを実行、ウィザードに従って進めます。
$ sam init You can preselect a particular runtime or package type when using the `sam init` experience. Call `sam init --help` to learn more. Which template source would you like to use? 1 - AWS Quick Start Templates 2 - Custom Template Location Choice: 1 Choose an AWS Quick Start application template 1 - Hello World Example 2 - Data processing 3 - Hello World Example with Powertools for AWS Lambda 4 - Multi-step workflow 5 - Scheduled task 6 - Standalone function 7 - Serverless API 8 - Infrastructure event management 9 - Lambda Response Streaming 10 - Serverless Connector Hello World Example 11 - Multi-step workflow with Connectors 12 - GraphQLApi Hello World Example 13 - Full Stack 14 - Lambda EFS example 15 - Hello World Example With Powertools for AWS Lambda 16 - DynamoDB Example 17 - Machine Learning Template: 4 Which runtime would you like to use? 1 - dotnet6 2 - go1.x 3 - go (provided.al2) 4 - java21 5 - java17 6 - java11 7 - java8.al2 8 - java8 9 - nodejs20.x 10 - nodejs18.x 11 - nodejs16.x 12 - python3.9 13 - python3.8 14 - python3.12 15 - python3.11 16 - python3.10 17 - ruby3.2 18 - ruby2.7 Runtime: 16 Based on your selections, the only Package type available is Zip. We will proceed to selecting the Package type as Zip. Based on your selections, the only dependency manager available is pip. We will proceed copying the template using pip. Would you like to enable X-Ray tracing on the function(s) in your application? [y/N]: Would you like to enable monitoring using CloudWatch Application Insights? For more info, please view https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwatch-application-insights.html [y/N]: Would you like to set Structured Logging in JSON format on your Lambda functions? [y/N]: Project name [sam-app]: Cloning from https://github.com/aws/aws-sam-cli-app-templates (process may take a moment) ----------------------- Generating application: ----------------------- Name: sam-app Runtime: python3.10 Architectures: x86_64 Dependency Manager: pip Application Template: step-functions-sample-app Output Directory: . Configuration file: sam-app/samconfig.toml Next steps can be found in the README file at sam-app/README.md Commands you can use next ========================= [*] Create pipeline: cd sam-app && sam pipeline init --bootstrap [*] Validate SAM template: cd sam-app && sam validate [*] Test Function in the Cloud: cd sam-app && sam sync --stack-name {stack-name} --watch
ポイントは以下の通りです。
Which template source would you like to use?
の質問で、1 - AWS Quick Start Templates
を選択します。Choose an AWS Quick Start application template
の質問で、4 - Multi-step workflow
を選択します。Which runtime would you like to use?
の質問で、今回は16 - python3.10
を選択しています。 ここは好みで選択で大丈夫です。- その他の質問は、デフォルトのままで大丈夫です。
コマンドが完了するとソース一式ができます。
できあがるソースの構成も、上記の公式サイトにあるのでご覧ください。
テンプレートの中身を確認
できたものを動かす前にちょっと中身を確認してみます。
Lambda関数やState Functionsのステートマシン本体の定義は、template.yaml
に記述されています。
ステートマシンの中身は、state-machine-definition.json
に記述されています。
template.yaml
のステートマシンの定義がこちらです。
StockTradingStateMachine: Type: AWS::Serverless::StateMachine # More info about State Machine Resource: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-statemachine.html Properties: DefinitionUri: statemachine/stock_trader.asl.json DefinitionSubstitutions: StockCheckerFunctionArn: !GetAtt StockCheckerFunction.Arn StockSellerFunctionArn: !GetAtt StockSellerFunction.Arn StockBuyerFunctionArn: !GetAtt StockBuyerFunction.Arn DDBPutItem: !Sub arn:${AWS::Partition}:states:::dynamodb:putItem DDBTable: !Ref TransactionTable Events: HourlyTradingSchedule: Type: Schedule # More info about Schedule Event Source: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-statemachine-schedule.html Properties: Description: Schedule to run the stock trading state machine every hour Enabled: False # This schedule is disabled by default to avoid incurring charges. Schedule: "rate(1 hour)" Policies: # Find out more about SAM policy templates: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-policy-templates.html - LambdaInvokePolicy: FunctionName: !Ref StockCheckerFunction - LambdaInvokePolicy: FunctionName: !Ref StockSellerFunction - LambdaInvokePolicy: FunctionName: !Ref StockBuyerFunction - DynamoDBWritePolicy: TableName: !Ref TransactionTable
DefinitionUri
プロパティでステートマシンの定義を書いたJSONファイルのパスを指定しています。
これにより、JSONでステートマシンを定義することができます。
DefinitionUri: statemachine/stock_trader.asl.json
DefinitionUrl
プロパティではなく、Definition
プロパティを使えば、template.yaml
に直接ステートマシンの定義を書くこともできます。
個人的には、マネジメントコンソールで既存のステートマシンの定義をJSONエクスポートできるので、JSONで管理する方が楽だと思います。
LambdaのARNなどをステートマシンの定義ファイルへ値を渡す仕組み
DefinitionSubstitutions: StockCheckerFunctionArn: !GetAtt StockCheckerFunction.Arn StockSellerFunctionArn: !GetAtt StockSellerFunction.Arn StockBuyerFunctionArn: !GetAtt StockBuyerFunction.Arn DDBPutItem: !Sub arn:${AWS::Partition}:states:::dynamodb:putItem DDBTable: !Ref TransactionTable
template.yaml
のDefinitionSubstitutions
は、JSONファイルに値を渡すためのプロパティです。
statemachine/stock_trader.asl.json
側で、以下のように使用できます。
"Resource": "${StockCheckerFunctionArn}",
ステートマシンでは呼び出すLambda関数やDynamoDBテーブルのARNを指定します。
Lambda関数やDynamoDBテーブルもSAMの中で作るので、このようにJSONに値を渡すような形に仕上げる必要があります。
ポリシーの指定の仕組み
Policies: # Find out more about SAM policy templates: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-policy-templates.html - LambdaInvokePolicy: FunctionName: !Ref StockCheckerFunction - LambdaInvokePolicy: FunctionName: !Ref StockSellerFunction - LambdaInvokePolicy: FunctionName: !Ref StockBuyerFunction - DynamoDBWritePolicy: TableName: !Ref TransactionTable
template.yaml
のこの部分でステートマシンに付与するポリシーを指定しています。
LambdaInvokePolicy
やDynamoDBWritePolicy
の部分は、使える文字列が決まっていて、下記サイトで確認ができます。
FunctionName
やTableName
の部分は、各ポリシーの詳細を見ることで知ることができます。
LambdaInvokePolicy
なら以下のように書いてあります。
"Statement": [ { "Effect": "Allow", "Action": [ "lambda:InvokeFunction" ], "Resource": { "Fn::Sub": [ "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:${functionName}*", { "functionName": { "Ref": "FunctionName" } } ] } } ]
"functionName": { "Ref": "FunctionName" }
とあるので、FunctionName: !Ref StockCheckerFunction
のように書きます。
Lambdaの関数名を渡すと、それを元に内部でARNを組み立ててくれます。
逆に、内部でARNを組み立てようとするので、ARNそのものを渡してしまうとARN+ARNのような無効な文字列になってしまうので注意です。
なお、この指定の仕方だとステートマシン名+ランダム文字列のロールができます。
Policies
属性ではなくRole
属性を使えば、別途作成したIAMロールを指定することもできますので、お好みでどうぞ。
ビルドとデプロイ
AWS SAMはビルド>デプロイの順でコマンド実行することで、AWSリソースを作成します。
aws sam build aws sam deploy
これでStep Functionsにステートマシンが作成されます。
このサンプルはNameが固定値で指定されていないので、ステートマシンの名前にランダム文字列が付与されます。
作成したステートマシンを実行する
作成したステートマシンは、実行を開始する
で実行できます。
実行すると、パラメータの入力画面が出ます。
パラメータを入力して続行するとステートマシンが実行されます。
ステートマシンの動きを知る
せっかくなのでできあがったステートマシンの動きを見ていきましょう。
作成されたステートマシンを見ると分岐があるのが確認できます。
これはどのように動いているのか、調べてみます。
Buy or Sell?
の部分(ステートと呼びます)をクリックして、定義
タブをクリックすると、この分岐の定義が見ることができます。
そこで、$.stock_price
が50以下なら次のステートBuy Stock
に進むよう指定されているのがわかります。
定義
と入力
を見比べると、$.stock_price
の$
の部分は入力パラメータのことなのだとわかります。
1つ目のステートCheck Stock Value
の入力、そして2つ目のステートBuy or Sell?
の入力と見ていくと、入力の内容がステートマシンが進むのに応じて変化していることがわかります。
最初は、実行時に入力されたパラメータがそのまま入力となっていて、1つ目のステートCheck Stock Value
が終わると、次のステートに入力される値が変わっているのがわかります。
ここで1つ目のステートCheck Stock Value
の詳細
を見てみます。
Lambdaを実行するステートであることが確認できます。
このLambdaのソースコードは、サンプルの中にあります。
functions/stock_checker/app.py
です。
def lambda_handler(event, context): """Sample Lambda function which mocks the operation of checking the current price of a stock. For demonstration purposes this Lambda function simply returns a random integer between 0 and 100 as the stock price. Parameters ---------- event: dict, required Input event to the Lambda function context: object, required Lambda Context runtime methods and attributes Returns ------ dict: Object containing the current price of the stock """ # Check current price of the stock stock_price = randint( 0, 100 ) # Current stock price is mocked as a random integer between 0 and 100 return {"stock_price": stock_price}
ソースコードを見ると、2つ目のステートBuy or Sell?
の入力は、1つ目のステートCheck Stock Value
で実行したLambdaの戻り値そのままであることがわかります。
return {"stock_price": stock_price}
特に何もしていない場合、入力パラメータの値(=$
)は、良くも悪くももステートの結果で書き変わることがわかります。
Lambdaを実行するステートなら、Lambdaの戻り値がそのまま次のステートの入力パラメータとなります。
入力パラメータが書きかわらないようにするには
ステートの結果で入力パラメータが書きかわらないようにするには、ResultPath
プロパティを使い、結果を別の場所に書き込むようにします。
ResultPath
プロパティの使い方は、下記の記事で触れているのでご覧ください。
まとめ
AWS SAMを使って、AWS Step Functionsのステートマシンを構築する方法を紹介しました。
実は、AWS Step Functionsのステートマシンは、CloudFormationでも作ることができますが、AWS SAMを使うと、ステートマシンの定義をJSONファイルで書けるので、より簡単に作成できます。
次回はAWS Step FunctionsでECSのタスクを実行する方法を紹介します。
兼安 聡(執筆記事の一覧)
アプリケーションサービス部 DS3課所属
2024 Japan AWS Top Engineers (Database)
2024 Japan AWS All Certifications Engineers
認定スクラムマスター
広島在住です。今日も明日も修行中です。