[AWS Step Functions] 複数の先行ステートマシンの実行が完了した後に後続ステートマシンを実行してみた

AWS Step Functionsで複数の先行ステートマシンの実行が完了した後に後続ステートマシンを実行してみました。先行ステートマシンAと先行ステートマシンBの正常に実行完了した数のメトリクス(ExecutionsSucceeded)をCloudWatch Metric Mathで計算します。計算結果が先行ステートマシンの数と同じ値になったらアラームを発報し、EventBridgeで後続ステートマシンを実行します。
2021.11.30

ジョブ管理システムをAWS Step Functionsに置き換えたい

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

皆さんはジョブ管理システムから抜け出したいと思ったことはありますか? 私は常に思っています。

これまで、ジョブ管理システムをAWS Step Functionsに置き換えたいと思い、以下記事を書いてきました。

しかし、AWS Step Functionsはジョブ管理システムではないので、ジョブ管理システムでは使えた機能が存在しない場合ももちろんあります。

例えば、複数の先行ステートマシン(ジョブ管理システムで言うところのジョブネット)が完了したことをきっかけに後続ステートマシンを実行する場合は、一工夫が必要です。

今回、複数の先行ステートマシンの実行が完了した後に後続ステートマシンを実行するためにCloudWatchアラームを使って対応してみたので、そちらの方法を紹介します。

検証の構成

今回の検証の構成は以下の通りです。

検証構成図

先行ステートマシンAと先行ステートマシンBの正常に実行完了した数のメトリクス(ExecutionsSucceeded)をCloudWatch Metric Mathで計算します。計算結果が先行ステートマシンの数と同じ値になったらアラームを発報し、EventBridgeで後続ステートマシンを実行します。

CloudWatch Metric Mathについては以下記事が参考になります。

検証はステートマシンが作成済みの状態で、CloudWatchアラームの設定とEventBridgeルールの設定から行います。

CloudWatchアラームの設定

それではCloudWatchアラームの設定をしていきます。

ステートマシンが実行されていない場合はメトリクス一覧に表示されません。そのため、未実行のステートマシンについてCloudWatchアラームを設定する場合は、AWS CLIを使って設定する必要があります。

また、AWS CLIのCloudWatchアラームを作成するコマンド(put-metric-alarm)でCloudWatch Metric Mathを使用する際は、--metricsでメトリクスを指定する必要があります。

こちらのパラメーターはJSONで指定する必要があります。JSONをワンライナーで記述するにかなり大変なので、以下記事で紹介されている通り、ヒアドキュメントと--cli-input-jsonを使ってメトリクスを指定します。

最終的なコマンドは以下の通りです。

$ METRICS_PARAMS=$(cat <<EOM
{
    "Metrics": [{
      "Id": "e1",
      "Label": "Sum ExecutionsSucceeded",
      "ReturnData": true,
      "Expression": "SUM(METRICS())"
    }, {
      "Id": "m1",
      "ReturnData": false,
      "MetricStat": {
        "Metric": {
          "Namespace": "AWS/States",
          "MetricName": "ExecutionsSucceeded",
          "Dimensions": [{
            "Name": "StateMachineArn",
            "Value": "arn:aws:states:us-east-1:<AWSアカウントID>:stateMachine:StateMachineA"
          }]
        },
        "Period": 300,
        "Stat": "Maximum"
      }
    }, {
      "Id": "m2",
      "ReturnData": false,
      "MetricStat": {
        "Metric": {
          "Namespace": "AWS/States",
          "MetricName": "ExecutionsSucceeded",
          "Dimensions": [{
            "Name": "StateMachineArn",
            "Value": "arn:aws:states:us-east-1:<AWSアカウントID>:stateMachine:StateMachineB"
          }]
        },
        "Period": 300,
        "Stat": "Maximum"
      }
    }]
}
EOM
)

$ aws cloudwatch put-metric-alarm \
  --alarm-name prior-state-machine-alarm \
  --alarm-description 'Alarm to detect that two preceding state machines have successfully completed execution.' \
  --threshold 2 \
  --comparison-operator GreaterThanOrEqualToThreshold \
  --evaluation-periods 1 \
  --datapoints-to-alarm 1 \
  --treat-missing-data missing \
  --cli-input-json "$METRICS_PARAMS"

作成するCloudWatchアラームの条件としては、「5分以内に2つのステートマシンが正常に実行完了した場合」にしています。

何分以内」にというパラメーターは、Metrics配下のPeriodで指定します。例えば2つのステートマシンがあり、片方のステートマシンの実行が完了してからもう片方のステートマシンが完了するまでの待ち時間を1時間としてたいのであれば、"Period": 3600とします。

上述のコマンドでCloudWatchアラームを作成した後はaws cloudwatch describe-alarmsで作成したCloudWatchアラームを確認を行います。結果は以下の通り正常に作成完了していました。

$ aws cloudwatch describe-alarms --alarm-names "prior-state-machine-alarm"
{
    "MetricAlarms": [
        {
            "AlarmName": "prior-state-machine-alarm",
            "AlarmArn": "arn:aws:cloudwatch:us-east-1:<AWSアカウントID>:alarm:prior-state-machine-alarm",
            "AlarmDescription": "Alarm to detect that two preceding state machines have successfully completed execution.",
            "AlarmConfigurationUpdatedTimestamp": "2021-11-30T05:01:57.848000+00:00",
            "ActionsEnabled": true,
            "OKActions": [],
            "AlarmActions": [],
            "InsufficientDataActions": [],
            "StateValue": "INSUFFICIENT_DATA",
            "StateReason": "Unchecked: Initial alarm creation",
            "StateUpdatedTimestamp": "2021-11-30T05:01:57.848000+00:00",
            "Dimensions": [],
            "EvaluationPeriods": 1,
            "DatapointsToAlarm": 1,
            "Threshold": 2.0,
            "ComparisonOperator": "GreaterThanOrEqualToThreshold",
            "TreatMissingData": "missing",
            "Metrics": [
                {
                    "Id": "e1",
                    "Expression": "SUM(METRICS())",
                    "Label": "Sum ExecutionsSucceeded",
                    "ReturnData": true
                },
                {
                    "Id": "m1",
                    "MetricStat": {
                        "Metric": {
                            "Namespace": "AWS/States",
                            "MetricName": "ExecutionsSucceeded",
                            "Dimensions": [
                                {
                                    "Name": "StateMachineArn",
                                    "Value": "arn:aws:states:us-east-1:<AWSアカウントID>:stateMachine:StateMachineA"
                                }
                            ]
                        },
                        "Period": 300,
                        "Stat": "Maximum"
                    },
                    "ReturnData": false
                },
                {
                    "Id": "m2",
                    "MetricStat": {
                        "Metric": {
                            "Namespace": "AWS/States",
                            "MetricName": "ExecutionsSucceeded",
                            "Dimensions": [
                                {
                                    "Name": "StateMachineArn",
                                    "Value": "arn:aws:states:us-east-1:<AWSアカウントID>:stateMachine:StateMachineB"
                                }
                            ]
                        },
                        "Period": 300,
                        "Stat": "Maximum"
                    },
                    "ReturnData": false
                }
            ]
        }
    ],
    "CompositeAlarms": []
}

マネージメントコンソールから確認すると、以下のように表示されます。

作成したCloudWatchアラームの確認

EventBridgeルールの設定

次にEventBridgeルールを設定します。

CloudWatchアラームがアラーム状態になった場合に検知してほしいので、イベントパターンは以下のようになります。

{
  "source": ["aws.cloudwatch"],
  "detail-type": ["CloudWatch Alarm State Change"],
  "resources": [
    "arn:aws:cloudwatch:us-east-1:<AWSアカウントID>:alarm:prior-state-machine-alarm"
  ],
  "detail": {
    "state": {
      "value": ["ALARM"]
    }
  }
}

イベントを検知した場合は、後続ステートマシンを実行するようにターゲットを設定します。また、ステートマシン実行時のIAMロールは新規で作成しました。マネージメントコンソール上では以下のように設定を行います。

EventBridgeルールの設定

先行ステートマシンを実行してみた

それでは先行ステートマシンを実行してみて、そのタイミングで後続ステートマシンが実行されるのかを確認してみます。

マネージメントコンソールから先行ステートマシンAを実行しました。終了時間を確認すると、17:07 (JST)でした。

先行ステートマシンAの実行

先行ステートマシンA実行後のCloudWatchアラームを確認すると、08:05 (UTC)で、CloudWatch Metric Mathの計算結果であるSum ExecutionsSucceeded1となっていました。

先行ステートマシンA実行後のCloudWatchアラーム

少し時間を置いて、先行ステートマシンBを実行しました。終了時間を確認すると、17:09 (JST)でした。

先行ステートマシンBの実行

先行ステートマシンB実行後のCloudWatchアラームを確認すると、08:05 (UTC)で、CloudWatch Metric Mathの計算結果であるSum ExecutionsSucceeded2となり、アラーム状態となっていました。

先行ステートマシンB実行後のCloudWatchアラーム

後続ステートマシンCの実行履歴を確認すると、確かに先行ステートマシンBの実行後である17:10 (JST)に実行開始をしていることを確認できました。

後続ステートマシンCの実行履歴確認

これで複数の先行ステートマシンの実行が完了した後に後続ステートマシンを実行する動作確認ができました。

また、改めてCloudWatchアラームの履歴を確認すると、先行ステートマシンAが実行完了した約5分後である08:13 (UTC)に、状態がアラーム状態からOKに遷移しています。CloudWatchアラームの条件として「5分以内に2つのステートマシンが正常に実行完了した場合」という条件も正しく判定されていそうですね。

メトリクスが5分単位で丸められないか確認してみた

めでたし。めでたし。と思ったのですが、先行ステートマシンAとBを実行した後のCloudWatchアラームを確認した際にどちらも、08:05 (UTC)と5分単位でメトリクスが丸められて表示されていたのがなんだか気になりました。

そこから、「ひょっとして、5分単位でメトリクスが丸められてしまい、2つのステートマシンを5分以内に実行完了しても正しく判定されないのでは?」と思いました。

そこで、追加検証として、先行ステートマシンAとBの実行終了の時間が08:0408:13など5分単位の時刻をまたいでいる場合に丸められてしまうのかどうかを検証してみます。

先行ステートマシンAの実行終了時間が18:13 (JST)で、先行ステートマシンBの実行終了時間が18:16 (JST)と2つのステートマシンの実行終了時間が18:10 (JST)をまたぐように実行してみました。

先行ステートマシンAの実行履歴

先行ステートマシンAの実行終了時間 (追加検証)

先行ステートマシンBの実行履歴

先行ステートマシンBの実行終了時間 (追加検証)

しばらく待つと、正常にCloudWatchアラームがアラーム状態となりました。どうやらメトリクスは5分単位で丸められないようですね。一安心です。

CloudWatchアラームの履歴

また、実際に後続ステートマシンCの実行履歴を確認すると、確かに先行ステートマシンBの実行後である18:17 (JST)に実行開始をしていることを確認できました。

後続ステートマシンCの実行履歴確認 (追加検証)

複雑過ぎないジョブネットについては作り込めば乗り換えられる

AWS Step Functionsで複数の先行ステートマシンの実行が完了した後に後続ステートマシンを実行してみました。

AWS Step Functions単体ではジョブ管理システムの機能を代替することは難しいですが、他のサービスを組み合わせることによって、複雑過ぎないジョブネットであれば置き換えれそうだなと実感しました。

また、今回は先行ステートマシンと後続ステートマシンが同じAWSアカウントである前提で構成しました。先行ジョブネットと後続のジョブネットのアカウントが異なる場合でも、以下記事で紹介しているようにEventBridgeルールのターゲットで別アカウントまたはリージョンのイベントバスを選択することで対応可能だと考えます。

この記事が誰かの助けになれば幸いです。

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