💨

Goで体験するAWS SAMとStep Functions:簡単なステップから学ぶ

2023/08/27に公開

はじめに

Step Functionsを実際に操作して理解を深めるために、この記事を執筆しました!
AWS SAMを活用することで、容易に実行環境をセットアップでき、手軽に試すことが可能です。この環境で、Step Functionsの構築手順について詳細に解説していきます。sam initコマンドのテンプレートオプションには、Step Functionsも含まれているので、このテンプレートを選択します。
そのため、Step Functionsの知識がない方でも、ゼロから実践する必要がないため、安心して取り組むことができます!

もしStep Functionsに関する理解が不十分な場合は、以下の記事が参考になると思います。
https://dev.classmethod.jp/articles/aws-step-functions-for-beginner/

準備(sam init)

sam initコマンドを使用して、サーバーレスアプリケーションを初期化します。
今回はアプリケーション名を「sam-step-functions」としました。
「Choose an AWS Quick Start application template」という質問が表示され、実装済みのStep Functionsが含まれているテンプレートを選択します。具体的には「4 - Multi-step workflow」を選択します。

$ 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 - Full Stack
	13 - Lambda EFS example
	14 - Hello World Example With Powertools
	15 - DynamoDB Example
	16 - Machine Learning
Template: 4

Which runtime would you like to use?
	1 - dotnet6
	2 - go1.x
	3 - go (provided.al2)
	4 - java17
	5 - java11
	6 - java8.al2
	7 - java8
	8 - nodejs18.x
	9 - nodejs16.x
	10 - nodejs14.x
	11 - nodejs12.x
	12 - python3.9
	13 - python3.8
	14 - python3.7
	15 - python3.11
	16 - python3.10
	17 - ruby3.2
	18 - ruby2.7
Runtime: 3

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 mod.
We will proceed copying the template using mod.

Would you like to enable X-Ray tracing on the function(s) in your application?  [y/N]: 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]: y
AppInsights monitoring may incur additional cost. View https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/appinsights-what-is.html#appinsights-pricing for more details

Project name [sam-app]: sam-step-functions

    -----------------------
    Generating application:
    -----------------------
    Name: sam-step-functions
    Runtime: go (provided.al2)
    Architectures: x86_64
    Dependency Manager: mod
    Application Template: step-functions-sample-app
    Output Directory: .
    Configuration file: sam-step-functions/samconfig.toml
    
    Next steps can be found in the README file at sam-step-functions/README.md
        

Commands you can use next
=========================
[*] Create pipeline: cd sam-step-functions && sam pipeline init --bootstrap
[*] Validate SAM template: cd sam-step-functions && sam validate
[*] Test Function in the Cloud: cd sam-step-functions && sam sync --stack-name {stack-name} --watch


SAM CLI update available (1.95.0); (1.93.0 installed)
To download: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html

フォルダ構成

今回作成した「sam-step-functions」フォルダの構成は以下の通りです。

|--Makefile
|--README.md
|--functions
|  |--stockBuyer
|  |  |--go.mod
|  |  |--go.sum
|  |  |--main.go
|  |  |--main_test.go
|  |--stockChecker
|  |  |--go.mod
|  |  |--go.sum
|  |  |--main.go
|  |  |--main_test.go
|  |--stockSeller
|  |  |--go.mod
|  |  |--go.sum
|  |  |--main.go
|  |  |--main_test.go
|--samconfig.toml
|--statemachine
|  |--stockTrader.asl.json
|--template.yaml

AWS SAMテンプレート

初期化後に生成されるテンプレートです。

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  sam-step-functions

  Sample SAM Template for sam-step-functions

Resources:
  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/stockTrader.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
            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

  StockCheckerFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html
    Metadata:
      BuildMethod: go1.x
    Properties:
      CodeUri: functions/stockChecker/
      Handler: bootstrap
      Runtime: provided.al2
      Architectures:
      - x86_64

  StockSellerFunction:
    Type: AWS::Serverless::Function
    Metadata:
      BuildMethod: go1.x
    Properties:
      CodeUri: functions/stockSeller/
      Handler: bootstrap
      Runtime: provided.al2
      Architectures:
      - x86_64

  StockBuyerFunction:
    Type: AWS::Serverless::Function
    Metadata:
      BuildMethod: go1.x
    Properties:
      CodeUri: functions/stockBuyer/
      Handler: bootstrap
      Runtime: provided.al2
      Architectures:
      - x86_64

  TransactionTable:
    Type: AWS::Serverless::SimpleTable # More info about SimpleTable Resource: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-simpletable.html
    Properties:
      PrimaryKey:
        Name: Id
        Type: String
      ProvisionedThroughput:
        ReadCapacityUnits: 1
        WriteCapacityUnits: 1

  ApplicationResourceGroup:
    Type: AWS::ResourceGroups::Group
    Properties:
      Name:
        Fn::Sub: ApplicationInsights-SAM-${AWS::StackName}
      ResourceQuery:
        Type: CLOUDFORMATION_STACK_1_0
  ApplicationInsightsMonitoring:
    Type: AWS::ApplicationInsights::Application
    Properties:
      ResourceGroupName:
        Ref: ApplicationResourceGroup
      AutoConfigurationEnabled: 'true'
Outputs:
  # StockTradingStateMachineHourlyTradingSchedule is an implicit Schedule event rule created out of Events key under Serverless::StateMachine
  # Find out more about other implicit resources you can reference within SAM
  # https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-generated-resources.html
  StockTradingStateMachineArn:
    Description: Stock Trading State machine ARN
    Value: !Ref StockTradingStateMachine
  StockTradingStateMachineRoleArn:
    Description: IAM Role created for Stock Trading State machine based on the specified
      SAM Policy Templates
    Value: !GetAtt StockTradingStateMachineRole.Arn

全体図

初期化が完了した後のアプリ構成図です。

【図の参照先】https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/tutorial-state-machine-using-sam.html
この構成では、事前定義されたスケジュールに基づいてワークフローが実行されますが、スケジュールはデフォルトで無効になっており、これにより手数料の発生を回避することができます。

AWS SAMテンプレートの解説

まず、template.yaml内でのStep Functionsに関連する部分を解説します。

Step Functions ステートマシンの定義

  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/stockTrader.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
            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プロパティを使用して、AWS SAMテンプレートの外部にあるステートマシンのワークフロー定義を指定しています。また、DefinitionSubstitutionsプロパティを使用して、ワークフロー内で定義された変数(${LambdaFunction})と、具体的な値との対応関係をマッピングしています。EventsセクションではEvent Bridgeをトリガーとして指定しています。これにより、1時間ごとにスケジュールされるイベントが発生しますが、初期状態では無効となっています。さらに、Policiesセクションでは、各Lambda関数に対する実行権限を与えるポリシーが定義されています。

ワークフローの内容解説

ワークフローははstatemachine/stockTrader.asl.jsonに記述されています。

ワークフロー
stockTrader.asl.json
{
    "Comment": "A state machine that does mock stock trading.",
    "StartAt": "Check Stock Value",
    "States": {
        "Check Stock Value": {
            "Type": "Task",
            "Resource": "${StockCheckerFunctionArn}",
            "Retry": [
                {
                    "ErrorEquals": [
                        "States.TaskFailed"
                    ],
                    "IntervalSeconds": 15,
                    "MaxAttempts": 5,
                    "BackoffRate": 1.5
                }
            ],
            "Next": "Buy or Sell?"
        },
        "Buy or Sell?": {
            "Type": "Choice",
            "Choices": [
                {
                    "Variable": "$.stockPrice",
                    "NumericLessThanEquals": 50,
                    "Next": "Buy Stock"
                }
            ],
            "Default": "Sell Stock"
        },
        "Sell Stock": {
            "Type": "Task",
            "Resource": "${StockSellerFunctionArn}",
            "Retry": [
                {
                    "ErrorEquals": [
                        "States.TaskFailed"
                    ],
                    "IntervalSeconds": 2,
                    "MaxAttempts": 3,
                    "BackoffRate": 1
                }
            ],
            "Next": "Record Transaction"
        },
        "Buy Stock": {
            "Type": "Task",
            "Resource": "${StockBuyerFunctionArn}",
            "Retry": [
                {
                    "ErrorEquals": [
                        "States.TaskFailed"
                    ],
                    "IntervalSeconds": 2,
                    "MaxAttempts": 3,
                    "BackoffRate": 1
                }
            ],
            "Next": "Record Transaction"
        },
        "Record Transaction": {
            "Type": "Task",
            "Resource": "${DDBPutItem}",
            "Parameters": {
                "TableName": "${DDBTable}",
                "Item": {
                    "Id": {
                        "S.$": "$.id"
                    },
                    "Type": {
                        "S.$": "$.transactionType"
                    },
                    "Price": {
                        "N.$": "$.price"
                    },
                    "Quantity": {
                        "N.$": "$.qty"
                    },
                    "Timestamp": {
                        "S.$": "$.timestamp"
                    }
                }
            },
            "Retry": [
                {
                    "ErrorEquals": [
                        "States.TaskFailed"
                    ],
                    "IntervalSeconds": 20,
                    "MaxAttempts": 5,
                    "BackoffRate": 10
                }
            ],
            "End": true
        }
    }
}

ワークフローをプレビューした際のイメージ図は以下の通りです。
VSCode上のStep Functionsのアイコン(Render state machine graph)をクリックするとワークフローを可視化することができます。

ワークフローの主な処理内容について

  • Check Stock Value (株価チェック):

    • タイプ:Task
    • StockCheckerFunctionArn で指定されたリソースを実行(株価をチェックする関数)
    • エラー時には最大5回までリトライする
    • 次のステップ:"Buy or Sell?"
  • Buy or Sell? (買うか売るか選択):

    • タイプ:Choice
    • $stockPrice の値が50以下の場合、「Buy Stock」ステップに進む。それ以外の場合は「Sell Stock」ステップに進む。
  • Buy Stock (株を買う):

    • タイプ:Task
    • StockBuyerFunctionArn で指定されたリソースを実行(株を購入する関数)
    • エラー時には最大3回までリトライする
    • 次のステップ:"Record Transaction"
  • Sell Stock (株を売る):

    • タイプ:Task
    • StockSellerFunctionArn で指定されたリソースを実行(株を売却する関数)
    • エラー時には最大3回までリトライする
    • 次のステップ:"Record Transaction"
  • Record Transaction (取引を記録):

    • タイプ:Task
    • DDBPutItem で指定されたリソースを実行(DynamoDBテーブルに取引情報を記録する関数)
    • エラー時には最大5回までリトライする
    • ワークフローの終了

sam deploy

デプロイにはsam deployコマンドを使用します。
初回の場合、デプロイ先がSAMに設定されていないため、"--guided"オプションを使用して、必要なパラメータを対話形式で設定できます。

$ sam deploy --guided

Setting default arguments for 'sam deploy'
        =========================================
        Stack Name [sam-step-functions]: 
        AWS Region [ap-northeast-1]: 
        #Shows you resources changes to be deployed and require a 'Y' to initiate deploy
        Confirm changes before deploy [Y/n]: Y
        #SAM needs permission to be able to create roles to connect to the resources in your template
        Allow SAM CLI IAM role creation [Y/n]: Y
        #Preserves the state of previously provisioned resources when an operation fails
        Disable rollback [y/N]: y
        Save arguments to configuration file [Y/n]: Y
        SAM configuration file [samconfig.toml]: 
        SAM configuration environment [default]: 

変更されるリソースの一覧が表示されます。変更セットを承認すれば、デプロイが開始されます。

CloudFormation stack changeset
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Operation                                        LogicalResourceId                                ResourceType                                     Replacement                                    
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Add                                            ApplicationInsightsMonitoring                    AWS::ApplicationInsights::Application            N/A                                            
+ Add                                            ApplicationResourceGroup                         AWS::ResourceGroups::Group                       N/A                                            
+ Add                                            StockBuyerFunctionRole                           AWS::IAM::Role                                   N/A                                            
+ Add                                            StockBuyerFunction                               AWS::Lambda::Function                            N/A                                            
+ Add                                            StockCheckerFunctionRole                         AWS::IAM::Role                                   N/A                                            
+ Add                                            StockCheckerFunction                             AWS::Lambda::Function                            N/A                                            
+ Add                                            StockSellerFunctionRole                          AWS::IAM::Role                                   N/A                                            
+ Add                                            StockSellerFunction                              AWS::Lambda::Function                            N/A                                            
+ Add                                            StockTradingStateMachineHourlyTradingScheduleR   AWS::IAM::Role                                   N/A                                            
                                                 ole                                                                                                                                              
+ Add                                            StockTradingStateMachineHourlyTradingSchedule    AWS::Events::Rule                                N/A                                            
+ Add                                            StockTradingStateMachineRole                     AWS::IAM::Role                                   N/A                                            
+ Add                                            StockTradingStateMachine                         AWS::StepFunctions::StateMachine                 N/A                                            
+ Add                                            TransactionTable                                 AWS::DynamoDB::Table                             N/A                                            
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Changeset created successfully. arn:aws:cloudformation:ap-northeast-1:************:changeSet/samcli-deploy1693122309/df09c882-d104-430d-a226-ca0f9b30e55e


Previewing CloudFormation changeset before deployment
======================================================
Deploy this changeset? [y/N]: 

作成されたリソースなどが表示され、しばらくするとデプロイが完了した旨のメッセージが表示されます。

2023-08-27 16:49:17 - Waiting for stack create/update to complete

CloudFormation events from stack operations (refresh every 5.0 seconds)
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ResourceStatus                                   ResourceType                                     LogicalResourceId                                ResourceStatusReason                           
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
CREATE_IN_PROGRESS                               AWS::IAM::Role                                   StockSellerFunctionRole                          -                                              
CREATE_IN_PROGRESS                               AWS::IAM::Role                                   StockCheckerFunctionRole                         -                                              
CREATE_IN_PROGRESS                               AWS::ResourceGroups::Group                       ApplicationResourceGroup                         -    

〜(省略)〜

Successfully created/updated stack - sam-step-functions in ap-northeast-1

作成されたリソースの確認

  1. Lambda
    正常に作成されていることが確認できます。

  2. DynamoDB
    正常に作成されていることが確認できます。

  3. Step functions
    正常に作成されていることが確認できます。
    のちほどコンソール画面上から動作確認していきます。

Step Functions ステートマシンの動作確認

作成されたステートマシンの詳細画面にアクセスし、"定義"タブを選択すると、ワークフローを確認することができます。

動作確認を行うため、画面右上の「実行を開始」ボタンをクリックします。
ボタンをクリックすると、開始画面が表示され、JSON形式の入力パラメータを求められます。
通常は、ワークフロー内で定義した変数に対して値を指定する必要がありますが、今回はそのまま進めていきます。
(例えば、株の売買の判定変数である"StockPrice"には、0から99までのランダムな数字が設定されるようになっています。)

【株売却】の結果
詳細画面の「グラフビュー」タブを選択すると、各ステップの動作を詳細に確認することができます。
成功したステップは緑色で表示されます。

【株購入】の結果

株の取引が行われた後、取引データがDynamoDBに正しく登録されていることを確認します。

データが登録されていることが確認できました。

Discussion