ELEMENTS Engineering
😎

AI自動給油許可監視システム (AiQ PERMISSION) における IoT デバイスの死活監視

に公開

はじめに

「AI自動給油許可監視システム(AiQ PERMISSION)」では、現場に設置された IoT デバイス群の死活状態を AWS IoT Core 経由で収集し、Slack に通知するサーバーレス構成を採用しています。

IoT Core / Lambda / DynamoDB / EventBridge で組んでいます。本記事では「いつ」「どこに」「どんな条件で」通知が飛ぶのか、実装パターンを中心に解説します。実装内容は他の IoT デバイス監視にも応用できる汎用的なパターンとして整理しました。

IoT 死活監視で直面する 4 つの課題

IoT デバイス監視を Slack 通知と組み合わせると、必ずぶつかる課題が 4 つあります。これらをすべて素直に実装すると、運用が破綻するか、障害を見逃します。

課題 1: 同一異常で Slack が埋まる

1 つの異常が継続している間、受信するメッセージごとに通知すると、Slack チャンネルが同じアラートで埋め尽くされ、運用チームが本当に重要な情報を見逃します。

課題 2: 一瞬の揺らぎで誤検知する

無線通信や電源の瞬断を単発で異常通知すると、夜間に何度も叩き起こされてしまうので、瞬断に反応しないように実装を工夫する必要があります。

課題 3: 履歴と最新状態を両立しにくい

履歴データは時系列分析のために残したい一方、データ量は増え続けます。リアルタイム判定には最新状態だけを即座に参照したい — 蓄積と即時参照という 2 つの要求をどう両立させるかが課題になります。

課題 4: 完全な沈黙は検知できない

デバイスがクラッシュしてメッセージ送信そのものが止まると、届いたメッセージを見ているだけでは異常に気付けません。「来ないこと」を能動的に確認する仕組みが必要です。

設計の 5 つの原則

上記の課題に対し、本システムは以下の 5 原則で設計しています。

# 原則 解決する課題
1 状態遷移時のみ通知する 課題 1
2 連続性とクールダウンで誤検知を抑える(デバウンス + レートリミット) 課題 1, 2
3 データの寿命に応じてテーブルを分ける 課題 3
4 沈黙は能動的に検知する(定期スキャン) 課題 4
5 経路を意図的に統合・分離する
(5-a 入口を統合 / 5-b 処理を分ける / 5-c 出口を分ける)
横断的(拡張性・責務分離)

以降の実装パターンはすべて、この 5 原則のいずれかに対応します。各セクションのタイトルに原則番号を入れているので、「どの課題への解決策か」を意識しながら読んでみてください。

アーキテクチャ全体像

主な構成要素は以下です:

  • IoT Core: デバイスからの MQTT メッセージを受信、Topic Rule で Lambda に振り分け
  • Lambda: メッセージ処理・通知判定・DynamoDB 更新
  • DynamoDB: 最新状態テーブル + 履歴テーブル(TTL 付き)の 2 段構成
  • EventBridge: 定期実行による沈黙検知(タイムアウト監視)
  • Slack Webhook: 通知配信(正常用 / 異常用の 2 系統)

実装パターン

原則 5-a: 入口を統合する(MQTT トピック設計)

複数カテゴリのヘルスチェックを扱う場合、カテゴリごとに Lambda を作ると数が爆発します。MQTT トピックは health/{category}/{env} のようにカテゴリで分割しつつ、IoT Rule のワイルドカード health/+/{env} と WHERE 句で 1 つの Lambda にルーティングを集約します。

トピック階層の例:

health/{category}/{env}    # ヘルスチェック

統合 IoT Rule SQL:

SELECT *, topic(2) as category
FROM 'health/+/{env}'
WHERE topic(2) = 'category_a'
   OR topic(2) = 'category_b'
   OR topic(2) = 'category_c'
  • topic(N) でトピックの N 階層目を抽出して Lambda イベントに付加できる
  • Lambda 側でカテゴリ別に処理を分岐
  • IoT Rule の数を抑えながら、新しいメトリクスカテゴリを追加しやすい

原則 5-b: 処理を分ける(Lambda の役割分担)

リアルタイム処理と定期実行で Lambda を 2 つに分けます。リアルタイム側は状態遷移検知(原則 1)とノイズフィルタ(原則 2)を担い、定期実行側は沈黙検知(原則 4)を担います。

リアルタイム処理 (IoT MQTT トリガー) 定期実行 (EventBridge)
message-processor timeout-monitor
メッセージ受信 DynamoDB スキャン
状態遷移検知 沈黙検知
DynamoDB 更新 レートリミット
Slack 通知(遷移時) Slack 通知
  • message-processor は MQTT メッセージ(正常/異常)を受信して、Slack に通知
  • timeout-monitor は EventBridge で定期起動し、長期間メッセージが来ていないデバイスを検出して通知

責務を物理的に分離することで、リアルタイム処理が落ちても定期スキャンは生き残るなど、障害の影響範囲を局所化できます。

原則 3: データの寿命に応じてテーブルを分ける(履歴と最新状態)

履歴テーブルは TTL で自動削除しつつ、最新状態テーブルは常に保持する — この異なる寿命を 1 つのテーブルで扱うと、最新状態が誤って消えるリスクが残ります。用途ごとにテーブルを分離するのが安全です。

履歴テーブル:

項目
TTL あり(例: 7 日)
用途 時系列分析、過去調査

最新状態テーブル:

項目
TTL なし
用途 タイムアウト監視、デバウンス管理、レートリミット

キーは複合キーにしています。デバイス × コンポーネント × メトリクス単位で状態を管理できます。

履歴テーブルは TTL で古いデータを自動削除しますが、最新状態テーブルは常に保持する必要があります。1 テーブルにまとめると TTL で消える行と消さない行が混在し、最新状態が誤って消失するリスクがあるため、用途ごとに分離しています。

原則 1・2: 状態遷移時のみ通知し、連続性とクールダウンで誤検知を抑える(3 段フィルタ)

リアルタイム処理側で、3 段階のフィルタを通すことで Slack ノイズを最小化します。

受信メッセージ

[1] 状態遷移検知 ── 状態が変化したときだけ通知 (原則 1)

[2] デバウンス   ── 連続 N 回継続したら通知 (原則 2)

[3] レートリミット ── 同一メトリクスは X 分に 1 回まで (原則 2)

Slack へ

1. 状態遷移のみ通知

正常 ↔ 異常の 遷移時にしか通知しない ことで、Slack の通知数を激減させます。直前の状態と比較し、正常→異常なら「異常発生」、異常→正常なら「復旧」として通知し、変化がなければ何もしません。

2. デバウンス(誤検知抑制)

エッジ側のハートビートが一瞬途切れただけで通知すると、ノイズが多くなります。連続 N 回 異常を受けてから初めて異常通知 を出すように制御します。連続した異常回数をカウントしておき、閾値に達したときだけ通知し、正常が戻ったらカウントをリセットします。

3. レートリミット(重複通知抑制)

同一メトリクスに対しては X 分(例: 10 分)に 1 回しか通知しない ようにします。最後に通知した時刻を最新状態テーブルに記録しておき、一定時間が経つまでは再通知をスキップします。

原則 4: 沈黙は能動的に検知する(定期スキャン)

EventBridge が一定間隔で timeout-monitor Lambda を起動し、最新状態テーブルを走査して 最終受信から閾値以上経過したメトリクス を検出します。閾値を超えていればタイムアウトとみなして Slack に通知します。

タイミング設計の例:

項目
タイムアウト閾値 12 分
EventBridge チェック間隔 9 分
Slack レートリミット 10 分
最悪検知遅延 21 分(閾値 + チェック間隔)
デバウンス連続異常回数の閾値 3 回

EventBridge の間隔をタイムアウト閾値より短く設定するのがポイントです。これにより最悪ケースの検知遅延を「閾値 + 間隔」に抑えられます。

原則 5-c: 出口を分ける(Slack チャンネル構成)

入口を統合した一方、出口(Slack チャンネル)は意図的に分離します。緊急対応が必要な異常と、確認用の正常通知を同じチャンネルに混ぜると、運用チームの注意リソースを浪費するためです。

チャンネル 用途 通知色
OK チャンネル 正常遷移通知 🟢 緑
Alert チャンネル 異常検知通知・タイムアウト通知 🔴 赤 / 🟠 黄

まとめ

IoT デバイスの死活監視 + Slack 通知を組むときに効くパターンを、5 つの原則として整理しました。

  1. 状態遷移時のみ通知する — 同一異常の連投を防ぐ
  2. 連続性とクールダウンで誤検知を抑える — 瞬断ノイズを除去
  3. データの寿命に応じてテーブルを分ける — 履歴と最新状態を両立
  4. 沈黙は能動的に検知する — 来ないことに気付く
  5. 経路を意図的に統合・分離する — 入口統合・処理分離・出口分離で拡張性と運用負荷をバランス

同じような IoT デバイス監視を構築する方の参考になれば幸いです。

ELEMENTS Engineering
ELEMENTS Engineering

Discussion