Serverless Frameworkで構築するStep Functions

これまで、Step Functionsの構築にCFnやCLI等を利用していましたが、Serverless Frameworkを利用したところ、JSONベースのステートメント言語から開放され、サクッと構築できましたので紹介したいと思います。

Serverless FrameworkでStep Functionsを扱うにはプラグインが必要になります。

過去にStep Functionsプラグインを利用したエントリはありますが、プラグイン等もアップデートされていますので改めてやってみた系のエントリになります。

前提

Serverless Framework自体のインストールや、基本的な利用方法については割愛しています。

その辺りから確認されたい方は、以下が参考になると思います。

本エントリでは以下バージョンを利用しています。

  • serverless 1.48.4
  • serverless-step-functions 2.1.0

やってみた

サービス作成

Serverless Frameworkではサービスという単位で複数のリソースを管理しますので、まずはサービスを作成します。

ここでは、サービス用のディレクトリTestStepFunctionsを作成して、サービス名StepFunctionsServiceでランタイムはpython3としました。

$ serverless create --template aws-python3 --name StepFunctionsService --path TestStepFunctions
Serverless: Generating boilerplate...
Serverless: Generating boilerplate in "/Users/sakamaki.kazuyoshi/awscli/sakamaki.kazuyoshi/TestStepFunctions"
 _______                             __
|   _   .-----.----.--.--.-----.----|  .-----.-----.-----.
|   |___|  -__|   _|  |  |  -__|   _|  |  -__|__ --|__ --|
|____   |_____|__|  \___/|_____|__| |__|_____|_____|_____|
|   |   |             The Serverless Application Framework
|       |                           serverless.com, v1.48.4
 -------'

Serverless: Successfully generated boilerplate for template: "aws-python3"

上記コマンドを実行すると、カレントディレクトリにTestStepFunctionsディレクトリが作成され、配下にServerless Frameworkの定義ファイルserverless.ymlと、テンプレートのpythonスクリプトhandler.pyが作成されます。

$ ls -l
total 0
drwxr-xr-x  5 sakamaki.kazuyoshi  staff  160 Jul 28 16:15 TestStepFunctions
$ ls -lR TestStepFunctions
total 16
-rw-r--r--  1 sakamaki.kazuyoshi  staff   497 Jul 28 16:15 handler.py
-rw-r--r--  1 sakamaki.kazuyoshi  staff  3162 Jul 28 16:15 serverless.yml

プラグイン インストール

Step Functionsを利用するために以下のプラグイン導入します。

Serverless Frameworkのプラグインはサービスごとに追加され、グローバルには適用されませんので、サービスのルートディレクトリ(ここではTestStepFunctions)よりインストールを行います。

$ cd TestStepFunctions/
$ npm install --save-dev serverless-step-functions

serverless.yml修正

serverless.ymlはサービス全体の設定を行うためのファイルです。ここでは以下のように定義しました。

service: StepFunctionsService

plugins:
  - serverless-step-functions

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

functions:
  Function1:
    name: TestFunction
    handler: handler.hello

stepFunctions:
  stateMachines:
    StateMachine1:
      name: TestStateMachine
      definition:
        StartAt: HelloWorld
        States:
          HelloWorld:
            Type: Task
            Resource:
              Fn::GetAtt: [Function1LambdaFunction, Arn]
            End: true

Step Functions周りの設定について説明します。pluginsプロパティでStep Functionsのプラグインを指定し、stepFunctionsプロパティにステートマシンを定義していきます。

ここでは、単一のLambda関数を実行するだけのステートマシンを定義しています。

ステートマシンから実行されるLambda関数は定義ファイル内で作成しており、ステートマシンの定義では、実行するLambda関数のARNが必要になるので、CFn組み込み関数(Fn::GetAtt)を使用してARNを取得しています。

定義の詳細については、以下をご確認ください。

余談ですが、Serverless Frameworkでどのようにリソース論理名が作成されるか、マニュアルから読み取れなかったので、Fn::GetAttに指定するリソース論理名はsls package等で生成されるCFnテンプレートから確認しました。functionsプロパティ配下のLambda関数論理名(ここではFunction1)+LambdaFunctionとなっていました。

デプロイ

サービスをデプロイします。

$ sls deploy -v
Serverless: Packaging service...
Serverless: Excluding development dependencies...

(省略)

デプロイが完了すると、CFnスタックが作成され、serverless.ymlで定義したLambda、ステートマシンが作成されています。

スタック

Lambda

ステートマシン

動作確認(ステートマシン実行)

デプロイしたステートマシンをServerless Frameworkより実行します。

$ sls invoke stepf --name StateMachine1
.
{ executionArn:
   'arn:aws:states:ap-northeast-1:XXXXXXXXXXXX:execution:TestStateMachine:e3fde4c1-c416-4be1-83d1-2ac8f7ae0734',
  stateMachineArn:
   'arn:aws:states:ap-northeast-1:XXXXXXXXXXXX:stateMachine:TestStateMachine',
  name: 'e3fde4c1-c416-4be1-83d1-2ac8f7ae0734',
  status: 'SUCCEEDED',
  startDate: 2019-07-28T07:47:05.951Z,
  stopDate: 2019-07-28T07:47:06.458Z,
  input: '{}',
  output:
   '{"statusCode": 200, "body": "{\\"message\\": \\"Go Serverless v1.0! Your function executed successfully!\\", \\"input\\": {}}"}' }

実行結果が出力され、ステートマシンを正常に実行することができました。

ステートマシン更新/動作確認

ステートマシン実行時の入力値に応じて、処理分岐するステートマシンに更新してみたいと思います。serverless.ymlを以下に更新しました。ハイライトしている箇所が更新部分です。

以前はステートマシンの更新がサポートされておらず、一度作成したステートマシンは編集できませんでしたが、現在はサポートされていますので、Serverless Frameworkからでも問題なく更新ができます。

service: StepFunctionsService

plugins:
  - serverless-step-functions

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

functions:
  Function1:
    name: TestFunction
    handler: handler.hello

stepFunctions:
  stateMachines:
    StateMachine1:
      name: TestStateMachine
      definition:
        StartAt: ChoiceState
        States:
          ChoiceState:
            Type: Choice
            Choices:
              - Variable: "$.input"
                StringEquals: "error"
                Next: Fail
            Default: HelloWorld
          HelloWorld:
            Type: Task
            Resource:
              Fn::GetAtt: [Function1LambdaFunction, Arn]
            Next: Succeed
          Fail:
            Type: Fail
          Succeed:
            Type: Succeed

デプロイします。

$ sls deploy -v
Serverless: Packaging service...
Serverless: Excluding development dependencies...

(省略)

デプロイが完了するとステートマシンが更新されれました。

ステートマシン実行時のインプットを変え、動作を確認してみたいと思います。

成功

$ sls invoke stepf --name StateMachine1 --data '{"input":"sakana"}'
.

{ executionArn:
   'arn:aws:states:ap-northeast-1:XXXXXXXXXXXX:execution:TestStateMachine:76401943-7bde-4e90-9acf-ea04d1389ab5',
  stateMachineArn:
   'arn:aws:states:ap-northeast-1:XXXXXXXXXXXX:stateMachine:TestStateMachine',
  name: '76401943-7bde-4e90-9acf-ea04d1389ab5',
  status: 'SUCCEEDED',
  startDate: 2019-07-28T08:14:09.082Z,
  stopDate: 2019-07-28T08:14:09.634Z,
  input: '{"input":"sakana"}',
  output:
   '{"statusCode": 200, "body": "{\\"message\\": \\"Go Serverless v1.0! Your function executed successfully!\\", \\"input\\": {\\"input\\": \\"sakana\\"}}"}' }
HL00257-2:TestStepFunctions sakamaki.kazuyoshi$

失敗

$ sls invoke stepf --name StateMachine1 --data '{"input":"error"}'

{ executionArn:
   'arn:aws:states:ap-northeast-1:XXXXXXXXXXXX:execution:TestStateMachine:86dcf2f5-3768-457c-b775-852e08908b65',
  stateMachineArn:
   'arn:aws:states:ap-northeast-1:XXXXXXXXXXXX:stateMachine:TestStateMachine',
  name: '86dcf2f5-3768-457c-b775-852e08908b65',
  status: 'FAILED',
  startDate: 2019-07-28T08:15:16.756Z,
  stopDate: 2019-07-28T08:15:16.896Z,
  input: '{"input":"error"}' }
HL00257-2:TestStepFunctions sakamaki.kazuyoshi$

さいごに

ステートマシンの定義にボリュームがでてくると、ステートメント言語(JSONベース)での定義がつらくなってくると思います。また、Step Functionsを利用するほとんどのケースで、Lambdaを利用していると思いますので、一連のデプロイをServerless Frameworkで実施すると簡潔になると思います。CloudWatchイベントの連携等もサクッと定義することができるので、Step Functionsを構築する際はServerless Frameworkの利用を検討してみてはいかがでしょうか。

参考