Azure IoT Hub & Device Provisioning Service でリージョン間HAを実施する

Azure の IoT Hub は非常に簡単に利用を開始できます。
例えば、[クイック スタート:デバイスから IoT ハブに利用統計情報を送信してバックエンド アプリケーションで読み取る (.NET)](https://docs.microsoft.com/ja-jp/azure/iot-hub/quickstart-send-telemetry-dotnet)をたどれば、中身がよくわからなくても30分もあればIoT Hubの利用を開始できます。

今回は、実際の運用を意識した際に避けては通れない話題、"HA(高可用性)/DR(ディザスター リカバリー)"みたいな話題を扱いたいと思います。

# はじめに 
こちらで取り扱うのは、デバイスとIoT HubのHA/DRです。
実際には、Azure内(IoT Hubより後ろ側)もHA/DR対策が必要になりますが、ここでは扱いません。
（その多くは、一般的なクラウドネイティブな構成となります。）
また、長くなるので細かい手順は記載しません。
参考ドキュメント等は明示します。
HA/DRの話題はお金をかけたら正直いくらでもやれますが、これはIoTのお話しです。基盤そのものがお金を生まないIoTですから、今回も省コストで最大のメリットを享受できるように考えました。

# Azureドキュメントの確認
Azureのドキュメントでは、こちらをご確認ください。
[IoT Hub の高可用性とディザスター リカバリー](https://docs.microsoft.com/ja-jp/azure/iot-hub/iot-hub-ha-dr)

中身をまとめると以下になります。
HA/DRは、自身が提供するサービスのレベルに応じて以下から選択する

|No.|方式|軽く説明|
|---|---|---|
|1|リージョン内 HA|IoT Hubは単体で実現している(内部的に冗長構成になっている)。|
|2|リージョン間 DR|リージョンごと落ちた場合に備えて、ペアリージョン(例えば東西日本)にフェールオーバーできます。Microsoftが勝手にやるパターンと自身で手動でやるパターンを準備しました。|
|3|リージョン間 HA の達成|上の２種類ではビジネス上問題がある場合、自分で頑張って作ってください。|

リージョン内HAでは、一時的な障害等の発生は否定できません。よって、"落ちないように神様にお祈りする"のではなく"落ちた場合の回復方法の検討"を実施してください。
例えば、デバイス側でテレメトリーを可能な限り保持するのも手段ですし、ビジネスの内容次第では、落ちたらごめんなさいするっていうのも立派な手段です。
その手段の１つが別リージョンへのフェールオーバーになります（リージョン間DR）。
フェールオーバーの問題は、データをロストすることが宣言されていることと、フェールオーバーそのものに時間がかかるということです。

**↓Azureのドキュメントより抜粋**
"フェールオーバーのRPOについて"
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439826/43649381-056b-ca2f-a745-a970fc108f74.png)
"HA/DRオプションについて"
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439826/050f5fd7-8ae1-23ee-68d1-baa9ab1aba48.png)

上記記載の内容でビジネス上の要件を満たせる場合は、この先のお話しは不要となります。
個人的には"最大26時間後の回復を許容するIoTサービスって何だ？"と思っているので、"リージョン間 HA"の実現が必要であると判断します。

IoT Hubのリージョン間HAの組み方について、ドキュメントには「どのIoT Hubを使えば良いか？を教えてくれるコンシェルジュなサーバーを準備してくれ」と書いてあります。
こんな普段まったく利用しないコンシェルジュサービスを構築・維持するのは無駄ですので、今回は"[Device Provisioning Service](https://docs.microsoft.com/ja-jp/azure/iot-dps/about-iot-dps)(DPS)"で代替したいと思います。
DPSにつきましては、超概要をこちら"[IoT Hub Device Provisioning Service とは？超概要](https://qiita.com/mstakaha1113/items/231c859d7427b466d4ad)"に記載しました。必要な方はご確認いただけますとうれしいです。

# 全体像
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439826/21384407-1ecd-e3e7-8e11-8bf914ae2b5b.png)
はじめにこの関係を構築します。

- DPS － IoT Hub ： DPSが問い合わせを受け付けた際に接続先の選択肢となるIoT Hubを登録する
- デバイス － DPS ： IoT Hubとの接続ができない場合はDPSに接続先を問い合わせる
- デバイス － IoT Hub ： 接続情報を持っている場合、デバイスはIoT Hubに接続してテレメトリーデータを送信する

作り方はこちら"[IoT Hub Device Provisioning Service とは？を作りながら理解する](https://qiita.com/mstakaha1113/items/1ccaaf445ea6f7ffd76c)"に記載しましたので、必要な方はご確認いただけますとうれしいです。

今回は上記に"Azure内の監視機能"を加えます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439826/a5ee32a5-feb9-c561-1951-1222b3773fd5.png)
IoT Hubを状態監視して、DPSからの接続先として妥当な状態では無い(使えない)場合はDPSからの問い合わせを実施しないように、妥当な状態になれば(使えるように復旧)すれば問い合わせ候補として復活させるという動作を行います。

# 監視機能として何を使うのか
IoT Hubの正常性を監視する方法は主に2つあります。
1つはメトリックデータを確認して、通常と異なる動作をしていたら問題とする方法です。
1つはIoT Hubそのもののリソースとしての正常性を確認する方法です。
VMに例えるなら、CPUやメモリの状態を見るのがメトリックの監視、HOSTコンピューターから動作状態を確認するのがリソースの監視です。
容易で中を細かく確認できるのは前者ですが、リソースそのものの動作が怪しいことを確認するなら後者を選択すべきかと思います。
今回は後者"リソースの正常性"を監視します。

# リソース正常性
## Azureの状態確認方法は見たいスケールで異なる
リソース正常性とは、現在自分が利用している具体的なリソースそのものの正常性を意味します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439826/0165b66d-acb9-bafe-5973-afa0233984c7.png)
この図の中でいいますと、"～～～1.azure-devices.net"と"～～～2.azure-devices.net"が該当します。
それより上位の部分は、"サービス正常性"というもので確認ができます。
詳しくは、以下のリンクを参照ください。
[Azure Service Health とは](https://docs.microsoft.com/ja-jp/azure/service-health/overview)
[Azure の状態の概要](https://docs.microsoft.com/ja-jp/azure/service-health/azure-status-overview) ：Azure全体に関して状態を確認
[Service Health の概要](https://docs.microsoft.com/ja-jp/azure/service-health/service-health-overview) ：各リージョン内のもう少し細かい状態を確認
[Resource Health の概要](https://docs.microsoft.com/ja-jp/azure/service-health/resource-health-overview) ：自身が利用中のリソースの状態を確認

## ポータル上でリソース正常性を確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439826/be5f37de-a5ac-02b6-c43c-411d25614075.png)

個々のリソースを開き、こちらをクリックしてください。
そのリソースが正常か否かについての情報が確認いただけます。

# リソース正常性のチェックからDPSの構成変更までの流れ
方式は2つあります。
方式1：Resource Health アラート発報をMonitorで検知、Automationで環境変更を実施する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439826/8fd3898f-2153-da4f-2431-16b068819272.png)

方式2：Azure REST APIを使用して監視、状態変化に応じてAutomationで環境変更を実施する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439826/0fcc4faf-befe-0bab-9dba-ee90160c068e.png)

前半がリソース正常性を取得するための2つの方法をさしています。
後半はAutomationでARMテンプレートを実行してやろうという話題です。
Automationじゃなくても対応できます。例えばアラートをAzure DevOpsに流して処理しても良いと思いますし、各種Webhookなサービスを使っていただければと思います。

# Resource Health アラート発報をMonitorで収集する方法

※現在(2020年6月1日)時点ではプレビュー機能です
[Resource Manager テンプレートを使用して Resource Health アラートを構成する](https://docs.microsoft.com/ja-jp/azure/service-health/resource-health-alert-arm-template-guide)
こちらを使って設定してください。
アラートのスコープというものを設定することになりますが今回はIoT Hubを指定しますので、IoT Hubの画面から"プロパティ"を選択し
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439826/1cf60748-efac-c516-1061-64924fab5afa.png)
こちらの値を利用してください。
リソース単体でアラートを検出したい場合は"リソースID"を、リソースグループで検出したい場合は"リソースグループID"を利用します。

こちらも参照ください。他にもいろいろなサービスで値が取れます。
[Azure Resource Health で利用できるリソースの種類と正常性チェック](https://docs.microsoft.com/ja-jp/azure/service-health/resource-health-checks-resource-types#microsoftdevicesiothubs)

Monitorがアラート検知したあとAutomationをキックすることとなります。
その部分はこちらを参考にしてください。
[Azure Automation Runbook をトリガーするアラートを使用する](https://docs.microsoft.com/ja-jp/azure/automation/automation-create-alert-triggered-runbook)


# Azure REST APIを使用して監視する方法

自動化はREST APIを叩き続けるだけですので、そもそもの呼び出し方についてこちらを参考にしてください。
[REST API を使用して Resource Health を取得する](https://docs.microsoft.com/ja-jp/azure/service-health/service-health-rest)
[Azure REST API Reference](https://docs.microsoft.com/ja-jp/rest/api/azure/)

今回、実際に呼び出すAPIはこちらになります。
[Availability Statuses - Get By Resource](https://docs.microsoft.com/ja-jp/rest/api/resourcehealth/availabilitystatuses/getbyresource)
上記REST APIは、現在の状態を収集できますが、状態の遷移は追えません。よって、別途状態(使える、使えない)を保持する機構が必要になります。(コスト的には、REST API投げつけるLogicAppsをスケジュール起動で作って、状態をBlobに保存しておくのが良いと思います。)

# AutomationでDPSを設定変更するARMテンプレート
ARMテンプレートは、まずDPSから収集します。
AzureポータルでDPSを開き"テンプレートのエクスポート"を選択、ダウンロードします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439826/2d995fa9-783c-cf80-cf98-998bcfc3e97b.png)
内容を適宜書き換えて、Automationで使います。
(センスとかは別にして)今回最低限必要なこととして、例えばDPSの名前とかサイズとか決め打ちで良いのではないでしょうか。
接続先として採用されるIoT Hubの情報(connectionString)は正しいものを各IoT Hubから収集する必要がありますし、1つのテンプレートを動的に利用するのか、それともパターン別に複数個のテンプレートを準備するのか、決めて作成する必要があります。

# 動作確認は？
自分でサービスを落とせないので、完全なものはできません；；
場面場面で切り分けて動作確認をお願いします。

# ちなみにデバイス側は？
デバイスSDKを利用する前提で記載しますが、通信の再試行プロセスは存在します。
こちらを参考にしてください。
[Azure IoT Hub device SDK を使用して、接続と信頼できるメッセージングを管理する](https://docs.microsoft.com/ja-jp/azure/iot-hub/iot-hub-reliability-features-in-sdks)

これに加えて、必要であればIoT HubとDPSへの再接続をご自身でコーディングください。
例えば
1. 通信エラーが発生したら、接続はそのままに再送信を実行する(1秒後、2秒後、4秒後・・・)
2. それでもダメならIoT Hubに対して再接続を実施して再送信を実行する(1秒後、2秒後、4秒後・・・)
3. それでもダメならDPSに再接続先を問い合わせてIoT Hub再接続の後、再送信を実行する
4. それでもダメなら可能な限り必要な範囲のデータをローカル保持しつつDPSに問い合わせを継続する
このような感じでいかがでしょうか。
通信網やIoT Hubの一時的な障害等を意識しつつ、最終的にはデバイスが許す範囲でデータを保持して通信の再開を待つという姿勢です。
電源を入れたら上記のようなルートをたどって通信を始めるようにロジックを組めば、テストは少なくて済むのではないでしょうか。

# IoT Hubって入れ替えても動作に問題は無いの？
IoT Hubは例えば"プロパティ"を保持できます。よってプロパティを利用する場合、影響はあります。そこは、プロパティ情報のマスタを別途"機器マスタ"のようなものでDBに保持しておいて、そこから新規接続したらロードするような仕掛けを検討します。
「余分に作りこみが必要なの？」と感じられるかもしれませんが、ユーザーとデバイスを紐付けたりしていこうと思うと、どのみちそのような仕掛けは作らざるを得ないと思いますので、そこをうまく活用してください。(こういう機能が無いと、例えば故障機器の交換や一時的な代替機の貸し出し等を考えた時にサービスを回すのが難しくなりますので、留意ください。)

# さいごに
今回は、DPSを使ったリージョン間HAを考えてみました。
実際の運用を意識すると、例えば「メッセージ数上限に達したらどうする？」とかも検討した方がいいですが、これもAzure Monitor等を使えばできます。
"メトリック"と"リソース正常性"は運用を手助けしてくれる重要な要素ですので、必要に応じて確認いただけると良いかと思います。