[AWS Step Functions] 複数先行ステートマシンの実行完了かつ指定起動開始時刻を満たしていることを条件に後続ステートマシンを実行してみた

複数先行ステートマシンの実行完了かつ指定起動開始時刻を満たしていることを条件に後続ステートマシンを実行してみました。CloudWatchアラームを使って一工夫することで実装することができます。
2021.12.08

何があってもジョブ管理システムをAWS Step Functionsに置き換えたい

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

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

最近、ステートマシン間の連携に関する記事を2本書きました。

しかし、まだ「複数先行ステートマシンの実行完了」かつ「指定起動開始時刻を満たしている」ことを条件に後続ステートマシンを実行する という複雑なパターンについてチャレンジしていませんでした。

このパターンは「先行のステートマシンの実行は完了している。しかし、日中帯はシステムの負荷が高いため、先行ステートマシンの実行が完了して即座に後続ステートマシンを実行するのは困る」という場面に役立ちます。

そこで今回は、実際に「複数先行ステートマシンの実行完了かつ指定起動開始時刻を満たしていることを条件に後続ステートマシンを実行」してみました。

検証の構成

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

検証構成図

基本的な構成は以前の記事と同じです。異なる要素としては、起動時間調整用ステートマシンと、それを指定した時間に実行するためのEventBridgeルールを追加しています。

指定した時間になったら、起動時間調整用ステートマシンが実行され、CloudWatchに計算用のメトリクスデータをPutします。

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

なお、CloudWatchに計算用のメトリクスデータをPutするためにLambdaではなくStep Functionsを採用した理由は、コードの管理を少しでも減らすためです。

Step Functionsは2021年10月に200以上のAWSサービスと連携できるようになりました。今回は単純に PutMetricData APIを叩ければ良いので、このアップデートを活用することで管理が必要なコードを減らすことができます。

起動時間調整用ステートマシンの作成

それでは、起動時間調整用を作成します。

Step Functionsのコンソールからステートマシンの作成をクリックします。

ステートマシンの作成

作成方法はStep Functions Workflow Studio、タイプは標準を選択して次へをクリックします。

作成方法を選択

ワークフローの設計ではStep Functions Workflow Studioを使ってPutMetricDataのみの実行する単純なワークフローを設計します。

MetricDataでどの後続ステートマシンに対する起動時間調整なのかが分かるように、PutMetricDataのAPIパラーメーターは以下のように行いました。

{
  "MetricData": [
    {
      "MetricName": "StateMachineScheduler",
      "Dimensions": [
        {
          "Name": "StateMachineArn",
          "Value.$": "$.StateMachine.Id"
        }
      ],
      "Value": 1
    }
  ],
  "Namespace": "StateMachineScheduler"
}

設計が完了したら次へをクリックします。

ワークフローを設計

生成されたコード(Amazon States Language )を確認します。

{
  "Comment": "A description of my state machine",
  "StartAt": "PutMetricData",
  "States": {
    "PutMetricData": {
      "Type": "Task",
      "End": true,
      "Parameters": {
        "MetricData": [
          {
            "MetricName": "StateMachineScheduler",
            "Dimensions": [
              {
                "Name": "StateMachineArn",
                "Value.$": "$.StateMachine.Id"
              }
            ],
            "Value": 1
          }
        ],
        "Namespace": "StateMachineScheduler"
      },
      "Resource": "arn:aws:states:::aws-sdk:cloudwatch:putMetricData"
    }
  }
}

問題なければ次へをクリックします。

生成されたコードを確認

最後にステートマシンの設定を行います。ステートマシン名やIAMロールの指定を行います。

PutMetricDataを叩く必要があるので、指定したIAMロールで以下のようにPutMetricDataを許可する必要があります。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "cloudwatch:PutMetricData",
            "Resource": "*"
        }
    ]
}

設定が完了したらステートマシンの作成をクリックします。

ステートマシン設定を指定

起動時間調整用ステートマシンの作成が完了すると以下のように表示されます。

StateMachineScheduler

CloudWatchアラームの設定

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

以前の記事と同じように、AWS CLIでCloudWatchアラームを設定します。

コマンドは以下の通りです。

$ METRICS_PARAMS=$(cat <<EOM
{
    "Metrics": [{
      "Id": "e1",
      "Label": "Sum ExecutionsSucceeded and StateMachineScheduler",
      "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:PriorStateMachine-A"
          }]
        },
        "Period": 7200,
        "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:PriorStateMachine-B"
          }]
        },
        "Period": 7200,
        "Stat": "Maximum"
      }
    }, {
      "Id": "m3",
      "ReturnData": false,
      "MetricStat": {
        "Metric": {
          "Namespace": "StateMachineScheduler",
          "MetricName": "StateMachineScheduler",
          "Dimensions": [{
            "Name": "StateMachineArn",
            "Value": "arn:aws:states:us-east-1:<AWSアカウントID>:stateMachine:SubsequentStateMachine-C"
          }]
        },
        "Period": 7200,
        "Stat": "Maximum"
      }
    }]
}
EOM
)

$ aws cloudwatch put-metric-alarm \
  --alarm-name RunSubsequentStateMachineAlarm \
  --alarm-description 'CloudWatch Alarm for executing subsequent state machines on the condition that the execution of multiple prior state machines is completed and the specified start-up start time is met.' \
  --threshold 3 \
  --comparison-operator GreaterThanOrEqualToThreshold \
  --evaluation-periods 1 \
  --datapoints-to-alarm 1 \
  --treat-missing-data missing \
  --cli-input-json "$METRICS_PARAMS"

ステートマシンの実行完了時間と起動時間差は最大2時間まで許容する場合は、"Period": 7200とします。Periodの値が86400など大きい値にした場合は、翌日の先行ステートマシンの実行時にもアラームが上がるなど意図しない挙動をする可能性が高くなります。そのため、Periodはできるだけ小さい値にする必要があると考えます。

コマンド実行後、CloudWatchのコンソールから作成したCloudWatchアラームを確認すると以下のように表示されます。

CloudWatchアラームの設定確認

起動時間調整用ステートマシン実行用のEventBridgeルールの設定

次に後続ステートマシン実行用のEventBridgeルールを設定します。

今回は毎日日本時間の20:30になった場合に起動する場合を考えます。この条件をCron式で表すとcron(30 11 * * ? *)になります。

指定した時間になった場合は起動時間調整用ステートマシンを実行するようにターゲットを設定します。また、トリガーした際は起動時間調整用ステートマシンに後続ステートマシンのARNをJSON形式で渡します。ステートマシン実行時のIAMロールについては新規で作成しました。

マネージメントコンソール上では以下のように設定を行います。

起動時間調整用ステートマシン実行用のEventBridgeルールの設定

後続ステートマシン実行用のEventBridgeルールの設定

次に起動時間調整用ステートマシン実行用のEventBridgeルールを設定します。

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

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

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

マネージメントコンソール上では以下のように設定を行います。

後続ステートマシン実行用のEventBridgeルールの設定

先行ステートマシンを実行して、起動開始時間になるまで待ってみた

それでは「2つの先行ステートマシンの実行が正常に完了」かつ「起動開始時間に(20:30)になった」タイミングで後続ステートマシンが実行されるのかを確認してみます。

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

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

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

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

この状態で20:30まで待ち、CloudWatchアラームを確認すると、20:31 (JST)にアラーム状態になっていました。

CloudWatchアラームのアラーム状態確認

念のため、起動時間調整用ステートマシンの実行履歴を確認すると、確かにEventBridgeルールで設定した20:30 (JST)に実行されていることが確認できました。

起動時間調整用ステートマシンの実行確認

最後に、本題の後続ステートマシンCの実行履歴を確認すると、確かにCloudWatchアラームがアラーム状態となった20:31 (JST)に実行されていることが確認できました。

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

これで、「複数先行ステートマシンの実行完了」かつ「指定起動開始時刻を満たしている」ことを条件に後続ステートマシンを実行する仕組みが正常に動作している事が確認できました。

これからもジョブ管理システムのAWS Step Functionsへの置き換えチャレンジをしていきます

AWS Step Functionsで「複数先行ステートマシンの実行完了」かつ「指定起動開始時刻を満たしている」ことを条件に後続ステートマシンを実行してみました。

基本的な構成は前回の記事から変わっていませんが、工夫次第で実装できることが分かりました。

ジョブ管理システムをAWS Step Functionsに完全に置き換えることは難しいかもしれませんが、今後もAWS Step Functionsでできるだけ同様の動作を実現できるようにチャレンジしようと思います。

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

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