Step Functionsの構築はAWS SAMを利用すると捗りそうです

Step Functionsを構築する時は、AWS SAMを積極的に利用していきたいと思いました。
2020.06.24

AWS Serverless Application Model(以下、AWS SAM)は、サーバーレスアプリケーションのデプロイに特化した、CloudFormation(以下、CFn)の拡張機能です。

AWS SAMもテンプレートにリソースを定義し、デプロイの際はCFnが使用されます。AWS SAMはCFnに比べ、テンプレートを簡潔に定義できるのが特徴だったりします。

そんな、AWS SAMで、先日Step Functionsがサポートされました。

これにより、Lambda Functionとあわせ、呼び出し元(Step Functions)が定義できるようになりました。また、Step FunctionsのワークフローをAWS SAMテンプレート外に定義することで、Amazon States Language(以下、ASL)で記述できるので、AWS SAMを利用した構築に際し、新たな書式を覚える必要がありません。

AWS SAMでStep Functionsを構築したところ、積極的に利用していきたいと思いましたので、使用感などお伝えしたいと思います。

前提

AWS SAMでStep Functionsを扱うためには、AWS SAM CLI バージョン0.52.0以上が必要になります。AWS SAM CLIのインストールについては、以下を参照ください。

本エントリではAWS SAMについての詳細は割愛していますので、AWS SAMを詳しく求むという方は以下を参照してください。

やってみた

今回はLambda Functionを呼び出す、シンプルなステートマシンを構築してみたいと思います。

以下のような構成で、Lambda Functionのコードはfunctionsディレクトリ配下のapp.pyで定義しました。ステートマシンのワークフローは、AWS SAMテンプレート内で定義するのではなくstatemachineディレクトリ配下のsfn.asl.jsonで定義しました。

.
├── functions
│   └── hello_world
│       ├── app.py
│       └── requirements.txt
├── statemachine
│   └── sfn.asl.json
└── template.yaml

それぞれ定義をみていきたいと思います。

ステートマシンから呼び出しされる、Lambda Functionのコードです。

functions/hello_world/app.py

def lambda_handler(event, context):
    print("Hello world")

ステートマシンのワークフローは、AWS SAMテンプレートから外出ししているので、マネジメントコンソールで定義する際と同様、ASL (Amazon States Language) で定義します。呼び出しを行うLambda Funtionの指定は、変数定義にすることで、AWS SAMテンプレート内で動的に指定することができます。

sfn.asl.json

{
  "StartAt": "hello world",
  "States": {
    "hello world": {
      "Type": "Task",
      "Resource": "${LambdaFunction}",
      "End": true
    }
  }
}

AWS SAMテンプレートになります。ハイライトしている箇所がStep Functionsの定義です。

template.yaml

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Parameters:
  StateMachineName:
    Description: Please type the Step Functions StateMachine Name.
    Type: String
    Default: 'sfn-sam-app-statemachine'
  LambdaFunctionName:
    Description: Please type the Lambda Function Name.
    Type: String
    Default: 'sfn-sam-app-function'
Resources:
  LambdaFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: !Sub ${LambdaFunctionName}
      CodeUri: functions/hello_world/
      Handler: app.lambda_handler
      Runtime: python3.8
      Timeout: 60
      Role: !GetAtt LambdaFunctionRole.Arn
  LambdaFunctionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: /
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
        - arn:aws:iam::aws:policy/AWSStepFunctionsReadOnlyAccess
  StateMachine:
    Type: AWS::Serverless::StateMachine
    Properties:
      Name: !Sub ${StateMachineName}
      DefinitionUri: statemachine/sfn.asl.json
      DefinitionSubstitutions:
        LambdaFunction: !GetAtt LambdaFunction.Arn
      Role: !GetAtt StateMachineRole.Arn
      Logging:
        Level: ALL
        IncludeExecutionData: True
        Destinations:
          - CloudWatchLogsLogGroup:
              LogGroupArn: !GetAtt StateMachineLogGroup.Arn
  StateMachineLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName : !Join [ "", [ '/aws/states/', !Sub '${StateMachineName}', '-Logs' ] ]
  StateMachineRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - states.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: /
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaRole
        - arn:aws:iam::aws:policy/CloudWatchLogsFullAccess

簡単に補足します。

DefinitionUriプロパティでAWS SAMテンプレート外に記述したステートマシンのワークフロー定義を指定しています。DefinitionSubstitutionsプロパティで、ワークフロー内に定義した変数(${LambdaFunction})と、定義する値をマッピングしています。

これで構築の準備が完了です。

sam deployでデプロイを行います。オプション--guidedを指定することで、CFnスタック名など、必要なパラメータを対話形式で指定することが可能です。

$ sam deploy --guided

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

        Looking for samconfig.toml :  Not found

        Setting default arguments for 'sam deploy'
        =========================================
        Stack Name [sam-app]: 
        AWS Region [us-east-1]: ap-northeast-1
        Parameter StateMachineName [sfn-sam-app-statemachine]: 
        Parameter LambdaFunctionName [sfn-sam-app-function]: 
        #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]: 
        Save arguments to samconfig.toml [Y/n]: 

プロンプトに従い必要な情報を入力していくと、変更されるリソース等が表示されます。変更セットを許可すればデプロイが開始されます。

Waiting for changeset to be created..

CloudFormation stack changeset
---------------------------------------------------------------------------------------------------------------------------
Operation                                 LogicalResourceId                         ResourceType                            
---------------------------------------------------------------------------------------------------------------------------
+ Add                                     LambdaFunctionRole                        AWS::IAM::Role                          
+ Add                                     LambdaFunction                            AWS::Lambda::Function                   
+ Add                                     StateMachineLogGroup                      AWS::Logs::LogGroup                     
+ Add                                     StateMachineRole                          AWS::IAM::Role                          
+ Add                                     StateMachine                              AWS::StepFunctions::StateMachine        
---------------------------------------------------------------------------------------------------------------------------

Changeset created successfully. arn:aws:cloudformation:ap-northeast-1:XXXXXXXXXXXX:changeSet/samcli-deploy1592909667/0face98a-4e59-4dd9-be36-f9662d24d0c9


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

作成されたリソース等が表示され、しばらくするとデプロイが完了します。

2020-06-23 19:59:05 - Waiting for stack create/update to complete

CloudFormation events from changeset
-------------------------------------------------------------------------------------------------------------------------
ResourceStatus                 ResourceType                   LogicalResourceId              ResourceStatusReason         
-------------------------------------------------------------------------------------------------------------------------
CREATE_IN_PROGRESS             AWS::Logs::LogGroup            StateMachineLogGroup           -                            
CREATE_IN_PROGRESS             AWS::IAM::Role                 StateMachineRole               -                            
CREATE_IN_PROGRESS             AWS::IAM::Role                 LambdaFunctionRole             -                            
CREATE_IN_PROGRESS             AWS::Logs::LogGroup            StateMachineLogGroup           Resource creation Initiated  
CREATE_IN_PROGRESS             AWS::IAM::Role                 StateMachineRole               Resource creation Initiated  
CREATE_IN_PROGRESS             AWS::IAM::Role                 LambdaFunctionRole             Resource creation Initiated  
CREATE_COMPLETE                AWS::Logs::LogGroup            StateMachineLogGroup           -                            
CREATE_COMPLETE                AWS::IAM::Role                 StateMachineRole               -                            
CREATE_COMPLETE                AWS::IAM::Role                 LambdaFunctionRole             -                            
CREATE_IN_PROGRESS             AWS::Lambda::Function          LambdaFunction                 -                            
CREATE_IN_PROGRESS             AWS::Lambda::Function          LambdaFunction                 Resource creation Initiated  
CREATE_COMPLETE                AWS::Lambda::Function          LambdaFunction                 -                            
CREATE_IN_PROGRESS             AWS::StepFunctions::StateMac   StateMachine                   -                            
                               hine                                                                                       
CREATE_COMPLETE                AWS::StepFunctions::StateMac   StateMachine                   -                            
                               hine                                                                                       
CREATE_IN_PROGRESS             AWS::StepFunctions::StateMac   StateMachine                   Resource creation Initiated  
                               hine                                                                                       
CREATE_COMPLETE                AWS::CloudFormation::Stack     sam-app                        -                            
-------------------------------------------------------------------------------------------------------------------------

Successfully created/updated stack - sam-app in ap-northeast-1

デプロイが完了していますので、マネジメントコンソールからもステートマシンの作成を確認することができます。

今回の構成でワークフローを変更する際は、sfn.asl.jsonを更新します。

sfn.asl.json

{
  "StartAt": "hello world",
  "States": {
    "hello world": {
      "Type": "Task",
      "Resource": "${LambdaFunction}",
      "Next": "succeed",
      "Catch": [
        {
          "ErrorEquals": [
            "States.ALL"
          ],
          "Next": "fail"
        }
      ]
    },
    "succeed": {
      "Type": "Succeed"
    },
    "fail": {
      "Type": "Fail",
      "Error": "ErrorCode 100",
      "Cause": "There is Error."
    }
  }
}

再度、デプロイを行えば環境に反映されます。

$ sam deploy

(省略)

CloudFormation stack changeset
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Operation                                                 LogicalResourceId                                         ResourceType                                            
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
* Modify                                                  StateMachine                                              AWS::StepFunctions::StateMachine                        
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------

(省略)

CloudFormation events from changeset
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ResourceStatus                             ResourceType                               LogicalResourceId                          ResourceStatusReason                     
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
UPDATE_IN_PROGRESS                         AWS::StepFunctions::StateMachine           StateMachine                               -                                        
UPDATE_COMPLETE                            AWS::StepFunctions::StateMachine           StateMachine                               -                                        
UPDATE_COMPLETE_CLEANUP_IN_PROGRESS        AWS::CloudFormation::Stack                 sam-app                                    -                                        
UPDATE_COMPLETE                            AWS::CloudFormation::Stack                 sam-app                                    -                                        
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Successfully created/updated stack - sam-app in ap-northeast-1

変更が反映されたことが確認できました。

おまけ

テンプレート内のLambda Functionから、StepFunctionsを操作したいといった時には、 該当するステートマシンのARNをLambda Functionの環境変数に設定したり...なんてことがあると思いますが、今回のような構成でそれをやってしまうと、circular dependencies(循環参照)のエラーとなります。

StepFunctionsでは実行中に使用できるContext オブジェクトがあり、ステートマシンのARNも取得することができます。 循環参照が発生した時でもいいので、Context オブジェクトで値がとれるということを、頭の片隅にでも入れておいていただければ幸いです。

最後に

Step Functionsがサポートされたことにより、Lambda Functionとあわせ、呼び出し元(Step Functions)も定義できますので、 AWS SAMのベストプラクティスにあるような管理が行え、 開発、運用が行いやすくなるのではないでしょうか。

CFnに馴染みのある方であれが、AWS SAM自体はスムーズに利用できると思いますので、Step Functionsを構築する際はAWS SAMの利用も検討してみてはいかがでしょうか。

参考