AWS Systems Manager Change Calendar+Amazon EventBridgeで祝日にバッチ処理が実行されないよう制御しよう!

f:id:hamo2020:20211208175615j:plain

マネックス証券 システム開発推進部のHです。

はじめに

2021年もそろそろ終わりです。年末年始は休日や祝日も多くのんびりできますね。(今年から来年にかけては少ないかもしれませんが・・・ )
ところで弊社の基幹システムGALAXYでは、早朝から深夜までたくさんのバッチ処理が稼働しております。大半は平日のみ実行され、休日や祝日は実行しないようジョブスケジューラーがコントロールしております。
さてAWSで平日のみバッチ処理を実行しようとした場合に、どうやればよいのだろうか?と考えました。

どうやって実現するか考える

やりたいことは例えばこんな感じです。

  • 月曜日~金曜日の10:00に実行する。
  • 土曜日、日曜日、祝日は実行しない。

カレンダーとしては2022年1月を例にすると下記のイメージです。 f:id:hamo2020:20211209162015p:plain

これを実現するために、いくつかパターンを考えてみました。
ちなみに先日のアップデートで、AWS Systems Manager Change Calendar(以降SSM CCと略します)で状態が変わった場合にAmazon EventBridgeに対してイベント通知されるようになりました。
これを利用するパターンも考えてみます。

aws.amazon.com

パターン1:SSM CC+EventBridgeで実行日のみスケジュールするようにする。

上記にも記載した、SSM CCで指定した日時になるとイベントが発生し、カレンダーの状態が変更されます。そしてそのイベントの変更をEventBridgeで受け取り、バッチ処理を実行します。 構成としてはこんな感じです。

f:id:hamo2020:20211210160725p:plain

  • SSM CCには、営業日の指定した時刻に開始するようイベントを作成します。
  • EventBridgeはトリガ発火を受けてStep Functionsのステートマシンを実行します。

この構成によるメリット、デメリットは下記の通りです。

  • メリット:営業日にしか実行されないので、休日や祝日の保守が不要となります。
  • デメリット:SSM CCのイベントを1件ずつ手動で作成する必要があります。iCalendar形式のカレンダーをインポートできるため、他のカレンダー作成アプリでスケジュールを作成して取り込むといったことができますが、1日に複数回実行するようなジョブの場合は、結構手間がかかると思います。

パターン2:EventBridge内で祝日を含む月曜日~金曜日に実行するようにする。さらに実行するバッチ処理内で営業日かどうかチェックする。

SSM CCで祝日を定義したカレンダーを作成します。(これを「祝日カレンダー」とします)
祝日については、下記からiCalendarファイルをダウンロードして、SSM CCにインポートすれば作成できます。

https://www.google.com/calendar/ical/japanese__ja%40holiday.calendar.google.com/public/basic.ics

  • バッチ処理を実行するEventBridgeルールは、スケジュール実行(Cron形式)で月曜日~金曜日の指定した時刻に実行するようにします。
  • バッチ処理はStep Functionsで実装します。
  • Step Functionsのステートマシンでは「祝日カレンダー」の状態をチェックします。
    ⇒「祝日カレンダー」の初期状態の作り方によるのですが、初期状態を「OPEN」とすると、祝日になると「CLOSED」になり祝日が終了すると再び「OPEN」になります。

構成としてはこんな感じです。

f:id:hamo2020:20211210160830p:plain

Step Functionsのステートマシンとしては下記のような感じになります。

  • 「GetCalendarState」でSSM CCからカレンダーの状態を取得します。
  • 「Choice」で状態(OPEN/CLOSED)の判定を行い、平日であれば「Job execution」に進みます。

この構成によるメリット、デメリットは下記の通りです。

  • メリット:EventBridgeルールの実行をCron形式で指定するため、シンプルな設定ができる。複数ルールを作成した場合も、祝日判定は1つのカレンダー(この場合は「祝日カレンダー」)の状態を見ればよいため、カレンダーのメンテナンスがしやすい。(ルール毎にカレンダーを作成する必要がない)
  • デメリット:祝日にも実行されるため、もし異常終了した場合には対応が必要となる。(休日作業が発生する)

パターン3:SSM CC+EventBridge+Lambdaで祝日をトリガとしてバッチ処理の有効/無効を切り替える。(今回推しのパターン)

今回はこのパターンで検証しました。
このパターンでは、EventBridgeのルールを2つ使用します。

  • 「祝日カレンダー」をトリガとしてルールの有効化/無効化を切り替えるルール(ルール有効化/無効化ルール)
  • バッチ処理を定時実行するルール(定時実行ジョブルール)

SSM CCの状態変更をトリガとして、定時実行するルールの有効化/無効化化を切り替えます。
先にメリットとデメリットを書きます。

  • メリット:祝日にバッチ処理が実行されなくなります。カレンダーも祝日のみのカレンダーを作っておけばよく、運用もしやすいです。Lambdaの作り方によっては、複数ルールの制御も可能です。
  • デメリット:定時実行ジョブとはまた別のEventBridgeルールとルールの有効/無効を制御するためのLambda関数が必要となります。そのため他の2パターンより作成するものが増えます。

構成としてはこんな感じです。

f:id:hamo2020:20211210160415p:plain

  • パターン2と同様に祝日カレンダーを作成します。
  • 平日の指定した時刻に実行するようEventBridgeでルールを作成します。
  • SSM CCの状態を見てルールの有効化/無効化を切り替えるLambdaを実行するルールを作成します。
  • ルール有効化/無効化ルールは祝日開始のトリガ発火を受けてLambdaを実行します。
  • Lambdaは定時実行ジョブルールを無効化します。
    ⇒これにより祝日に実行されなくなる。
  • EventBridgeは祝日終了のトリガ発火を受けてLambdaを実行します。
  • Lambdaは定時実行ジョブルールを有効化します。
    ⇒これにより平日に実行されるようになる。

EventBridgeルールの有効化/無効化を切り替えるLambdaは下記のとおりです。Node.jsで作成しています。

const AWS = require('aws-sdk');

exports.handler = (event, context, callback) => {
    AWS.config.update({region: 'ap-northeast-1'});
    console.info(AWS.VERSION);
    var eventbridge = new AWS.EventBridge({apiVersion: '2015-10-07'});
    var calState = event.detail.state;
    var params = {
        Name: 'Eblog202112-job-execution',
        EventBusName: 'default'
    };
    // 定時実行ジョブルールの情報を取得
    eventbridge.describeRule(params, function(err, data) {
        if (err) {
            console.log("Error", err);
            return;
        }

        // SSM CCの状態によってジョブの有効/無効を変更する。
        if (calState == "OPEN") {
            // OPENの処理=>ジョブ有効化
            if (data.State == "DISABLED") {
                // ルールを有効化する
                eventbridge.enableRule(params, function(errOpen) {
                    if (errOpen) console.log(errOpen);
                });
            }
            console.log("OPENの処理");
        } else {
            // CLOSEDの処理=>ジョブ無効化
            if (data.State == "ENABLED") {
                // ルールを無効化する
                eventbridge.disableRule(params, function(errClosed) {
                    if (errClosed) console.log(errClosed);
                });
            } 
            console.log("CLOSEDの処理");
        }
    });

    const response = {
        statusCode: 200,
        body: JSON.stringify('ルール有効化/無効化処理終了しました。'),
    };
    callback(null, response);
};
実行してみる

実行してみます。次の祝日が元旦でそれまで待てないため、ある期間(2021/12/10 16:44~17:14)を仮の祝日として登録します。

  • イベントの登録内容 f:id:hamo2020:20211210164355p:plain

  • 登録後のSSM CC f:id:hamo2020:20211210164436p:plain

  • バッチ処理は以下のステートマシンをEventBridgeで5分ごとに実行します。 f:id:hamo2020:20211210164624p:plain f:id:hamo2020:20211210164820p:plain

実行結果

それでは実行結果を確認してみます。

〇祝日開始

祝日開始になるとトリガが発火して、定時実行ジョブが無効化されます。イベントの実行結果について今回はCloudTrailで確認します。

  • 16:44に定時実行ジョブルールが無効化される。

    • EventBridgeで定時実行ジョブルールが無効化されています。 f:id:hamo2020:20211210172046p:plain

    • CloudTrailでルール無効化のイベントが確認できます。 f:id:hamo2020:20211210171738p:plain f:id:hamo2020:20211210172740p:plain

〇祝日終了

祝日終了になるとトリガが発火して、定時実行ジョブが有効化されます。イベントの実行結果について今回はCloudTrailで確認します。

  • 17:14に定時実行ジョブルールが有効化される。

    • EventBridgeで定時実行ジョブルールが有効化されています。 f:id:hamo2020:20211210173259p:plain

    • CloudTrailでルール有効化のイベントが確認できます。 f:id:hamo2020:20211210173747p:plain f:id:hamo2020:20211212145610p:plain

●Step Functionsの実行ログ

Step Functionsの実行ログを確認してみます。16:45~17:10の間ジョブが実行されないことが分かります。 f:id:hamo2020:20211212150137p:plain

おわりに

今回Systems Manager Change CalendarとEventBridgを使用して、一般のジョブスケジューラでできるような平日のみバッチ処理実行を行うということをやってみました。業務ロジックに手を入れなくても実現できるため、汎用性は高いかなと思っております。