本文へジャンプ

Azure IoT Edge 連携ガイド

Revision: 1.5.0-9417-7fa03c1d

1 はじめに

本書では、Azure IoT EdgeやAzure Functionsを使って、IoTデバイスで発生したデータをGridDBに登録する方法を説明します。

1.1 本書の構成

各章の内容は次のとおりです。

  • はじめに
    本書の構成及び用語について説明します。

  • システム構成
    本書で使うシステム構成について説明します。

  • デバイスデータ送信の設定
    デバイスで発生したデータを、Azure IoTハブに送信する設定やサンプルプログラムを紹介します。

  • デバイスデータ登録の設定
    Azure IoTハブのデータを受信し、GridDBに登録するAzure Functionsの設定やサンプルプログラムを紹介します。

  • 登録データの確認
    サンプルプログラムで登録したデータを運用管理GUIで確認する方法を紹介します。

1.2 用語の説明

本書で用いられる用語の説明です。

用語 意味
Azure IoT Edge IoTデバイス上でのカスタムコードの実行環境であり、本書ではIoTハブにデータ送信を行うために利用します。カスタムコードはDockerコンテナとしてデプロイします。詳細はMicrosoftのリファレンスを参照ください。
Azure コンテナレジストリ コンテナイメージを格納するAzureのサービスです。Azure IoT Edgeで動作するDockerコンテナは、コンテナレジストリに登録したものを取得して利用します。詳細はMicrosoftのリファレンスを参照ください。
Azure IoTハブ IoTデバイスのメッセージハブとして動作したり、接続デバイスを管理したりします。本書ではAzure IoT Edgeのデータ送信先となり、それをAzure Functionsが受信するシステム構成となります。詳細はMicrosoftのリファレンスを参照ください。
Azure Functions ユーザがコードと設定を書くだけで機能を提供できる、サーバレスアーキテクチャです。機能は関数アプリという単位で設定します。本書ではAzure IoTハブのデータを受信し、GridDBに登録する関数アプリを作成します。詳細はMicrosoftのリファレンスを参照ください。
運用管理GUI GridDB Cloudを管理するWebアプリケーションです。
クラスタ 一体となってデータ管理を行う、1つ、もしくは複数のノードの集合を指します。
ノード GridDBでデータ管理を行うひとつのサーバプロセスを指します。
コンテナ ロウの集合を管理する入れ物です。コレクションと時系列コンテナの2種類が存在します。
コレクション 一般の型のキーを持つロウを管理するコンテナの一種です。
ロウ GridDBで管理する1件分のデータを指します。

2 システム構成

本書で説明するシステムの構成を下記に示します。

GridDBにデータ登録を行う流れを下記に示します。

  1. IoT Edge上で動作するデータ送信アプリから、IoTハブにデバイスデータを送信
  2. FunctionsがIoTハブからデバイスデータを受信
  3. FunctionsがデバイスデータをGridDBに登録

次章以降では上記を実現するため、下記の設定方法について説明します。

  • IoT Edgeを中心としたデバイスデータ送信の設定方法
  • Functionsを中心としたデバイスデータ登録の設定方法

本書では、Microsoftのドキュメントを参照する箇所があります。各種設定値に関しては、別途指定がある場合を除き、ドキュメント内で指定されている値を設定することを推奨します。

3 デバイスデータ送信の設定

デバイスデータをIoTハブに送信するには、下記の手順が必要です。

  1. IoTハブの作成
  2. IoT EdgeデバイスをIoTハブに登録
  3. IoT Edgeデバイスを構成
  4. データ送信アプリを仮想マシンにデプロイ

手順1, 2, 3についてはMicrosoftのクイックスタートガイドの手順にしたがって実施してください。クイックスタートガイドでは、仮想マシンにIoT Edgeランタイムをインストールしますが、Raspberry Piなど実機にインストールして動作させることも可能です。サポートされているプラットフォームについてはMicrosoftのリファレンスを参照してください。

手順4のデータ送信アプリのデプロイについて説明します。

3.1 データ送信アプリを仮想マシンにデプロイ

データ送信アプリを仮想マシンにデプロイするには下記の手順が必要です。

  1. コンテナエンジンのインストール
  2. VS Codeとツールの設定
  3. コンテナレジストリの作成
  4. 新しいモジュールプロジェクトの作成
  5. データ送信アプリのビルドとプッシュ
  6. データ送信アプリのデプロイ

手順1, 2, 3については、Microsoftのチュートリアルの手順にしたがって実施してください。手順4, 5, 6については、本書で説明します。

3.1.1 新しいモジュールプロジェクトの作成

VS Code上でモジュールプロジェクトを作成します。

プロジェクトの作成前に、Microsoftのチュートリアルの前提条件に記載されている、開発に必要な要素をインストールしてください。

F1を押下し、「Azure IoT Edge: New IoT Edge Solution」を選択します。

ソリューションを作成する任意のフォルダを指定します。ソリューション名を指定するコマンドパレットが表示されるので、デフォルト値「EdgeSolution」を指定します。

プログラミング言語を選択するコマンドパレットが表示されるので、「Java Module」を選択します。

モジュール名を指定するコマンドパレットが表示されるので、デフォルト値「SampleModule」を指定します。

Dockerイメージリポジトリを指定するコマンドパレットが表示されるので、手順3で作成したリポジトリを指定します。指定方法は下記の形式となります。

<ログインサーバ名>/<リポジトリ名>

グループIDを指定するコマンドパレットが表示されるので、デフォルト値「com.edgemodule」を指定します。

新たなソリューションが自動で作成されます。

3.1.2 データ送信アプリのビルドとプッシュ

ソリューションのファイル一覧より、「App.java」を選択して開いてください。

下記のコードを貼り付けて保存します。

package com.edgemodule;

import com.microsoft.azure.sdk.iot.device.*;
import java.io.IOException;
import java.text.SimpleDateFormat;

public class App {
    private static EventCallback eventCallback = new EventCallback();

    protected static class EventCallback implements IotHubEventCallback {
        @Override
        public void execute(IotHubStatusCode status, Object context) {
            if (context instanceof Message) {
                System.out.println("Send message with status: " + status.name());
            } else {
                System.out.println("Invalid context passed");
            }
        }
    }
    public static void main(String[] args) throws IOException {
        System.out.println("The main function was invoked.");
        DeviceClient client = null;
        try {
            client = new DeviceClient("<デバイスの接続文字列>" , IotHubClientProtocol.HTTPS);
            client.open();
            for (int i=0; i<60; i++) {
                String timestampStr = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(new java.util.Date());
                double temperature = 20 + Math.random() * 15;
                String msgStr = timestampStr + "," + String.valueOf(temperature);
                Message msg = new Message(msgStr);
                System.out.println("msg: " + msgStr);
                client.sendEventAsync(msg, eventCallback, msg);
                Thread.sleep(60000);
            }
            client.closeNow();
        } catch (Exception e) {
            e.printStackTrace();
            if (client != null) {
                client.closeNow();
            }
            System.exit(1);
        }
    }
}

コード中の「<デバイスの接続文字列>」は、対応する接続文字列を入力してください。接続文字列はAzureポータルの「IoT Hub」の「IoT Edge」から登録デバイスを選択することで取得できます。

本コードは、下記の処理を行います。

  1. IoTハブに接続
  2. 現在時刻と温度データ(20.0度-35.0度のランダム値)から構成されるCSV文字列を生成
  3. IoTハブにCSV文字列を送信
  4. 1分停止
  5. 2-4を59回実行
  6. IoTハブから切断

上記コードをビルド・コンテナ化し、コンテナレジストリにプッシュします。VS Codeのターミナルより下記コマンドを実行し、コンテナレジストリにログインしてください。ACR(Azure Container Registry)のパラメータは、Azureポータルのコンテナレジストリより取得できます。

docker login -u <ACR username> -p <ACR password> <ACR login server>

「deployment.template.json」を右クリックし、「Build and Push IoT Edge Solution」を選択し、コンテナイメージをビルドしてプッシュします。

3.1.3 データ送信アプリのデプロイ

「AZURE IOT HUB」の「Devices」から、登録済みのデバイスを右クリックします。「Create Deployment for Single Device」を選択します。

「config」フォルダにある、「deployment.amd64.json」を選択すると、仮想マシンへのデプロイが行われます。

IoTハブの登録デバイスのページを開き、Edgeランタイムのレスポンスが「200 -- OK」となっていて、登録モジュールのステータスが「running」になっていればデプロイ成功です。

4 デバイスデータ登録の設定

Azure Functionsを用いてデバイスデータをGridDBに登録するには、下記の手順が必要です。

  1. 関数アプリ作成
  2. VNet作成
  3. 関数アプリのVNet統合
  4. 統合済みVNetとGridDBのVNetのピアリング作成
  5. GridDBのコンテナ作成
  6. データベースユーザ作成
  7. 関数のデプロイ

GridDB Cloudでは、V1.3時点で下記の2種類の接続方法を提供しています。

  • VNetピアリングを使った、プライベートIPアドレスによる接続
  • WebAPIを使った、パブリックIPアドレスによる接続

WebAPIを利用する場合は手順2,3,4は不要となりますが、本書ではより安全性の高いVNetピアリングを使った接続方法を紹介します。

関数アプリがGridDBのプライベートIPアドレスを用いて接続するには、VNetピアリングが必要になります。VNetピアリングはVNet同士を接続するサービスですが、関数アプリはデフォルトではVNetに所属しません。そのため、既存のVNetと関数アプリを統合する必要があります。

以降でそれぞれの手順について説明します。

4.1 関数アプリ作成

Microsoftのリファレンスにしたがって、Azureポータルで関数アプリを作成します。

その際、下記の設定を行ってください。特に、「プラン」はVNet統合を唯一サポートしている「Functions Premium」を必ず設定してください。

その他の設定項目はリファレンスどおり、もしくは任意の値で構いません。

設定項目 設定内容
インスタンスの詳細/公開 コード
インスタンスの詳細/ランタイムスタック Java
インスタンスの詳細/バージョン 11.0
プラン/プランの種類 Functions Premium

4.2 VNet作成

関数アプリを所属させるVNetを作成します。既に利用可能なVNetが存在する場合は、本節を飛ばしてください。

利用可能なVNetが存在しない場合は、Microsoftのチュートリアルにしたがって、VNetを作成してください。

4.3 関数アプリのVNet統合

関数アプリを作成したVNetと統合します。VNet統合の詳細な仕様についてはMicrosoftのリファレンスを参照してください。

Azureポータルより、作成した関数アプリを開きます。「ネットワーク」を選択し、「VNet統合」を選択します。

VNet統合を実行するときは、統合するVNetに空のサブネットを作成する必要があります。「VNetの追加」をクリックし、新しいサブネットを作成してください。

4.4 統合済みVNetとGridDBのVNetのピアリング作成

GridDB Cloudのクイックスタートガイドに記載の手順にしたがって、統合済みVNetとGridDBのVNet間でVNetピアリングを作成してください。

4.5 GridDBのコンテナ作成

関数アプリのデータ登録先となるコンテナを作成します。

運用管理GUIにログインし、クエリエディタを開きます。下記SQLを実行し、タイムスタンプと温度から構成される時系列コンテナを、publicデータベースに作成します。

CREATE TABLE device_temperature (time TIMESTAMP PRIMARY KEY, temperature DOUBLE) USING TIMESERIES

4.6 データベースユーザ作成

データベースユーザを作成済みの場合は、本節を飛ばしてください。

GridDB Cloudのクイックスタートガイドに記載の手順にしたがって、データベースユーザを作成します。

4.7 関数のデプロイ

IoTハブからデバイスデータを取得し、GridDBに登録する関数をデプロイします。

Microsoftのクイックスタートにしたがって、開発環境をセットアップし、ローカルプロジェクトを作成してください。この際、Javaのバージョンは「Java 11」を指定します。他の項目はデフォルト値を指定します。

次に、F1を押し、関数を作るコマンドパレットを表示します。Azure Functions: Create Functionを選択します。

「EventHubTrigger」を選択します。表示されない場合は、「Change template filter」を選択し、「All」を選択してください。

Eventハブ(IoTハブと名称が異なりますが問題ありません)の名称を入力するコマンドパレットが表示されるまでデフォルト値を指定します。Eventハブ名として、作成済みのIoTハブ名を入力します。

デフォルト値を指定し、関数の作成を完了します。下記の画面が表示された場合は、「Skip for now」を選択してください。

関数のファイルが追加されたことが確認できます。

pom.xmlを開き、GridDBのJavaライブラリを依存ライブラリとして追加します。<dependencies>内の任意の箇所に下記を追加してください。

        <!-- https://mvnrepository.com/artifact/com.github.griddb/gridstore -->
        <dependency>
            <groupId>com.github.griddb</groupId>
            <artifactId>gridstore</artifactId>
            <version>4.5.0</version>
        </dependency>

「host.json」を開き、「extensionBundle/version」の値を下記のように変更します。これは関数アプリがIoTハブをトリガー・バインドとして利用するのに必要なライブラリのバージョン情報を表します。2022年2月現在では下記の指定(3.3以上、4.0未満)で関数アプリの動作確認を行っていますが、必要なライブラリバージョンが上がった場合は本項目の修正が必要です。

{
  "extensionBundle": {
    "version": "[3.3.0, 4.0.0)"
  } 
}

「EventHubTriggerJava1.java」を開き、下記コードで上書きします。

package com.function;

import com.microsoft.azure.functions.annotation.*;
import com.microsoft.azure.functions.*;
import com.toshiba.mwcloud.gs.Container;
import com.toshiba.mwcloud.gs.GridStore;
import com.toshiba.mwcloud.gs.GridStoreFactory;
import com.toshiba.mwcloud.gs.Row;
import com.toshiba.mwcloud.gs.TimestampUtils;

import java.util.*;

/**
 * Azure Functions with Event Hub trigger.
 */
public class EventHubTriggerJava1 {
    /**
     * This function will be invoked when an event is received from Event Hub.
     */
    @FunctionName("EventHubTriggerJava1")
    public void run(
        @EventHubTrigger(name = "message", eventHubName = "<IoTハブ名>", connection = "AzureWebJobsEventHubSender", consumerGroup = "$Default", cardinality = Cardinality.ONE, dataType = "string") String message,
        final ExecutionContext context
    ) {
        context.getLogger().info("Java Event Hub trigger function executed.");
        context.getLogger().info(message);

        try {
            Properties prop = new Properties();
            prop.setProperty("notificationProvider", "<Notification Provier URL>");
            prop.setProperty("clusterName", "<クラスタ名>");
            prop.setProperty("database", "public");
            prop.setProperty("user", "<データベースユーザ名>");
            prop.setProperty("password", "<データベースユーザのパスワード>");

            GridStore store = GridStoreFactory.getInstance().getGridStore(prop);
            String containerName = "device_temperature";
            Container<?, Row> container = store.getContainer(containerName);
            if (container == null) {
                throw new Exception("Container not found.");

            }

            String[] messageElements = message.split(",");
            if (messageElements.length != 2) {
                throw new Exception("The message format is invalid.");
            }

            Row row = container.createRow();
            row.setTimestamp(0, TimestampUtils.parse(messageElements[0]));
            row.setDouble(1, Double.parseDouble(messageElements[1]));
            container.put(row);

            context.getLogger().info("Data transfer to the container " + containerName + " has been completed.");

            container.close();
            store.close();
        } catch (Exception e) {
            context.getLogger().info("error" + e.getMessage());
        }
    }
}

上記コードは、IoTハブのCSVデータを受信し、コンテナ「device_temperature」に登録します。本コードはお使いの環境に合わせて修正する必要があります。修正する箇所は下記のとおりです。

修正箇所 修正内容 修正例
IoTハブ名 作成したIoTハブの名前を入力します。 iot-edge-function-test
Notification Provier URL クイックスタートガイドを参考に接続情報を設定します。 http://dbaassharegsst.blob.core.windows.net/dbaas-share-griddb-blob/1234567890.json
クラスタ名 クイックスタートガイドを参考にクラスタ名を設定します。 cluster1234
データベースユーザ名 本書で作成したデータベースユーザ名を指定します。 test_user1
データベースユーザのパスワード 本書で作成したデータベースユーザのパスワードを指定します。 test_user1

次に、IoTハブの接続情報を設定します。接続情報はコードに埋め込むのではなく、Azureポータルから関数アプリの環境変数として設定します。

IoTハブの接続情報は作成したIoTハブの「組み込みのエンドポイント」より取得できます。「イベントハブ互換エンドポイント」の値をコピーしてください。

作成した関数アプリの「構成」を開きます。「新しいアプリケーション設定」をクリックし、「AzureWebJobsEventHubSender」という名前でコピーした値を設定します。本設定項目は、コード中の下記のパラメータに自動で割り当てられます。

connection = "AzureWebJobsEventHubSender"

VS CodeのAzure拡張機能を開き、「EventHubTriggerJava1」をデプロイします。デプロイ先が表示されるので、作成した関数アプリを指定します。

Azureポータルの関数アプリの「関数」を開き、デプロイした関数が存在することを確認します。

5 登録データの確認

ここまでの手順が完了している場合、IoT Edgeデバイスで発生したデータはAzure Functions経由でGridDBのコンテナに継続的に登録されています。本章では、登録データを運用管理GUIを使って確認します。

運用管理GUIにログインしたら、「チャート」ページを開き、「チャートの追加」ボタンをクリックします。ダイアログが表示されるので、下記のように設定します。

設定名 設定内容
時間範囲 過去1時間
データベース public
コンテナ device_temperature
カラム temperatureにチェック

下記のように、デバイスの温度データがグラフ表示されれば成功です。

本書の説明は以上となります。本書で作成したリソースが不要な場合は削除してください。IoT Edgeデバイスは、停止するまでデータ送信を続けます。また、IoTハブや関数アプリなど、一部のリソースは課金対象となります。