🔖

SAMでAWS Step Functionsを試してみる

2023/09/20に公開

はじめに

Lambdaは使ったことあるのですが、StepFunctionを使ったことが無いので、サンプルプログラムで雰囲気をつかんでみたいと思います。

下記の公式チュートリアルも見つつ進めていきます。
https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/tutorial-state-machine-using-sam.html

実行環境

Step FunctionsはAWS SAM(Serverless Application Model)で作っていきます。

ランタイムはGoにします。
Goのバージョンは元々1.xだけだったのですが、いつの間にかprovided.al2が出来ていました。
1.xは今年一杯で廃止予定のようなので、provided.al2一択ですね。
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/go-image.html

事前準備

samコマンドで簡単にサーバレスアプリケーションの構成とビルド、デプロイができるので非常に便利です。

  • samコマンドを使うにはAWS CLIが必要なのでインストールします。
    https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html

  • デプロイに必要な権限を割り当てておく
    デプロイ時は、CloudFormationによってデプロイが実施されます。
    デプロイを実行する権限は、credentialsに定義されたアカウントを指定しますが、
    そのアカウントにデプロイ時に必要な権限が揃っていないと途中でエラーになります。

環境設定

sam initで対話式でテンプレートのセットアップができます。

  1. AWS Quick Start Templates
    1 - AWS Quick Start Templates
  2. Choose an AWS Quick Start application template
    4 - Multi-step workflow
  3. Which runtime would you like to use?
    3 - go (provided.al2)
  4. 他はデフォルトでOK
  5. 最後にプロジェクト名を聞かれるので、sam-step-function-sampleとしました。
    Project name [sam-app]: sam-step-function-sample
$ 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 - java17
        5 - java11
        6 - java8.al2
        7 - java8
        8 - nodejs18.x
        9 - nodejs16.x
        10 - nodejs14.x
        11 - python3.9
        12 - python3.8
        13 - python3.7
        14 - python3.11
        15 - python3.10
        16 - ruby3.2
        17 - 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]: 

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]: 

Project name [sam-app]: sam-step-function-sample

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


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

コマンドを実行したディレクトリに先ほど指定したプロジェクト名でディレクトリが作成されます。

出来上がったディレクトリ構成

sam-step-function-sample
├── 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
  • functions・・・lambda関数の実装が入っています。
  • samconfig.toml・・・デプロイ時に使用する設定ファイルです。
  • statemachine・・・ステートマシーンの定義ファイルが入っています。
  • template.yaml・・・SAMでデプロイされるlambda等のリソース定義が入っています。

ビルド

$ sam build

Starting Build use cache

Cache is invalid, running build and copying resources for following functions (StockCheckerFunction)

Building codeuri: /path/to/directory/sam-step-function-sample/functions/stockChecker runtime: provided.al2 metadata: {'BuildMethod': 'go1.x'} architecture: x86_64 functions:
StockCheckerFunction

Cache is invalid, running build and copying resources for following functions (StockBuyerFunction)

Cache is invalid, running build and copying resources for following functions (StockSellerFunction)

Building codeuri: /path/to/directory/sam-step-function-sample/functions/stockBuyer runtime: provided.al2 metadata: {'BuildMethod': 'go1.x'} architecture: x86_64 functions:  
StockBuyerFunction

Building codeuri: /path/to/directory/sam-step-function-sample/functions/stockSeller runtime: provided.al2 metadata: {'BuildMethod': 'go1.x'} architecture: x86_64 functions: 
StockSellerFunction

 Running GoModulesBuilder:Build

 Running GoModulesBuilder:Build

 Running GoModulesBuilder:Build


Build Succeeded

Built Artifacts  : .aws-sam/build
Built Template   : .aws-sam/build/template.yaml

Commands you can use next
=========================
[*] Validate SAM template: sam validate
[*] Invoke Function: sam local invoke
[*] Test Function in the Cloud: sam sync --stack-name {{stack-name}} --watch
[*] Deploy: sam deploy --guided

デプロイ

デプロイします。
※ログは適度に余分なスペースを取り除いてます。

$ sam deploy --guided

Configuring SAM deploy
======================

Looking for config file [samconfig.toml]: Found
Reading default arguments             : Success

Setting default arguments for 'sam deploy'
=========================================
Stack Name [sam-step-function-sample]: 
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]: 
#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]: 
#Preserves the state of previously provisioned resources when an operation fails
Disable rollback [y/N]: 
Save arguments to configuration file [Y/n]: 
SAM configuration file [samconfig.toml]: 
SAM configuration environment [default]: 

Looking for resources needed for deployment:

Managed S3 bucket: aws-sam-cli-managed-default-samclisourcebucket-1491q9q7zj9a6
A different default S3 bucket can be set in samconfig.toml and auto resolution of buckets turned off by setting resolve_s3=False

Parameter "stack_name=sam-step-function-sample" in [default.deploy.parameters] is defined as a global parameter [default.global.parameters].                          
This parameter will be only saved under [default.global.parameters] in /path/to/directory/sam-step-function-sample/samconfig.toml.   

Saved arguments to config file
Running 'sam deploy' for future deployments will use the parameters saved above.
The above parameters can be changed by modifying samconfig.toml
Learn more about samconfig.toml syntax at 
https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-config.html

Deploying with following values
===============================
Stack name                   : sam-step-function-sample
Region                       : ap-northeast-1
Confirm changeset            : True
Disable rollback             : False
Deployment s3 bucket         : aws-sam-cli-managed-default-samclisourcebucket-1491q9q7zj9a6
Capabilities                 : ["CAPABILITY_IAM"]
Parameter overrides          : {}
Signing Profiles             : {}

Initiating deployment
=====================

Waiting for changeset to be created..

CloudFormation stack changeset
----------------------------------------------------------------------------
Operation    LogicalResourceId                                  ResourceType                           Replacement
----------------------------------------------------------------------------
+ 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        StockTradingStateMachineHourlyTradingScheduleRole  AWS::IAM::Role                         N/A
+ 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

〜〜〜 中略 〜〜〜

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

途中CloudFormationでの進捗状況が出力されます。
完了した分のログのみ抽出すると以下のようになっています。
これらがデプロイによって作成されたリソースとなっています。

CloudFormation events from stack operations (refresh every 5.0 seconds)
----------------------------------------------------------------------
ResourceStatus     ResourceType                       LogicalResourceId                                  ResourceStatusReason
----------------------------------------------------------------------
CREATE_COMPLETE    AWS::DynamoDB::Table               TransactionTable                                   -
CREATE_COMPLETE    AWS::IAM::Role                     StockCheckerFunctionRole                           -
CREATE_COMPLETE    AWS::IAM::Role                     StockBuyerFunctionRole                             -
CREATE_COMPLETE    AWS::IAM::Role                     StockSellerFunctionRole                            -
CREATE_COMPLETE    AWS::Lambda::Function              StockBuyerFunction                                 -
CREATE_COMPLETE    AWS::Lambda::Function              StockCheckerFunction                               -
CREATE_COMPLETE    AWS::Lambda::Function              StockSellerFunction                                -
CREATE_COMPLETE    AWS::IAM::Role                     StockTradingStateMachineRole                       -
CREATE_COMPLETE    AWS::StepFunctions::StateMachine   StockTradingStateMachine                           -
CREATE_COMPLETE    AWS::IAM::Role                     StockTradingStateMachineHourlyTradingScheduleRole  -
CREATE_COMPLETE    AWS::Events::Rule                  StockTradingStateMachineHourlyTradingSchedule      -
CREATE_COMPLETE    AWS::CloudFormation::Stack         sam-step-function-sample                           -

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

cloud formation

lambda

作成されたlambdaはアプリケーションとしてまとめられて作成されています。

Step Functions

真ん中のフローチャートのような図がワークフローと言う、何が動作するかがなんとなく分かる図になっています。
それぞれの四角がステートといわれ、lambdaだったり判断条件だったりが設定できるようです。
DBもステートとして設定できるのもすごいですね。
ステートとステートの繋がりは次のステートを何にするかを指定することで設定します。

json定義

{
  "Comment": "A state machine that does mock stock trading.",
  "StartAt": "Check Stock Value",
  "States": {
    "Check Stock Value": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:ap-northeast-1:214536320901:function:sam-step-function-sample-StockCheckerFunction-qHTfdO2NslqG",
      "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": "arn:aws:lambda:ap-northeast-1:214536320901:function:sam-step-function-sample-StockSellerFunction-1XygAt1w6ObC",
      "Retry": [
        {
          "ErrorEquals": [
            "States.TaskFailed"
          ],
          "IntervalSeconds": 2,
          "MaxAttempts": 3,
          "BackoffRate": 1
        }
      ],
      "Next": "Record Transaction"
    },
    "Buy Stock": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:ap-northeast-1:214536320901:function:sam-step-function-sample-StockBuyerFunction-B9IleRhw0Gqh",
      "Retry": [
        {
          "ErrorEquals": [
            "States.TaskFailed"
          ],
          "IntervalSeconds": 2,
          "MaxAttempts": 3,
          "BackoffRate": 1
        }
      ],
      "Next": "Record Transaction"
    },
    "Record Transaction": {
      "Type": "Task",
      "Resource": "arn:aws:states:::dynamodb:putItem",
      "Parameters": {
        "TableName": "sam-step-function-sample-TransactionTable-1C4YWNMRDFRPA",
        "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
    }
  }
}

ちなみに、コンソールでステートマシンを作ると

EventBridge

リソース見なかったら見逃してましたが、
1時間おきの起動でスケジューラが作成されていました。
ただ、テンプレートだからなのか、有効化はされていないので、勝手に動き出すということは無いようです。

DynamoDB

上記以外にこれらのリソースを統合するための権限も作られています。

テスト

テストはステートマシーンの実行で行います。


「実行を開始」をクリックし、

入力はデフォルトのままいってみます。

成功です!

通過したところが分かるのと、成功失敗も分かりそうです。
緑の箇所をクリックすると、右側に実行された際の入力や出力、詳細情報が確認できます。

というわけで、無事動作確認までできました。

おわりに

SAMで簡単にStep Functionsを作成することができました。
Step Functionsの肝はステートマシンの設定まわりだと思いますが、
このチュートリアルでできた設定を見ながら、感覚を掴んでいきたいと思いました。

今回作成したStep Functionのテンプレートは以下リポジトリで公開しています。
https://github.com/KaT0819/aws-step-function-sample

下記のリンクには他にもStep Functionsのチュートリアルがあるので、こちらも参考になると思います。
https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/tutorials.html

Discussion