CloudWatch Alarm でバッチ処理が起動していないときだけ通知したい!

記事タイトルとURLをコピーする

はじめに

こんにちは。ディベロップメントサービス課の保田(ほだ)です。

「あるものが存在すること」を証明するには「あるもの」をひとつでも見つけられれば数学的にも問題ないですが、「ないこと」を証明するのって難しいですよね。下手すれば 悪魔の証明 にもなりかねないお話です。

今日はその 悪魔の証明 に「メトリクスの値があること」を前提とする CloudWatch Alarm で挑みます。

要約

頑張れば最長で直近72時間分のメトリクスを監視できる

導入

1日に1回起動するバッチ処理があるとします。これが毎日確実に起動されていることを監視するにはどうしたら良いでしょうか。

問いかけに対してはちょっとズルい回答になりますが、まず最初に考えるべきは CloudWatch Events で24時間おきにバッチ処理を起動するようにしておけばひとまずは OK 、ということですよね。

もちろん稀に複数回起動されてしまうこともあるので冪等性を確保しておく必要はあります。

まれに、単一のイベントまたはスケジュールされた期間に対して同じルールを複数回トリガーしたり、特定のトリガーされたルールに対して同じターゲットを複数回起動したりする場合があります

docs.aws.amazon.com

しかし、AWS 外の他システムとの連携で、例えば Lambda が毎日一定時刻に外部のシステムから起動されるシステムのような場合、その起動を監視するのは少し工夫が必要です。

特に、「実行されなかったときだけ通知したい」場合はさらに工夫が必要です。

毎日ほぼ決まった時刻に外部から実行される Lambda 関数の Invocations メトリクスを監視したい

今回はある Lambda 関数が1日(24時間)に1回実行されていることを監視し、実行されなくなったときだけ通知したいとします。

「特定の Lambda 関数が実行されたこと」はメトリクス Invocations として CloudWatch に記録されます。

docs.aws.amazon.com

なので、素朴な考えだとこのメトリクスが24時間観測されなかったらアラーム状態となるようにすれば良い、となります。

設定内容としてはこんな感じです(イメージがつきづらい方は実際にアラームを作成する画面と照らし合わせてみて下さい)。

項目 設定値
メトリクス名 Invocations
名前空間 AWS/Lambda
統計 合計
期間 24時間
しきい値 0以下
アラームを実行するデータポイント 1/1
欠落データの処理 欠落データを不正(しきい値を超えている)として処理

直近24時間で Invocations のメトリクスが 0 以下(つまり 0)な状態が 1 回続けばアラーム状態とする、データの欠落は(実行されていることが正常なので)不正とする、大丈夫そうです。

だが、ちょっと待ってほしい

実はこの設定だと、最長で72時間前までの Invocations メトリクス(以降はより一般にデータポイントと呼びます)があれば OK 扱いになってしまいます。

なので、ある一日分の実行がすっぽり抜け落ちていても気づくことができません。

欠落データの扱い

今回はデータポイントが「あること」ではなく「ないこと」を通知したいので、欠落データの扱いを理解しなければなりません。

また、 本来はデータポイントが「あること」や「あったうえでそれが特定の値を超える・超えない」を記録するための仕組みですので、若干裏技的なことをしている点をご理解いただければと思います。

以降はドキュメントをベースに解説いたしますが、ややこしく感じることかと思います。それも裏技的な使い方をしているが故のことです。

必要なことはこちらのドキュメントにすべて書かれています。

docs.aws.amazon.com

詳しく見ていきます。

マネジメントコンソール上で選択できるオプションは以下の4種類あります。

  1. 欠落データを不正(しきい値を超えている)として処理
  2. 欠落データを無視(アラーム状態を維持する)として処理
  3. 欠落データを適正(しきい値を超えていない)として処理
  4. 欠落データを見つかりませんとして処理

パターン 1. 欠落データを不正(しきい値を超えている)として処理

これは「データが取得出来なかった」ことが「良くないことが起きている可能性」を内包している場合に選ぶことになります。

例えばサーバーの CPU 使用率などのメトリクスを取得している場合、急にデータが取得できなくなるとそれはたとえ CPU 使用率が異常に高い状態がこれまで発生していなかったとしてもアラーム状態としてその CloudWatch アラーム内で通知したいですよね。

パターン 2. 欠落データを無視(アラーム状態を維持する)として処理

これは分かりやすいですが「データが取得できてなくても正常なデータが取れるまではアラーム状態を維持するよ」ですね。

正常とみなすとして設定した範囲を超えた状態からデータが取得できない状態に遷移したとき、それは異常な状態がまだ持続していると判断したい場合に選ぶことになります。

パターン 3. 欠落データを適正(しきい値を超えていない)として処理

これはデータが取得出来てなくてもそれは大丈夫なものと判断できる場合に選ぶことになります。

ヤバいことになったときだけ観測される( Lambda 関数の Errors や Throttles など)ようなメトリクスを見つけ次第通知するようなアラームを設定した場合、平和なときはずっとデータが欠落しているわけですから、それは適正な状態ですよと、そういうわけです。

パターン 4. 欠落データを見つかりませんとして処理

これは「データが取得できなかった状態を正常とも不正とも判断しない」場合に選ぶことになります。 単純に「データ不足」と表示されます。

「期間」と「アラームを実行するデータポイント」

以降の話で、この「期間」と「アラームを実行するデータポイント」の理解が曖昧だと分かるような分からないような感じになってしまう(半年前の私)恐れがありますので、一旦脇道に逸れてこれらについて説明します。

まず、コンソール上でアラームを作成するときに設定する 期間 なる値があります。 これは「その期間にあるデータポイントを集めてこのあとどうするか決めるよ」という指標です。 これは上限値が 24 時間で、標準メトリクスでは最短1分です。それより短く設定しても標準メトリクスだと意味がありません。

間隔 最大値は 1 日 (86,400 秒) です。このクォータは変更できません。

docs.aws.amazon.com

次に「アラームを実行するデータポイント」です。 これはコンソール上で分数のようにも見える素人目には謎な設定項目です。 これは「 期間 内で取得したデータポイントをもって正常・異常を判断する基準と状態遷移するまでの期間」となります。もっとザックリ言えば「見逃してやる基準」です。

例えばこれを 1/1 とすると、1回評価してその内1回でもしきい値を超えている状態が観測できたら即アラーム、となります。 2/2 とすると、これは約分して 1/1 と同じ~ではなく、 2回評価して2回ともしきい値を超えている状態だったらアラーム、となります。 3/5 だと5回評価して、2回までは許してあげるけど3回になるとアラーム、となります。 また、ここまでの説明で分かるように 5/1 のような左側の数字の方が大きくなるような設定はできません。因果律を超越しますのでね。

また、これと先ほどの 期間 と合わせると、次のようなアラームは「5分間で取得したデータポイントがしきい値を超えているかどうかを 4 回評価して、その内 3 回しきい値を超えているとアラームと見ます」といったものになります。

  • 期間:5分
  • アラームを実行するデータポイント: 3/4

より一般的な表現だと「 M/N だと N 回評価した内 M 回しきい値を超えているとアラーム状態に遷移しますよ」です。 この N評価期間(Evaluation Periods) とドキュメントでは呼ばれています。

docs.aws.amazon.com

ちなみに先ほどの 期間 の上限値が 24 時間という話がありましたが、より厳密には 期間N を乗じた値( 評価間隔 )の上限が 24 時間です。

例)

  • 設定 可能
    • 期間:24時間・アラームを実行するデータポイント:1/1 ← 24 時間が上限になっているパターン
    • 期間:4時間・アラームを実行するデータポイント:6/6
  • 設定 不可
    • 期間:24時間・アラームを実行するデータポイント:2/2
    • 期間:24時間・アラームを実行するデータポイント:1/2

どうやって欠落データを補っている?

さてここで本題に戻りまして、 CloudWatch はこの欠落データをどのように補っているかを見ていきます。

まず前提となるのが、 CloudWatch は「 評価間隔 の中で欠落したデータポイントがあると、それより過去のデータポイントを取得した上で状態遷移するか判定する」という仕様です。

再掲になりますが、公式ドキュメントを読んでみて下さい。

docs.aws.amazon.com

どうですか?ややこしくないですか?

これ実は言っていることは単純でして、こうなっています。

  • 期間 x 評価期間( M/N の N の方) の期間だけまずはデータを集める
  • データに欠落があり、 M 回分のデータポイントが取得できないと 期間 x 2 だけさらに遡ってデータを取得する
  • 追加で遡った期間内も合わせて M 回分データが揃えば、それらを用いてしきい値と照らし合せた評価ができる
  • 追加で遡った期間内も合わせて M 回分に到達しなかった場合は「欠落データの処理」に設定したルールに則って補完される

要するに、 期間 として設定した時間の 2倍 まで待ってからそれでもデータが足りない場合アラーム状態に遷移(パターン 1)するのかデータ不足(パターン 4)に遷移するのか判断するわけです。

ちなみにですが、手元で検証してみたところ追加で遡る期間として説明した「 期間 x 2 」の時間は 期間が3分や4分といった短い時間を設定すると、もう少し伸びる ことが分かっていますので、ご注意ください。

そのうえで冒頭で取り上げたこの設定をもう一度見てください。

項目 設定値
メトリクス名 Invocations
名前空間 AWS/Lambda
統計 合計
期間 24時間
しきい値 0以下
アラームを実行するデータポイント 1/1
欠落データの処理 欠落データを不正(しきい値を超えている)として処理

しきい値を超えた状態Invocations メトリクスが 0 以下である状態 なので、1回でも観測できれば正常と見なされます。 Lambda の Invocations メトリクスは(標準メトリクスなので)1分ごとに評価されるという点も踏まえると、こういう挙動になります。

  • 24時間データが取得できない状態が発生
  • 最大でそこから 24 x 2 = 48 時間遡ってデータポイントを探す
  • 見つかれば正常状態
  • 見つからなければ「欠落データを不正として処理」し、アラーム状態に遷移

つまり合計 72 時間ずっと Lambda が実行されていない状態まではずっと正常な状態と見なされることになります。

話は分かったので、毎日ほぼ決まった時刻に外部から実行される Lambda 関数の Invocations メトリクスを監視したい

では冒頭の問題に対して改めてどう対処するか考えます。 期間 に設定した時間の 2倍 まで待つので、アラームを評価する間隔である1分単位のズレも許せないのであれば、次のようにすればよいでしょう。

項目 設定値
メトリクス名 Invocations
名前空間 AWS/Lambda
統計 合計
期間 1時間
しきい値 0以下
アラームを実行するデータポイント 22/22
欠落データの処理 欠落データを不正(しきい値を超えている)として処理

こうすれば

1時間以内にデータポイントが 0 である状態を 22 回連続で観測したら、
追加で 1時間 x 2 で 2 時間遡り、それでも見つからなければアラーム状態に遷移する

すなわち

直近24時間で一度も対象の Lambda 関数が実行されなければアラーム状態に遷移する

というアラームが作成できます。

ただし、現実にはある程度のバッファを設けたい場合がほとんどかと思います。 例えば

項目 設定値
メトリクス名 Invocations
名前空間 AWS/Lambda
統計 合計
期間 1時間
しきい値 0以下
アラームを実行するデータポイント 23/23
欠落データの処理 欠落データを不正(しきい値を超えている)として処理

こうすれば

1時間以内にデータポイントが 0 である状態を 23 回連続で観測したら、
追加で 1 時間 x 2 で 2 時間遡り、それでも見つからなければアラーム状態に遷移する

すなわち

直近25時間で一度も対象の Lambda 関数が実行されなければアラーム状態に遷移する

という、最大1時間の猶予を設けたアラームが作成できます。

終わりに

こんなことも出来ますよ、ということで CloudWatch Alarm の少し細かい話についてご説明しました。

冒頭でも申し上げた通り、単純に定時実行される Lambda の起動を監視するのであれば CloudWatch Events で cron 実行すれば済む話ですので、今回はやや特殊なケースであるということをご理解下さい。