ほとんどの IoT の図には、デバイス、クラウドのアイコン、ダッシュボードの 3 つの箱しかありません。大事な疑問はその間にあります。ブローカーは、どのデバイスが話しているのかをどうやって知るのか。拠点が 1 時間接続を失ったらどうなるのか。どのメッセージが失われたり、2 回届いたりしうるのか。何千ものセンサーの中の 1 つに、コマンドはどうやって届くのか。
IoT のアーキテクチャ図は、こうした疑問に図の上で答えるべきです。この記事では、冷凍倉庫の温度監視という具体的な例で図を作り、各ホップの背後にある判断を順に見ていきます。MQTT とその QoS レベル、トピックの設計、デバイスのアイデンティティ、オフラインの拠点とイベント時刻、そしてデータの行き先です。
IoT システムのレイヤー
| レイヤー | 役割 | この例では |
|---|---|---|
| デバイス | 計測や操作を行う。リソースが限られ、電池で動くことが多い | 冷凍庫センサーとドアセンサー |
| エッジゲートウェイ | ローカルのプロトコルを話し、オフライン時はバッファーし、上流へ発行する | 拠点ゲートウェイ |
| 取り込み | デバイスを認証し、メッセージをトピックでルーティングする | MQTT ブローカー |
| ストリーム転送 | 多くのコンシューマー向けの、永続的で再生可能なログ | Kafka |
| 処理 | 集計、ルール、データの付加 | ストリーム処理 |
| ストレージ | クエリ用の最新データ、再生用の生データ | 時系列ストア、生データのアーカイブ |
| アプリケーション | ダッシュボード、アラート、デバイス管理 | ダッシュボード、アラート、デバイス管理 |
すべてのシステムにすべてのレイヤーが必要なわけではありません。セルラーや Wi-Fi のモジュールを持つデバイスは、ゲートウェイを通さずにブローカーへ直接発行することもよくあります。実際に運用しているレイヤーを描きましょう。
なぜ MQTT なのか
MQTT は、クライアントがブローカーに接続し、トピックを介してメッセージをやり取りするパブリッシュ/サブスクライブ型のプロトコルです。2019 年 3 月から OASIS 標準となっている MQTT 5.0 の仕様は、MQTT を軽量でオープン、実装が簡単なプロトコルとして説明しています。IANA には、TLS なしの MQTT 用に 1883 番ポート、TLS 上の MQTT 用に 8883 番ポートが登録されています。拠点の外に出るものはすべて 8883 を使うべきです。
各メッセージは、サービス品質(QoS)のレベルを指定して発行されます。
| QoS | 保証 | 用途 |
|---|---|---|
| 0 | 最大 1 回、確認応答なし | 次の値が失われた値の代わりになる、頻繁な測定値 |
| 1 | 最低 1 回、確認応答あり、重複の可能性あり | 冪等なコンシューマーを前提とした、ほとんどのテレメトリーとコマンド |
| 2 | ちょうど 1 回、4 段階のハンドシェイク | 下流で重複を処理できない、まれなケース |
QoS はエンドツーエンドではなく、ホップごとに適用されます。サブスクライバーがメッセージを受け取るときの QoS は、発行時の QoS と、そのサブスクリプションに許可された QoS のうち低いほうです。QoS 1 ではブローカーが重複を配信することがあるため、どのコンシューマーも同じ測定値を 2 回受け取っても問題ないようにする必要があります。デバイスが生成するメッセージ ID や、センサーごとのタイムスタンプがあれば簡単です。
MQTT 5 には、図に載せる価値のある機能がいくつか加わっています。
- 保持メッセージ(retained)。 ブローカーはトピックの最後のメッセージを保持し、新しいサブスクライバーに送ります。デバイスの現在の状態に向いています。
- ウィルメッセージ(will)。 クライアントの接続が正常な切断なしに切れると、ブローカーはそのクライアントのウィルメッセージを発行します。これでゲートウェイはオフラインになったことを知らせられます。
- メッセージの有効期限。 配信が始まる前にメッセージの期限が切れると、ブローカーはそれを削除します。一定時間だけ有効なコマンドに使いましょう。
- 共有サブスクリプション。 複数のコンシューマーが 1 つのサブスクリプションを共有し、各メッセージはそのうち 1 つにだけ届きます。これで、ストリーミング基盤へのブリッジをスケールアウトできます。
トピックの設計
トピックは階層構造になっており、サブスクライバーは 1 階層に +、残りすべてに # を使います。site/{site}/device/{id}/telemetry のような構成にすると、1 つの拠点、1 つのデバイス、またはすべてを購読でき、ブローカーは各デバイスを自分のトピックだけに制限できます。$ で始まるトピックはブローカー用に予約されており、ワイルドカードで始まるフィルターはそれらに一致しません。
トピックはルーティングと権限のために使いましょう。スキーマのバージョンと測定時刻はペイロードに入れます。そうすれば、すべてのサブスクリプションを壊さずに変更できます。
デバイスのアイデンティティ
各デバイスやゲートウェイには、自分たちのデバイス CA が署名した専用の X.509 証明書を持たせ、ブローカーにはクライアント証明書を必須にさせます。証明書のサブジェクトがクライアント ID になり、ブローカーはそれに基づいてトピックを許可します。そうすれば、盗まれたデバイスは個別に失効させられ、何千台ものファームウェアに共有パスワードが残ることもありません。証明書を製造時に発行するのか初回起動時に発行するのか、期限切れの前にどうローテーションするのかを決め、CA を図に描きましょう。
オフラインの拠点とイベント時刻
拠点は接続を失います。測定値をローカルにバッファーするゲートウェイは、回線が戻ると 1 時間分のデータを一度に送るので、データは遅れて、順不同で届きます。各測定値には測定した時刻を記録し、ゲートウェイの時計を同期させ、遅延を見込んでイベント時刻で処理しましょう。「冷凍庫が 10 分間 -18 °C を超えている」のようなアラートは、測定値が届いた時刻ではなく、測定された時刻に基づいて計算しなければなりません。同じ考え方は、どんなイベント駆動アーキテクチャにも当てはまります。
例:冷凍倉庫の監視
各倉庫には、30 秒ごとに報告する約 40 台の冷凍庫センサーと、変化があったときに報告する 12 台ほどのドアセンサーがあります。拠点ゲートウェイはローカルの無線で測定値を集め、オフライン時にはバッファーし、相互 TLS を使って QoS 1 で MQTT ブローカーへ発行します。ブローカーは各クライアント証明書をデバイス CA に照らして確認します。共有サブスクリプションを使うブリッジがテレメトリーを Kafka にコピーします。あるコンシューマーは生のメッセージをオブジェクトストレージにアーカイブし、ストリーム処理はイベント時刻で 1 分ごとの集計値を計算して、ダッシュボード用の時系列ストアに書き込み、温度のアラートを上げます。デバイス管理は、コマンドと設定をブローカー経由で送り返します。
ダイアグラムの内容
- Warehouse site
- Freezer sensors ×40
- Door sensors ×12
- Site gateway
- MQTT broker
- Device CA
- Device management
- Kafka
- Raw archive
- Stream processor
- Time-series store
- Dashboards
- Temperature alerts
- every 30 s
- on change
- telemetry, QoS 1
- commands
- checks client certificates
- commands, config
- bridge
- raw messages
- aggregates
- rules
図の読み方は次のとおりです。
- 左側は拠点。 センサーは拠点ゲートウェイとだけ通信します。ブローカーからこの拠点で見えるクライアントは、ゲートウェイだけです。
- 中央はブローカー。 テレメトリーは QoS 1 で上がっていきます。破線のエッジは、拠点へ下りるコマンドと、デバイス CA に照らした証明書の確認です。
- 右側はデータの経路。 Kafka は生データのアーカイブとストリーム処理に供給し、ストリーム処理は集計値を時系列ストアに書き込み、アラートを上げます。
- Markdown ブロック には、トピックの構成、トピックごとの QoS、アイデンティティのルールを書いています。
ラベルに書くこと
- 拠点からブローカーへのリンクには プロトコル、ポート、QoS を書きます。
- デバイスの種類ごとのメッセージレート。 40 台のセンサーが 30 秒ごとに送れば、拠点あたり毎分 80 メッセージです。ブローカーのサイズを決める前に、拠点の数を掛けましょう。
- アイデンティティ。 どの証明書か、どの CA か、証明書をどうローテーションし、どう失効させるかです。
- バッファー。 ゲートウェイがオフラインでどれだけの時間データを保持できるかです。
- 各ストアの 保持期間 を書きます。ストリームでは数時間、アーカイブでは数年といった具合です。
- コマンドの経路。 メッセージの有効期限や、未知のコマンドを受け取ったデバイスの動作も含めて書きます。
よくある間違い
- デバイス間で共有する認証情報。 1 つのパスワードが漏れると、すべてのデバイスが開いてしまいます。デバイスごとに証明書を使いましょう。
- インターネット上の 1883 番ポート。 TLS がなければ、測定値も認証情報も平文で流れます。
- ちょうど 1 回の配信を前提にする。 QoS 1 では重複が届くことがあります。あらゆる場所で QoS 2 に頼るのではなく、コンシューマーを冪等にしましょう。
- 到着時刻で処理する。 バッファーされたデータが誤ったアラートを出したり、本物のアラートを隠したりします。
- デバイスがデータベースに直接書き込む。 ブローカーによる認証、バッファリング、ファンアウトが失われます。
- 更新の経路がない。 ファームウェアの更新や設定の変更もアーキテクチャの一部です。図に描きましょう。
- デバイスへのワイルドカード権限。
#を購読できるデバイスは、ほかのすべてのデバイスのデータを読めてしまいます。
ネットワークトポロジー図では、IoT の図がたいてい省いてしまう拠点内のネットワークを扱っています。
Archboard で作る
- Google または GitHub でサインインし、図を作成します。
- 拠点用に基本パックの フレーム を追加します。その中に、ネットワークパックから、種類を IoT / スマートホームにした デバイス をセンサーのグループごとに追加し、拠点ゲートウェイ用に種類をモデム / ゲートウェイにした デバイス をもう 1 つ追加します。
- C4 モデルパックから、MQTT ブローカー、デバイス管理、ストリーム処理用の コンテナ ノードと、時系列ストア用の データベース を追加します。
- セキュリティパックから、デバイス CA 用の 証明書 を追加します。
- データパックから Kafka と、生データのアーカイブ用の オブジェクトストア を追加します。DevOps パックから、ダッシュボード用の メトリクス と アラート のノードを追加します。
- 左から右へつなぎ、アップリンクには QoS のラベルを付け、コマンドと証明書の確認は破線にし、トピックの設計を書いた基本パックの Markdown ブロックを追加します。
この例で使うダイアグラムノードは 14 個で、Free プランの範囲に収まります。ネットワーク図のページでは、ネットワークパックのほかのノードを紹介しています。
よくある質問
IoT デバイスには MQTT と HTTP のどちらを使うべきですか? MQTT は接続を開いたままにするので、クラウドからコマンドをプッシュでき、メッセージごとのオーバーヘッドも小さくて済みます。HTTP は、まれにしか報告せず、コマンドを受け取ることもないデバイスにとってはよりシンプルです。
どの QoS レベルを使えばよいですか? ほとんどのテレメトリーとコマンドには、重複を許容できるコンシューマーを前提に QoS 1 を使います。失っても構わない高頻度の測定値には QoS 0 を使います。QoS 2 は、下流で重複を処理できない場合に限りましょう。
MQTT ブローカーがあれば、Kafka は必要ですか? 必ずしも必要ではありません。ブローカーは接続中のサブスクライバーにメッセージをルーティングするもので、再生できる長期的なログではありません。複数のコンシューマーが同じデータを必要とするときや、履歴を再処理する必要があるときに、ストリーミング基盤を加えましょう。
デバイスはどうやって証明書を受け取りますか? 製造時、または初回起動時にプロビジョニングサービスを通じて受け取ります。それぞれが自分の鍵ペアを持ちます。デバイスにセキュアなハードウェアがあれば、秘密鍵はそこに保管しましょう。
ルールはエッジとクラウドのどちらで実行すべきですか? 開けっ放しのドアに対するローカルのアラームのように、障害中も動く必要があるルールはゲートウェイで実行します。多くの拠点のデータを必要とするルールはクラウドで実行します。