Nested Edge を Raspberry Pi で試す

Azure IoT Edge に2020年末に、Azure IoT Edge を Transparent Gateway として使い、Child Device として IoT Edge を接続できるようになった、Nested Edge という機能を、その筋の開発者なら大抵持っている、Raspbery Pi(3以上)で試すための Tips 集。
OS は、現在最新の Raspbian Buster を使用する。

※ 2021/1/8時点で、一応「ダウンストリーム IoT Edge デバイスを Azure IoT Edge ゲートウェイに接続する (プレビュー)」という Docs の説明はあるものの、多分これを参照してもうまく設定できないと思われるので、実際に動かせたステップを紹介する。

※ 以下の説明は、「Azure IoT Edge Runtime 1.2 新機能のご紹介 - Nested Edge」という素晴らしい技術ブログを元に、Raspberry Pi、Raspbian でやる場合に躓きそうな部分説明する。


内容

Nested Edge は、Raspberry Pi を2セット利用する。それぞれ、以下の hostname 、IP アドレスであると仮定して話を進める。また、Raspberry Pi を接続するローカルネットで、DNS が利用できない場合は、Static IP Address を付与してやるべきではあるが、DHCP でも一定期間同じ IP アドレスで接続されるので、それを活用する。2セットあるので混乱しないように作業を進めてほしい。

種類 hostname IP Address
親デバイス raspi-parent 192.168.0.108
子デバイス raspi-child 192.168.0.155

手順は、以下の通り。

  1. ホスト名の設定
  2. Raspbian Buster への IoT Edge Preview 版インストール
  3. 親デバイスと子デバイスの接続用の証明書作成と、親デバイスへの証明書インストール
  4. 親デバイスへの、Deploy 設定と、config.yaml の設定
  5. Azure IoT Device SDK を使った IoT アプリの接続テスト(寄り道)
  6. 子デバイスへの証明書インストール
  7. 子デバイスへの、Deploy 設定と、config.yaml の設定、Nested Edge の動作確認
  8. IoT Edge Module 内で、Device SDK アプリを起動し、そのアプリを 子デバイスとして扱う。

ホスト名の設定

Raspbian のインストールが終わったら、以下の二つのファイルの raspberrypi を親、子、それぞれの名前に変更する。

  • /etc/hostname
  • /etc/hosts

変更が終わったら、Reboot して確定
※ 別に変更しなくてもいいんだが、Putty 等で Remote Shell 上で作業する場合に区別がついてよいので推奨


Raspbian Buster への IoT Edge Preview 版インストール

実は、2021/1/8 現在、Raspbian 対応は正式には、Stretch までで、Buster には正式対応していない。でも動く。
この作業は、親、子、両方で行う。 まずは、Docker をインストール

curl -sSL get.docker.com | sh && sudo usermod pi -aG docker && sudo reboot

一旦リブートするので、リブート後に、以下を実行。

curl -L https://github.com/Azure/azure-iotedge/releases/download/1.2.0-rc1/libiothsm-std_1.2.0_rc1-1-1_debian9_armhf.deb
 -o libiothsm-std.deb
sudo dpkg -i ./libiothsm-std.deb
curl -L https://github.com/Azure/azure-iotedge/releases/download/1.2.0-rc1/iotedge_1.2.0_rc1-1_debian9_armhf.deb
 -o iotedge.deb
sudo dpkg -i ./iotedge.deb

※ Nested Edge は、Preview 版の1.2.0-RC1を使用
※ Busterhは、Debian 10 だが、まぁいいだろう。


親デバイスと子デバイスの接続用の証明書作成と 親デバイスへの証明書インストール

冒頭に紹介した技術ブログの「証明書とコピー - 親エッジデバイス」に記載された方法で、子側の Raspberry Pi が親側の Raspberry Pi に接続するための証明書の作成と、親側の Raspberry Pi への証明書のインストールを行う。
※ 証明書のインストールの部分で、

ルートCA証明書インストール
sudo cp $HOME/azure-iot-test-only.root.ca.cert.pem /usr/local/share/ca-certificates/azure-iot-test-only.root.ca.cert.pem.crt
sudo update-ca-certificates

/usr/local/share/ca-certificates にコピーする時に拡張子が変わっていることに注意。


親デバイスへの、Deploy 設定と、config.yaml の設定

親側(raspi-parent)の、IoT Edge の設定を行う。冒頭に紹介したブログの「IoT Edge Runtime 構成 - 親エッジデバイス」に従って、raspi-parent の /etc/iotedge/config.yaml を編集する。
前述の IP アドレスの構成の場合、Raspberry Pi での実行なので、

certificates:
  device_ca_cert: "/home/pi/iot-edge-device-ca-parent-device-full-chain.cert.pem"
  device_ca_pk: "/home/pi/iot-edge-device-ca-parent-device.key.pem"
  trusted_ca_certs: "/home/pi/azure-iot-test-only.root.ca.cert.pem"
hostname: "192.168.0.108"

となる。
次に、「親エッジデバイスへのモジュールデプロイ」に従って、Azure Portal で、raspi-parent にモジュールのデプロイ設定を行う。
※ 長い文字列が多いので、可能な限りブログからコピペして、スペルミスによるお間抜けな失敗をしないようにすること。

Azure Portal での設定後、しばらくして、raspi-parent のシェル上で、指定したモジュールが配置され動いていることが確認できる。

pi@raspi-parent:~ $ sudo iotedge list
NAME             STATUS           DESCRIPTION      CONFIG
IoTEdgeAPIProxy  running          Up 21 minutes    mcr.microsoft.com/azureiotedge-api-proxy:latest
edgeAgent        running          Up a day         mcr.microsoft.com/azureiotedge-agent:1.2.0-rc3
edgeHub          running          Up 21 minutes    mcr.microsoft.com/azureiotedge-hub:1.2.0-rc3
registry         running          Up 21 minutes    registry:latest

Azure IoT Device SDK を使った IoT アプリの接続テスト(寄り道)

ここで、若干横道にそれて、Azure IoT Device SDK を使ったアプリを、raspi-parent で動作する親 IoT Edge を介して、Azure IoT Hub に接続してみる。
※ この機能は、元々あった機能である。
アプリは、IoTDeviceAppForGatewayEdge を使う。このアプリは、.NET Core 上で実行するアプリなので、まず、.NET Core SDK を raspi-parent にインストールする。2021/1/8 現在、.NET Core の最新バージョンは 5.0 なのでそれを使う。

pi@raspi-parent:~ $ curl -L https://download.visualstudio.microsoft.com/download/pr/567a64a8-810b-4c3f-85e3-bc9f9e06311b/02664afe4f3992a4d558ed066d906745/dotnet-sdk-5.0.101-linux-arm.tar.gz
 -o dotent-sdk.tar.gz
pi@raspi-parent:~ $ sudo mkdir -p $HOME/dotnet
pi@raspi-parent:~ $ sudo tar zxf dotnet-sdk.tar.gz -C $HOME/dotnet
pi@raspi-parent:~ $ export DOTNET_ROOT=$HOME/dotnet
pi@raspi-parent:~ $ export PATH=$PATH:$HOME/dotnet

pi ユーザーでログインする度に、.NET Core の環境が有効になるために、$HOME/.bashrc の最後に、

export DOTNET_ROOT=$HOME/dotnet
export PATH=$PATH:$HOME/dotnet

の二行を追加する。
このリポジトリを clone する。

pi@raspi-parent:~ $ git clone https://github.com/ms-iotkithol-jp/NestedEdgeOnRaspberryPi.git
pi@raspi-parent:~ $ cd NestedEdge/IoTDeviceAppForGatewayEdge

Azure Portal で、IoT Device に、leaf-device という名前でデバイスを登録し、接続文字列をコピーし、その接続文字列に以下の様に Gateway 情報を加える

HostName=<myiothub>.azure-devices.net;DeviceId=leaf-device;SharedAccessKey=xxxyyyzzz;GatewayHostName=192.168.0.108

最後の IP アドレスは、raspi-parent の IP アドレスである。 この接続文字列を""で囲って引数として使い、アプリを起動する。

$ dotnet run "HostName=...;GatewayHostName=192.168.0.108"

※ ""を必ずつけること IoTDeviceAppForGatewayEdge は、10回、Azure IoT Hub にメッセージを送信し、Shell 空のキー入力待ちになる。このアプリへの C2D メッセージ送信、Device Twins Desired Properties 更新、Direct Method コールが全てできるので、試してみること。 ※ このアプリは、Visual Studio のリモートデバッガーのアタッチを想定している。利用しない場合は、Program.cs の1行目をコメントアウトすること。
一見、IoT Hub に直接接続しているようにみえるが、raspi-parent の IoT Edge Runtime を介しての接続・通信になっている。

次に、.NET Core SDK のインストールと同じアプリの実行を raspi-child 側でも試してみる。
pi@raspi-child:~/NestedEdgeOnRaspberryPi/IoTDeviceAppForGatewayEdge $ dotnet run "HostName=.azure-devices.net;DeviceId=leaf-device;SharedAccessKey=xxxxxxxxyyyyyyyy;GatewayHostName=192.168.0.108" waiting for debugger attach

waiting for debugger attach
Connection string of IoT Hub Device:HostName=...;DeviceId=leaf-device;SharedAccessKey=xxxxxxxxyyyyyyyy;GatewayHostName=192.168.0.108
Exception TLS authentication error.

当然であるが、証明書のインストールをしていなければ、raspi-parent とは接続できないので、実行は失敗する。


子デバイスへの証明書インストール

前述の、親デバイス側で作成して子デバイス側にコピーした証明書をインストールする。「証明書作成とコピー - 子デバイス」に従ってインストールする。
※ 親デバイスのパートでも書いたが、こちらも、/usr/local/share/ca-certificates にコピーする時の拡張子の違いに注意すること。
証明書のインストールが終わったら、前のステップで行った、IoTDeviceAppForGatewayEdge をもう一度実行してみよう。今度は raspi-parent への接続が成功し、Azure IoT Hub へのメッセージ送信、コマンド等の送受信が問題なく実行されるので、試してみてね。
以上で、寄り道は終わり。


子デバイスへの、Deploy 設定と、config.yaml の設定、Nested Edge の動作確認

寄り道して、IoT Edge ではない子デバイスの接続も体験したので、次に本題の IoT Edge を子デバイスとしての接続を試す。
raspi-child の /etc/iotedge/config.yaml を、「IoT Edge Runtime 構成 - 子エッジデバイス」に従って編集する。
Raspberry Pi の場合、かつ、IP アドレスの場合について、念のため以下に設定を記載する。

certificates:
  device_ca_cert: "/home/pi/iot-edge-device-ca-child-device-full-chain.cert.pem"
  device_ca_pk: "/home/pi/iot-edge-device-ca-child-device.key.pem"
  trusted_ca_certs: "/home/pi/azure-iot-test-only.root.ca.cert.pem"
hostname: "192.168.0.150"
parent_hostname: "192.168.0.108"
agent:
  name: "edgeAgent"
  type: "docker"
  env: {}
  config:
    image: "192.168.0.108:8000/azureiotedge-agent:1.2.0-rc3"
    auth: {}

config.yaml の編集が終わったら、sudo systemctl restart iotedge で IoT Edge Runtime を再起動する。しばらく経つと、SimulatedTemperatureSensor からのテレメトリーデータ送信が確認できる。 sudo iotedge list で試すと必要なモジュールがきちんと Pull されて実行できているのが確認できる。

pi@raspi-child:~ $ sudo iotedge list
NAME                        STATUS           DESCRIPTION      CONFIG
SimulatedTemperatureSensor  running          Up an hour       192.168.0.108:8000/azureiotedge-simulated-temperature-sensor:1.0
edgeAgent                   running          Up an hour       192.168.0.108:8000/azureiotedge-agent:1.2.0-rc3
edgeHub                     running          Up an hour       192.168.0.108:8000/azureiotedge-hub:1.2.0-rc3

以上で、Nested Edge の確認は終了。


MQTT Topic on IoT Edge

Preview 機能の MQTT Topic を使った通信のセットアップ方法を説明する。
本機能を使うと、mosquitto 等の一般的な mqtt プロトコルで通信しているアプリが、IoT Edge の MQTT Topic を使った pub/sub 通信や、IoT Hub へのメッセージ送信ができる。
これまで説明した方法でのセットアップが済んだ状況で、「Aure IoT Edge を使用した発行とサブスクライブ」を参考にセットアップを行う。
ハマりポイントがいくつかあるので、解説する。

Deployment.json の配置

承認」に記載の、Deployment.json の IoT Hub への通知は VS Code の Extension では Error になるようなので、Azure CLI で実行する。

PS> az login
PS> az iot edge set-modules --device-id yourdevice --hub-name youriothubname --content .\deployment.json

yourdevice は、親でも子でもどちらの IoT Edge デバイスでも構わない。

SAS Token の生成

Azure IoT Hub の利用になれている人ほどハマりがちなので念のため記載。
パブリッシャーとサブスクライバーのクライアントを作成する」 の 2.に記載されている通り、subscribe / publish のパスワードで使う SAS Token は、Azure CLI のコマンドで生成したものを利用する。
また、最後のオプションの --du 3600 は、3600秒だけ有効なトークンということなので、1時間経過後には無効になることに注意すること。また、MQTT で通信する Publisher と Subscriber は、IoT Hub に IoT Device として登録されていて、かつ、どちらのデバイスも、通信先の Azure IoT Edge デバイスの子デバイスとして設定されていなければならないことに注意。

その他

親子関係設定が完了している、二つの IoT Edge Device(Raspberry Pi)は、HW的に信頼関係が証明書で結ばれているので、親側の IoT Edge の MQTT Topic 機能を有効化した場合、 「購読」、「発行」は、親側、子側、どちらの Raspberry Pi で実行(同一デバイスでも異なるデバイスでもどちらでも可)しても、Subscriber 側が Publisher が発行したメッセージを受信できる。


IoT Edge Module 内で、Device SDK アプリを起動し、そのアプリを 子デバイスとして扱う

IoT Edge の Transparent Gateway としての利用は、ローカルネット側で様々な通信プロトコルで通信しているデバイス群を IoT Hub に接続する Protocol Gateway の実現方法として活用される。
このような場合、プロトコルで通信しているデバイス群をそれぞれ IoT Device として IoT Hub に登録し、IoT Edge Module 内で、それぞれの IoT Device 用のプロセスを作って、そのプロセス内で、登録された各 IoT Device 用の接続文字列を使って、IoT Edge デバイスを介して IoT Hub に接続し双方向通信を行う、といった実装が可能である。
このサンプルでは、そのような実装方法のひな型を提供する。
詳しくは、IoT Edge Module 内での IoT Device 制御を参照の事。