AWS Step Functionsステートマシーンのcron定期実行をServerless Frameworkで実装する

三度の飯より飯が好き🍚。どうも私です。

キラメックスでは、機械学習のTrainingや定期的な予測出力にAWS Step Functionsを使用しています。
今回は、Serverless Frameworkを使ってAWS Step Functionsのステートマシーンを作成・実行する方法を紹介します。

前提

  • Serverless Frameworkを使って実装していきます。
  • 諸々インストール、設定済みとします。

本記事でできること

  1. Lambda Functionを作ります。
  2. ステートマシーンに付与するIAMロールを作成します。
  3. Step Functionsのステートマシーンを作ります。このステートマシーン内では1.で作成したLambda Functionを実行します
  4. Step Functionsのステートマシーンを実行するためのIAMロールを作ります。
  5. Amazon EventBridgeのイベントルールを作ります。このイベントでは3.で作成したステートマシーンを4.のロールでcron実行します。

上記をServerless Frameworkで実装してリソースを作成します。

やってみる

プロジェクト作成

sf-exampleという名前でプロジェクトを作りました。

$ sls create -t aws-python -p sf-example

sf-exampleというフォルダが作成され、その下にhandler.pyserverless.ymlが作成されます。以降の作業はすべてsf-exampleフォルダの下で実施します。

Lambda関数の定義

Lambdaの関数はhandler.pyの自動生成されたコードを使うことにするので今回は特に編集はしません。 serverless.ymlを以下の通りに編集しhelloという関数を定義します。

service: sf-example
frameworkVersion: '2'

provider:
  name: aws
  runtime: python3.8
  region: ap-northeast-1

functions:
  hello:
    handler: handler.hello

ステートマシーンに付与するIAMロールを定義

serverless.ymlに以下を追記します。

resources:
  Resources:
    StepFunctionsExecutionRole:
      Type: AWS::IAM::Role
      Properties:
        Description: StepFunctions Example
        RoleName: StepFunctionsExample_Execution_Role
        AssumeRolePolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Principal:
                Service:
                - states.amazonaws.com
              Action:
                - 'sts:AssumeRole'
        Path: '/service-role/'
        Policies:
          - PolicyName: XRayAccess_Example_Policy
            PolicyDocument:
              Version: '2012-10-17'
              Statement:
                - Effect: Allow
                  Action: 
                    - xray:PutTraceSegments
                    - xray:PutTelemetryRecords
                    - xray:GetSamplingRules
                    - xray:GetSamplingTargets
                  Resource: '*'
          - PolicyName: InvokeLambdaFunction_Example_Policy
            PolicyDocument:
              Version: '2012-10-17'
              Statement:
                - Effect: Allow
                  Action: lambda:InvokeFunction
                  Resource: '*'

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

まずは以下のコマンドでStep Functionsのプラグインをインストールします。

$ npm install serverless-step-functions  

インストールしたプラグインについてserverless.ymlに以下を追記します。

plugins:
  - serverless-step-functions

package:
  exclude:
    - node_modules/**

続いてserverless.ymlにステートマシーンの定義を追記します。

stepFunctions:
  stateMachines:
    StateMachineExample:
      name: StateMachineExample
      role:
        Fn::GetAtt: [StepFunctionsExecutionRole, Arn]
      definition:
        Comment: Step Functions Example
        StartAt: Hello Function
        States:
          Hello Function:
            Type: Task
            Resource:
              Fn::GetAtt: [hello, Arn]
            End: True

roleには、前の手順で定義したロールを指定しています。 また、Hello FunctionというTaskの中でLambdaのhello関数を指定しています。

ステートマシーンを実行するためのIAMロール作成

EventBridgeからステートマシーンを実行する際のIAMロールを作成します。

serverless.ymlResources:に追記していきます。

resources:
  Resources:
    StepFunctionsExecutionRole:
  〜 略 〜
    InvokeStepFunctionsIamRoleExample:
      Type: AWS::IAM::Role
      Properties:
        Description: StepFunctions Example
        RoleName: Invoke_StepFunctionsExample_Role
        AssumeRolePolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Principal:
                Service:
                - events.amazonaws.com
              Action:
                - sts:AssumeRole
        Path: '/service-role/'
        Policies:
          - PolicyName: StartExecution_Policy
            PolicyDocument:
              Version: '2012-10-17'
              Statement:
                - Effect: Allow
                  Action: states:StartExecution
                  Resource: 
                    Fn::GetAtt: [StateMachineExample, Arn]

Amazon EventBridgeのルールを定義

最後に、cronでStep Functionsのステートマシーンを実行するイベントルールを定義します。 ここではcronで毎日15:00(JST)に実行するようにしています。

serverless.ymlResources:に追記していきます。

resources:
  Resources:
    StepFunctionsExecutionRole:
  〜 略 〜
    InvokeStepFunctionsIamRoleExample:
  〜 略 〜
    InvokeStepFunctionsEventExample:
      Type: AWS::Events::Rule
      Properties: 
        Description: StepFunctions Example
        Name: Invoke_StepFunctionsExample_Event
        ScheduleExpression: 'cron(0 6 * * ? *)' # 日本時間の15:00(毎日)
        State: ENABLED
        Targets: 
          - 
            Arn: 
              Fn::GetAtt: [StateMachineExample, Arn]
            Id: StateMachineExample
            RoleArn: 
              Fn::GetAtt: [InvokeStepFunctionsIamRoleExample, Arn]

デプロイ

以下のコマンドでデプロイします。

$ sls deploy 

デプロイが成功すると以下のリソースが新しく作成されるので、マネージメントコンソールにログインして確認してみてください。

  • CloudFormationのスタック
  • Lambdaの関数
  • IAMロール(2種類)
  • Step Functionsのステートマシーン
  • EventBridgeのルール

cronで指定した時刻になると、StepFunctionsのステートマシーンが実行されます!

実行結果を見るとこのように成功していることが確認できます。 f:id:mtr-krmx:20210416181654p:plain

リソースの削除

作成したリソースをすべて削除したい場合は以下のコマンドを実行します。

$ sls remove 

終わりに

最後にserverless.ymlの全体を載せておきます。

service: sf-example
frameworkVersion: '2'

provider:
  name: aws
  runtime: python3.8
  region: ap-northeast-1

plugins:
  - serverless-step-functions

package:
  exclude:
    - node_modules/**

functions:
  hello:
    handler: handler.hello

stepFunctions:
  stateMachines:
    StateMachineExample:
      name: StateMachineExample
      role:
        Fn::GetAtt: [StepFunctionsExecutionRole, Arn]
      definition:
        Comment: Step Functions Example
        StartAt: Hello Function
        States:
          Hello Function:
            Type: Task
            Resource:
              Fn::GetAtt: [hello, Arn]
            End: True

resources:
  Resources:
    StepFunctionsExecutionRole:
      Type: AWS::IAM::Role
      Properties:
        Description: StepFunctions Example
        RoleName: StepFunctionsExample_Execution_Role
        AssumeRolePolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Principal:
                Service:
                - states.amazonaws.com
              Action:
                - 'sts:AssumeRole'
        Path: '/service-role/'
        Policies:
          - PolicyName: XRayAccess_Example_Policy
            PolicyDocument:
              Version: '2012-10-17'
              Statement:
                - Effect: Allow
                  Action: 
                    - xray:PutTraceSegments
                    - xray:PutTelemetryRecords
                    - xray:GetSamplingRules
                    - xray:GetSamplingTargets
                  Resource: '*'
          - PolicyName: InvokeLambdaFunction_Example_Policy
            PolicyDocument:
              Version: '2012-10-17'
              Statement:
                - Effect: Allow
                  Action: lambda:InvokeFunction
                  Resource: '*'
    InvokeStepFunctionsIamRoleExample:
      Type: AWS::IAM::Role
      Properties:
        Description: StepFunctions Example
        RoleName: Invoke_StepFunctionsExample_Role
        AssumeRolePolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Principal:
                Service:
                - events.amazonaws.com
              Action:
                - sts:AssumeRole
        Path: '/service-role/'
        Policies:
          - PolicyName: StartExecution_Policy
            PolicyDocument:
              Version: '2012-10-17'
              Statement:
                - Effect: Allow
                  Action: states:StartExecution
                  Resource: 
                    Fn::GetAtt: [StateMachineExample, Arn]
    InvokeStepFunctionsEventExample:
      Type: AWS::Events::Rule
      Properties: 
        Description: StepFunctions Example
        Name: Invoke_StepFunctionsExample_Event
        ScheduleExpression: 'cron(0 6 * * ? *)' # 日本時間の15:00(毎日)
        State: ENABLED
        Targets: 
          - 
            Arn: 
              Fn::GetAtt: [StateMachineExample, Arn]
            Id: StateMachineExample
            RoleArn: 
              Fn::GetAtt: [InvokeStepFunctionsIamRoleExample, Arn]