実行中のStepFunctions数をカスタムメトリクスで送信する

こんばんは。
いま僕は堂島川のほとりでこれを書いています。

闇夜の水面に、在りし日を思い浮かべながらこれを書いています。
夜を越えるのって難しいですよね。

そんな1022回目の夏、みなさんいかがお過ごしですか?
僕は夏より熱い春を越えたので夏が来る前から夏バテです。

さて、そんな熱い春にやったことを少しずつ紹介しようと思いますが、まずはStepFunctionsのカスタムメトリクスをつくった話を。

StepFunctionsのメトリクス

どうもCTO室AI推進部@ken11です。
みなさん素敵なAWSライフを送ってますか?
僕は日々数多のStepFunctionsを使って楽しいAWSライフを送っています。

そんなStepFunctionsですが、もともと用意されているCloudWatchメトリクスではちょっと不満な点があります。
そうです、実行中のStepFunctionsの数や、実行時間が想定より長いStepFunctionsを知りたいときにちょうどよいメトリクスがないのです。

たとえば、我々AI推進部の場合、モデル学習のパイプラインとしてStepFunctionsを利用していますが、モデル学習で利用するGPUインスタンスは数が限られているので実行中の学習の数を把握する(=実行中のStepFunctionsの数を把握する)というのは非常に重要な指標となります。
また、StepFunctionsの実行時間は、「完了後にトータルでどれくらいの時間がかかったか」についてはメトリクスがありますが、「実行中にいまどれくらいの時間がかかっているか」を取得できるメトリクスはないので、1時間で終わると思っていた学習が5時間も6時間もかかっているというような情報は取得できない状態です。

ではこういった指標を取得したい場合どうすればよいでしょうか?

カスタムメトリクス

CloudWatchにはカスタムメトリクスという機能があります。
その名の通り、独自にカスタマイズしたメトリクスをCloudWatchに送信することができるのです。
そうです、なければつくればいいじゃないということです。

Lambdaをつくってカスタムメトリクスを送信する

カスタムメトリクスを送信することは、APIにリクエストするだけなので比較的簡単に実現できます。
今回はとりあえず、カスタムメトリクス送信用のLambdaをつくってこれを毎分実行することで、欲しいメトリクスを取得したいと思います。

というわけで、
– 実行中のStepFunctions数を取得
– 実行にxx時間以上かかっているStepFunctions数を取得

の2つを実際につくってみたいと思います。

import os
import datetime
import boto3


def sfn_monitor(event, context):
    sfn_arn = os.environ['SFN_ARN']
    threshold = 9000
    count, slow = get_values(sfn_arn, threshold)
    send_data(sfn_arn, count, slow)


def get_values(sfn_arn, threshold):
    client = boto3.client('stepfunctions', region_name='ap-northeast-1')
    res = client.list_executions(
        stateMachineArn=sfn_arn,
        statusFilter='RUNNING'
    )
    count = len(res['executions'])
    slow = 0
    for e in res['executions']:
        diff = (datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=9))) - e['startDate']).seconds
        if diff > threshold:
            slow += 1
    return count, slow


def send_data(sfn_arn, count, slow):
    now = datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=9)))
    client = boto3.client('cloudwatch')
    response = client.put_metric_data(
        Namespace='StepFunctions',
        MetricData=[
            {
                'MetricName': 'RunningCount',
                'Dimensions': [
                    {
                        'Name': 'stateMachineArn',
                        'Value': sfn_arn
                    },
                ],
                'Timestamp': now,
                'Value': count,
                'Unit': 'Count',
            },
            {
                'MetricName': 'SlowCount',
                'Dimensions': [
                    {
                        'Name': 'stateMachineArn',
                        'Value': sfn_arn
                    },
                ],
                'Timestamp': now,
                'Value': slow,
                'Unit': 'Count',
            },
        ]
    )

死ぬほど雑につくったLambda関数

ポイント1. 実行中のStepFunctions取得

これは stepfunctionslist_executions というAPIで取得可能です。
list_executionsstatusFilter でフィルタすることが可能なので、'RUNNING'ステータスのものに絞れば簡単に実行中のものだけが取得可能です。
注意点としては、よくあるAWSのAPIと同様にページングの概念があるので、件数が著しく多いようなケースではきちんとページングをケアしてあげないと取り漏らします。

ちなみに、みなさん既にお気づきだと思いますが、これはPythonで書く必要はどこにもないです。
Pythonだったのは~ただの気分~他のコードとの整合性のためです。

ポイント2. 実行の経過時間を取得

先ほど取得した list_executions は 実行のlistになっているので、1件ずつ取り出すと startDate といった情報が取得できます。
startDateDate というわりには実際には開始時刻が収まっているので、これを現在時刻と比較することで、実際に実行にかかっている時間(経過時間)が取得可能です。
今回の目的は実行に時間がかかりすぎているものを検知することなので、予め指定しておいたthresholdを越えている場合にカウントするようにしています。

ポイント3. カスタムメトリクスの送信

これには cloudwatchput_metric_data を使います。
MetricData 内にlistで送信したい内容を指定します。
このとき、Timestampの時間はタイムゾーンを指定することを忘れないようにする必要があります。

また、 NamespaceMetricNameDimensionsName がどこの値になるかというと、以下のようになっています。

EventBridgeで定期的に送信する

あとはこのLambda関数をEventBridgeで毎分実行するだけです。
もちろん、毎分ほど細かい頻度で必要ない場合は適宜調整してください。

まとめ

最終的に、今回新たに設定したカスタムメトリクスを使って以下のようなダッシュボードをつくりました。

これで実行中のStepFunctions数を見守ることができます。
もちろんカスタムメトリクスはCloudWatchアラームでも使うことができるので、たとえば「実行数が10を越えたらアラーム送信する」等の設定もできます。
これを活用して、思っていたよりも実行数が増えたときはSlackに通知したり、実行に時間がかかっているものがあらわれたらSlackに通知したり、といった監視体制をとっています。

StepFunctionsはあらゆるシーンで使い勝手のよいサービスだと思うので、必要に応じてカスタムメトリクスも活用しながら、ぜひ皆さんも素敵なAWSライフを送ってください!


マネーフォワードでは、エンジニアを募集しています。
ご応募お待ちしています。

【会社情報】
Wantedly
株式会社マネーフォワード
福岡開発拠点
関西開発拠点(大阪/京都)

【SNS】
マネーフォワード公式note
Twitter – 【公式】マネーフォワード
Twitter – Money Forward Developers
connpass – マネーフォワード
YouTube – Money Forward Developers

Pocket