AWS再入門ブログリレー2022 AWS Step Functions編

弊社コンサルティング部による『AWS再入門ブログリレー 2022』の30日目のエントリ『AWS Step Functions』です。
2022.03.16

こんにちは、のんピ(@non____97)です。

当エントリは弊社コンサルティング部による『AWS 再入門ブログリレー 2022』の30日目のエントリです。

このブログリレーの企画は、普段 AWS サービスについて最新のネタ・深い/細かいテーマを主に書き連ねてきたメンバーの手によって、 今一度初心に返って、基本的な部分を見つめ直してみよう、解説してみようというコンセプトが含まれています。

AWS をこれから学ぼう!という方にとっては文字通りの入門記事として、またすでに AWS を活用されている方にとっても AWS サービスの再発見や 2022 年のサービスアップデートのキャッチアップの場となればと考えておりますので、ぜひ最後までお付合い頂ければ幸いです。

では、さっそくいってみましょう。今日のテーマは『AWS Step Functions』です。

AWS Step Functionsとは

AWS Step Functionsとは様々な処理を連結して管理ができるサーバーレスオーケストレーションサービスです。

Lambda関数や各種APIの実行の順序を制御して、ETLやバッチ処理など順序だって実行したい一連の処理を簡単に管理することができます。

どのような処理を行うかはAWSマネージメントコンソール上でワークフローという形式で可視化することが出来ます。処置と処理が連携しているところからジョブ管理システムのような印象を持つかもしれません。

ワークフロー例

こちらのワークフローはAWS Step Functions Workflow Studioで設計したものです。AWS Step Functions Workflow Studioについての詳細な説明は以下エントリをご覧ください。

また、AWS Step Functions Workflow StudioのようにワークフローはGUIだけではなく、CLIで管理も可能です。こちらのワークフローをASL(Amazon States Language)で表現すると以下のようになります。(長いので折り畳みます。)

ASLの例
{
  "Comment": "A description of my state machine",
  "StartAt": "Lambda Invoke",
  "States": {
    "Lambda Invoke": {
      "Type": "Task",
      "Resource": "arn:aws:states:::lambda:invoke",
      "OutputPath": "$.Payload",
      "Parameters": {
        "Payload.$": "$"
      },
      "Retry": [
        {
          "ErrorEquals": [
            "Lambda.ServiceException",
            "Lambda.AWSLambdaException",
            "Lambda.SdkClientException"
          ],
          "IntervalSeconds": 2,
          "MaxAttempts": 6,
          "BackoffRate": 2
        }
      ],
      "Next": "SNS Publish"
    },
    "SNS Publish": {
      "Type": "Task",
      "Resource": "arn:aws:states:::sns:publish",
      "Parameters": {
        "Message.$": "$"
      },
      "Next": "Choice"
    },
    "Choice": {
      "Type": "Choice",
      "Choices": [
        {
          "Next": "Map"
        }
      ],
      "Default": "Glue StartJobRun"
    },
    "Map": {
      "Type": "Map",
      "Next": "Step Functions StartExecution",
      "Iterator": {
        "StartAt": "DescribeAlarms",
        "States": {
          "DescribeAlarms": {
            "Type": "Task",
            "End": true,
            "Parameters": {},
            "Resource": "arn:aws:states:::aws-sdk:cloudwatch:describeAlarms"
          }
        }
      }
    },
    "Step Functions StartExecution": {
      "Type": "Task",
      "Resource": "arn:aws:states:::states:startExecution.sync:2",
      "Parameters": {
        "StateMachineArn": "arn:aws:states:REGION:ACCOUNT_ID:stateMachine:STATE_MACHINE_NAME",
        "Input": {
          "StatePayload": "Hello from Step Functions!",
          "AWS_STEP_FUNCTIONS_STARTED_BY_EXECUTION_ID.$": "$$.Execution.Id"
        }
      },
      "Next": "Parallel"
    },
    "Parallel": {
      "Type": "Parallel",
      "Branches": [
        {
          "StartAt": "DeleteFunction",
          "States": {
            "DeleteFunction": {
              "Type": "Task",
              "End": true,
              "Parameters": {
                "FunctionName": "MyData"
              },
              "Resource": "arn:aws:states:::aws-sdk:lambda:deleteFunction"
            }
          }
        },
        {
          "StartAt": "CopyObject",
          "States": {
            "CopyObject": {
              "Type": "Task",
              "End": true,
              "Parameters": {
                "Bucket": "MyData",
                "CopySource": "MyData",
                "Key": "MyData"
              },
              "Resource": "arn:aws:states:::aws-sdk:s3:copyObject"
            }
          }
        }
      ],
      "Next": "Success"
    },
    "Success": {
      "Type": "Succeed"
    },
    "Glue StartJobRun": {
      "Type": "Task",
      "Resource": "arn:aws:states:::glue:startJobRun",
      "Parameters": {
        "JobName": "myJobName"
      },
      "Next": "Choice 2"
    },
    "Choice 2": {
      "Type": "Choice",
      "Choices": [
        {
          "Variable": "$",
          "IsPresent": true,
          "Next": "Fail"
        }
      ],
      "Default": "Wait"
    },
    "Wait": {
      "Type": "Wait",
      "Seconds": 5,
      "Next": "Success"
    },
    "Fail": {
      "Type": "Fail"
    }
  }
}

こちらのように定義はコードでも管理できるので、GitHubやCodeCommitなどのバージョン管理のサービスを使えば、CI/CDパイプラインを構築して、自動でデプロイするなんてことも可能です。実際にCodeCommitやCodeBuild、CodePipelineを使ってCI/CDパイプラインを構築した例は以下になります。

AWS Step Functionsで定義した一連の処理のまとまりをステートマシンと呼びます。そのため、「ステートマシンを実行する」というのはAWS Step Functionsで定義した一連の処理を実行するという意味になります。

また、AWS Step FunctionsはStandardとExpressの2種類があります。主な使い分けや特徴は以下の通りです。

Standard:

  • 1回の実行に5分以上かかると予想される場合に選択する。
    • 2,000/秒実行レート
    • 毎秒 4,000 の状態遷移レート
    • 状態移行ごとの価格設定
    • 実行履歴と視覚的なデバッグを表示
    • すべてのサービス統合とパターンをサポート
  • 一般的なバッチ処理やETLなど

Express:

  • 5分未満のワークフローで、1秒あたり100,000回の呼び出しが必要な場合に選択する。
    • 100,000/秒実行レート
    • ほぼ無制限の状態遷移レート
    • 実行回数および実行期間ごとの価格
    • Amazon CloudWatch に実行履歴を送信
    • すべてのサービス統合とパターンをサポート
  • マイクロサービスなど

StandardとExpressを組み合わせて、StandardのステートマシンからからExpressのステートマシンを呼び出すことも可能です。ジョブ管理システムで管理していたような処理をAWS Step Functionsに移行する場合は、ほとんどStandardを選択すると考えます。

AWS Step Functionsのメリット

このようなサービスに求められる機能はなんでしょう。

一連の処理をワークフローとして管理したいのであれば、例えば以下のような機能が必要だと考えます。

  • 同期実行
  • 並列実行
  • 条件分岐
  • ループ
  • 待機
  • リトライ
  • 例外処理
  • 実行結果の確認
  • 異なるワークフロー間の連携

AWS Step Functionsは上で挙げた機能を全て実現できます。しかもサーバーレスなので、一般的なジョブ管理システムのようにサーバーの管理をする必要はありません。

こういった機能を手軽に、すぐに使用できるのは非常に魅力的ですね。

AWS Step Functionsでできないこと

ここで、「ジョブ管理システムはAWS Step Functionsに置き換えられるんじゃないか?」と思われるかもしれません。

しかし、AWS Step Functionsはジョブ管理システムではないので、ジョブ管理システムにあるような以下の機能は提供されていません。併せて背景や緩和策についても記載します。

  1. ワークフローの途中からの実行
  2. カレンダーと連携した柔軟な実行

AWS Step Functionsでできる処理

ここでAWS Step Functionsでできる処理 = Stateの種類を紹介します。

Stateの種類一覧は以下の通りです。

  • Task
  • Choice
  • Wait
  • Success
  • Fail
  • Parallel
  • Map

Task

Taskは単一の処理を行うStateです。

Taskで設定可能な処理は各種AWSのAPIとActivityです。現在は200以上のAPIを呼び出すことが可能です。

ActivityはEC2インスタンスやオンプレミス環境で動作しているアプリケーションの制御するものです。詳細は以下エントリをご覧ください。

Choice

Choiceは条件分岐を行うStateです。

どのような条件の時に、どのStateに遷移させるか指定することができます。

指定可能な比較演算子は以下の通りです。

  • And
  • BooleanEquals,BooleanEqualsPath
  • IsBoolean
  • IsNull
  • IsNumeric
  • IsPresent
  • IsString
  • IsTimestamp
  • Not
  • NumericEquals,NumericEqualsPath
  • NumericGreaterThan,NumericGreaterThanPath
  • NumericGreaterThanEquals,NumericGreaterThanEqualsPath
  • NumericLessThan,NumericLessThanPath
  • NumericLessThanEquals,NumericLessThanEqualsPath
  • Or
  • StringEquals,StringEqualsPath
  • StringGreaterThan,StringGreaterThanPath
  • StringGreaterThanEquals,StringGreaterThanEqualsPath
  • StringLessThan,StringLessThanPath
  • StringLessThanEquals,StringLessThanEqualsPath
  • StringMatches
  • TimestampEquals,TimestampEqualsPath
  • TimestampGreaterThan,TimestampGreaterThanPath
  • TimestampGreaterThanEquals,TimestampGreaterThanEqualsPath
  • TimestampLessThan,TimestampLessThanPath
  • TimestampLessThanEquals,TimestampLessThanEqualsPath

Wait

Waitは指定された時間待機するStateです。

相対時間(秒数) と絶対時間 (タイムスタンプ) のいずれかを選択できます。

以下エントリでは相対時間と絶対時間のどちらかを指定して、指定した時間待機するような処理を実装しています。

Success

Successは正常終了をさせるStateです。

ワークフローの最後にSuccessを必ず追加する必要はありません。Choiceで条件分岐をした結果、何もせずにそのままステートマシンを終了させる場合に使用します。

Fail

Failは異常終了をさせるStateです。

Choiceで条件分岐をした結果、ステートマシンを異常終了させたい場合に使用します。

Parallel

Parallelは平行処理を制御するStateです。

2つ以上の処理を並行して実行したい場合に使用します。

Map

Mapは配列に対して繰り返し同じ処理を行うStateです。

例えば複数のEC2インスタンスを停止させたい場合は、EC2インスタンスIDの配列をMapに渡して、Map内でEC2:StopInstancesAPIを実行するように定義すれば簡潔に表現できます。

Mapの例

ステートマシンの実行方法

ステートマシンの実行方法は主に以下の方法があります。

  1. マネージメントコンソール
  2. EventBridgeルール
  3. API Gateway
  4. API
  5. CLI
  6. SDK

おそらく多くの場合は、EventBridgeルール経由でステートマシンを実行するのではないかと考えます。APIのバックエンド処理として使用する場合は、API Gatewayから実行するようにすれば良さそうです。

値の入出力

各State間で値を連携したい場合は各種制御パラメーターを使って、どういった値を受け取るのか、受け取らないのかを制御することができます。

制御パラメーターは以下の通りです。

  • InputPath
    • 入力の一部をフィルタリングしてStatesに渡す
  • OutputPath
    • 出力の一部をフィルタリングして次のStateに渡す
  • ResultPath
    • 出力を元の入力を出力に追加
  • Parameters
    • Statesに渡す入力を指定
  • ResultSelector
    • 出力の結果を変換

制御パラメーターの詳細な動作は以下エントリとAWS公式ドキュメントが参考になります。

ロギングとモニタリング

ステートマシンの実行結果のモニタリング方法は主に以下があります。

  • CloudWatch Logsに出力された各Stateの実行ログを確認する
  • マネージメントコンソールから実行履歴を確認する
  • X-Ray トレースマップから処理のタイムラインを確認する
  • CloudTrailからAPI実行履歴を確認する

マネージメントコンソールから確認する場合、以下のように正常に処理できたStateは緑色になっています。

ステートマシンの実行完了確認

また、CloudWatchメトリクスで取得可能なAWS Step Functionsの実行メトリクスは以下の通りです。

メトリクス 説明
ExecutionTime 実行のスタート時点から終了時点までの間隔 (ミリ秒単位)。
ExecutionThrottled 調整済みの StateEntered イベントと再試行回数。
ExecutionsAborted 中断または終了された実行の数。
ExecutionsFailed 失敗した実行の数。
ExecutionsStarted スタートされた実行の数。
ExecutionsSucceeded 正常に完了した実行の数。
ExecutionsTimedOut 何らかの理由でタイムアウトした実行の数。

AWS Step Functions気になるお値段

2022/3/16 時点 AWS Step Functionsの料金(東京リージョン)

Standardの場合は以下の通りです。

  • 毎月 4,000 回までの状態遷移は無料 (12ヶ月間のAWS無料利用枠の期間とは別に)
  • 状態遷移 1,000 回あたり $0.025

Expressの場合は以下の通りです。

  • 100 万件のリクエストあたり 1.00USD
  • リクエスト当たり 0.000001USD
  • メモリの利用量と使用時間
    • GB-秒あたり 0.00001667USD 最初の 1,000 時間 GB 時間 (GB-秒あたり 0.0600USD)
    • GB-秒あたり 0.00000833USD 次の 4,000 時間 GB 時間 (GB-秒あたり 0.0300USD)
    • GB-秒あたり 0.00000456USD それ以降 (GB-秒あたり 0.01642USD)

AWS Step Functionsざっくり振り返ってみた

AWS Step Functionsをざっくり振り返ってみたました。

AWSのAPIを直接呼ぶことができるようになって、かなり使いやすくなったサービスです。皆さんもぜひ楽しいAWS Step Functionsライフを満喫してください。

次回、3/17(木)は千葉 幸宏(チバユキ)の「AWS IAM」の予定です。興奮しますね。

以上、AWS事業本部 コンサルティング部の のんピ(@non____97)でした!