Help us understand the problem. What is going on with this article?

[Azure] Azure IoT Edge を使って取得した画像を Azure に送ってみる

やりたいこと

今回、デバイス側で撮影した動画から、キャプチャーを取得して、取得した画像を Azure Blob Storage に格納するというシナリオを、IoT Edge を利用して試してみました。参考にしたサイトはこちら
少し長くなりましたが、分かりやすく記載しましたので参考にしてみてください。

このシナリオ、いろいろなシーンに応用が可能だと思います。
例えば、Factory IoT(工場内の検査工程とか)、車載器(車とかAGV等)、建物内の監視など。

Overview.jpg

IoT Edge とは

これまでクラウドで行っていた分析とカスタムビジネスロジックをデバイス側で実行できるようにするものです。
IoT Edge は、次の3つのコンポーネントで構成されています。詳細はこちらを参照ください。

  • IoT Edge モジュール
    • IoT Edge のモジュールは、実行の単位となるもので、Docker 互換のコンテナ―として実装されます。
    • 互いに通信を行う複数のモジュールを構成することで、データ処理のパイプラインを作成することが可能です。
    • モジュールには、Azure のサービスである、Azure Functions、Azure Stream Analytics、Azure Machine Learning が利用可能です。
  • IoT Edge ランタイム
    • 個々の IoT Edge デバイス上で動作し、各デバイスにデプロイされたモジュールを管理します。
    • デバイスにワークロードをインストールし、更新します。
    • デバイス上の IoT Edge セキュリティ標準を維持します。
    • モジュールの正常性をクラウドにレポート(IoT Hub に対し)してリモート監視を可能にします。
  • クラウドベースのインターフェイス
    • IoT Edge デバイスをリモートから監視して管理します。
    • まとまったデバイスに対してワークロードを送信します。

個人的に IoT Edge の優位性は、Docker 互換のコンテナーであるところだと思っています。
あとで手順がありますが、Azure Container Registry に Push した Docker Image の最新情報を IoT Hub を介して取得して、最新の Docker Image を Pull できる点は、Edge デバイスの管理を楽にし、開発を容易にしていると思います。そのため、昨今話題の DevOps にも対応しやすい作りになっていると感じています。

環境情報

デバイス側の環境情報

IoT Edge によってサポートされている OS 情報 を参照してください。
Raspbian でサポートしているのは、Raspbian-stretch なので、これをあらかじめダウンロードしておく必要があります。
Raspberry Pi を購入した時点では、Raspbian の最新 OS である Raspbian Buster の場合がありますので、注意してください。

Raspberry Pi の OS のバージョンを確認するには、こちらを参考にしました。
取り付けたカメラがきちんと動作するかどうかの確認はこちらを参考にしました。

クラウド側の環境情報

  • 利用可能な Azure サブスクリプション
  • 利用するサービス
    • Azure IoT Hub
    • Azure Container Registry
    • Azure Blob Storage

開発環境

  • Visual Studio Code
    • ない場合はこちらからダウンロード
  • 拡張オプションのインストール
    • Azure Tools
    • Azure IoT Hub Toolkit
    • Azure IoT Edge
    • Python (開発に使用する言語向け拡張)

手順

それでは実際にはじめます。今回は、以下の12手順で実施しました。

1)IoT Hub の作成
2)IoT Edge デバイスの登録
3)IoT Edge のインストール
4)Azure Container Registry の作成
5)Storage Account の作成
6)開発環境のセットアップ
7)Visual Studio Code で IoT Edge Solution を作成
8)Blob on Edge Module の追加
9)camera-capture Module の追加
10) Solution の Build と Azure Container Registry への Push
11) Module 配置情報を IoT Hub に転送
12) 動作確認

1)IoT Hub の作成

Azure Portal にアクセスし、左のメニューで「リリースの作成」をクリックします。
次に、「モノのインターネット(IoT)」をクリックし、「IoT Hub」をクリックします。
IoTHub01.jpg

画面が変わるので、以下の項目を設定し、「次へ:サイズとスケール」をクリックします。

設定項目 設定値
サブスクリプション お持ちのサブスクリプションを選択。
リソースグループ 新規作成の場合は、下にある青字の「新規作成」を選択し、任意の名称を入力。既存のものがある場合は選択。
リージョン 任意の場所を選択する。
IoT Hub 名 任意の名称を入力。(緑のチェックマークが出ればOK)

IoTHub02.jpg

ここでは、標準の設定のままで良いです。「価格とスケールティア」は自分の環境に応じて設定してください。
「確認および作成」をクリックします。
IoTHub03.jpg

確認画面が表示されるので、内容を確認し、「作成」をクリックします。
IoTHub04.jpg

IoT Hub の作成が完了です。
IoTHub05.jpg

2)IoT Edge デバイスの登録

次に、IoT Hub 上で管理する IoT Edge デバイスの登録を行います。

IoT Hub のメニューの中から「IoT Edge」をクリックします。
右側の画面上部にある「IoT Edge デバイスを追加する」をクリックし、IoT Edge をインストールするデバイスの情報を登録します。
この登録によって、IoT Hub と IoT Edge をインストールするデバイスを紐づけます。
IoTHub06.jpg

以下の項目を設定し、「保存」をクリックします。

設定項目 設定値
デバイス ID 任意の名称を入力。(緑のチェックマークが出ればOK)
認証の種類 対称キー/X.509 自己署名済み のいずれかを選択。ここでは「対称キー」を選択します。
主キー 空欄で OK。
セカンダリキー 空欄で OK。
自動生成キー チェックを入れる。これで主キーとセカンダリキーを自動で作成してくれる。
このデバイスを IoT ハブに接続する 有効/無効 のいずれかを選択。ここでは「有効」を選択します。
子デバイス IoT Edge に紐づく子デバイスの設定。ここでは子デバイスはないので、何も設定しない。

IoTHub07.jpg

IoT Edge デバイスが登録されたことを確認します。
先ほど作成したデバイス ID をクリックして、接続文字列を確認します。
IoTHub08.jpg

キーや接続文字列は秘密情報のため、*にて伏字になっている。
プライマリ接続文字列をこのあとの手順で利用するためひかえておきます。
「HostName=myIoTH001.azure-devices.net;DeviceId=raspi3bp;SharedAccessKey=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX」のような形式。
IoTHub09.jpg

3)IoT Edge のインストール

これまでは Azure Portal で作業をしていたが、この手順では Raspberry Pi へ IoT Edge をインストールします。
まず最初に Raspberry Pi の初期設定を行います。

  • SD カードに Raspbian-stretch をインストールし、Raspberry Pi を起動します
  • Raspberry Pi 上で初期設定を行います
    • WiFi の設定
    • ラズパイアイコン → 設定 → Raspberry Pi の設定
    • Interfaces タブを選択
    • Camera、SSH、SPI、I2C、Serial Port、1-Wire を有効に設定し再起動
  • 再起動後、右上の WiFi マークにカーソルをあわせて、IP アドレスを確認します
  • これで、開発用マシンから Tera Term や Putty 等のツールでリモートでの接続が可能になります

上記が完了したら、Raspberry Pi 上か Tera Term などでリモート接続を行い、以下のコマンドを実行します。
詳細はこちらのサイトを参照ください。

Microsoft キーとソフトウェアリポジトリフィードを登録

以下の4つのコマンドを bash 環境で入力します。

curl https://packages.microsoft.com/config/debian/stretch/multiarch/prod.list > ./microsoft-prod.list
sudo cp ./microsoft-prod.list /etc/apt/sources.list.d/
curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg
sudo cp ./microsoft.gpg /etc/apt/trusted.gpg.d/

コンテナランタイムをインストール

以下の3つのコマンドを bash 環境で入力します。
Moby ベースのコンテナエンジンは、IoT Edge で公式にサポートされている唯一のものです。
Docker CE/EE コンテナー イメージは、Moby ランタイムと互換性があります。

sudo apt-get update
sudo apt-get install moby-engine
sudo apt-get install moby-cli

Azure IoT Edge セキュリティデーモンのインストール

以下の2つのコマンドを bash 環境で入力します。

sudo apt-get update
sudo apt-get install iotedge

セキュリティデーモンを構成する

以下のコマンドを bash 環境で入力します。

sudo vi /etc/iotedge/config.yaml

上記コマンドを入力したことで、config.yaml の設定が可能になります。
以下の「Manual provisioning configuration」部分に、IoT Hub にて登録した IoT Edge デバイスの接続文字列を、以下の「device_connection_string」にコピーし、保存します。(先ほどの手順でひかえておいた文字列です)

# Manual provisioning configuration
provisioning:
  source: "manual"
  device_connection_string: "HostName=myIoTH001.azure-devices.net;DeviceId=raspi3bp;SharedAccessKey=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

セキュリティデーモンの再起動

以下のコマンドを bash 環境で入力し再起動します。

sudo systemctl restart iotedge

インストールの成功を確認する

以下の3つのコマンドを bash 環境で入力。Error 等がないことを確認します。
インストールした後は、edgeAgent だけが実行されています。環境によっては少し時間がかかります。

systemctl status iotedge
journalctl -u iotedge --no-pager --no-full
sudo iotedge list

IoT Edge は IoT Hub と通信を行い、ステータス情報を通知します。
Azure Portal の IoT Edge 画面にてランタイムの状態を確認できます。
IoTHub10.jpg

4)Azure Container Registry の作成

Azure Portal にアクセスし、左のメニューで「リリースの作成」をクリックします。
次に、「コンテナ―」をクリックし、「Container Registry」をクリックします。
ACR01.jpg

以下の項目を設定し、「作成」をクリックします。

設定項目 設定値
レジストリ名 任意の名称を入力。(緑のチェックマークが出ればOK)
サブスクリプション お持ちのサブスクリプションを選択。
リソースグループ 新規作成の場合は、下にある青字の「新規作成」を選択し、任意の名称を入力。既存のものがある場合は選択。
場所 任意の場所を選択する。
管理者ユーザー 有効/無効 から選択。今回は「有効」を選択。
SKU Basic を選択。

ACR02.jpg

Azure Container Registry の作成が完了です。
ACR03.jpg

Azure Container Registry のメニューで、「アクセスキー」をクリックし、後ほど利用する以下の3つの設定値をひかえておきます。

  • ログインサーバー
  • ユーザー名
  • password

ACR04.jpg

5)Storage Account の作成

Azure Portal にアクセスし、左のメニューで「リリースの作成」をクリックします。
次に、「ストレージ」をクリックし、「ストレージアカウント」をクリックします。
Storage01.jpg

以下の項目を設定し、「詳細」をクリックします。

設定項目 設定値
サブスクリプション お持ちのサブスクリプションを選択。
リソースグループ 新規作成の場合は、下にある青字の「新規作成」を選択し、任意の名称を入力。既存のものがある場合は選択。
ストレージアカウント名 任意の名称を入力。(緑のチェックマークが出ればOK)
場所 任意の場所を選択する。
パフォーマンス Standard/Premium から選択。今回は「Standard」を選択。
アカウントの種類 StorageV2(汎用) を選択。
レプリケーション ローカル冗長ストレージ(LRS) を選択。
アクセス層(既定) クール/ホット から選択。今回は「ホット」を選択。

Storage02.jpg

標準の設定のまま「タグ」をクリックします。
Storage03.jpg

標準の設定のまま「確認および作成」をクリックします。
Storage04.jpg

確認画面が表示されるので、設定内容を確認し、「作成」をクリックします。
Storage05.jpg

Storage Account が無事に作成されました。
次に、画面中央の「BLOB」をクリックし、コンテナ―を作成します。
Storage06.jpg

画面中央上部にある「コンテナ―」をクリックします。
Storage07.jpg

名前に「edgephotos」と入力し、「OK」をクリックします。
Storage08.jpg

Blob コンテナ―が作成されました。
(下図には recognised というコンテナ―もありますが、今回は使用しないので気にしないでください。)
Storage10.jpg

6)開発環境のセットアップ

次に IoT Edge 上にインストールする Module を開発するための開発環境をセットアップします。
ローカル PC を使っていただいても良いですし、Azure 上に開発用 VM 環境を作成しても良いです。

  • Visual Studio Code のインストール
  • 拡張オプションのインストール
    • Azure Tools
    • Azure IoT Hub Toolkit
    • Azure IoT Edge
    • Python (開発に使用する言語向け拡張。今回は Python にした。)

Python のバージョンは3.6を使用するようにしてください。

7)Visual Studio Code で IoT Edge Solution を作成

開発用マシンで、任意の場所に「UploadCameraImageEdgeToCloud」というフォルダを作成します。
VSCode01.jpg

Visual Studio Code を起動し、メニューバーの「ファイル」→「フォルダーを開く」→先ほど作成したフォルダ「UploadCameraImageEdgeToCloud」を選択して開きます。
VSCode02.jpg

メニューバーの「表示」→「コマンドパレット」でコマンドパレットを起動し、表示された窓に「Azure IoT Edge」と入力します。一覧が表示されるので、「Azure IoT Edge: New IoT Edge Solution」を選択します。
VSCode03.jpg

フォルダーの選択画面にて、現在開いているフォルダー「UploadCameraImageEdgeToCloud」を選択します。
VSCode04.jpg

ソリューション名「UploadCameraImageEdgeToCloud」を入力し、Enter を押下します。
VSCode05.jpg

Module のプログラミング言語「Python」を選択します。
VSCode06.jpg

Module 名「StoreImageToLocalBlob」を入力し、Enter を押下します。
VSCode07.jpg

Docker Container の名前を設定します。「localhost:5000」の部分を、先ほど作成した Azure Container Registry のログインサーバーで置き換えます。ここでは「iotacr001.azurecr.io」で置き換えます。
VSCode08.jpg

以上で、Solution と Python Module のひな型が完成です。
VSCode09.jpg

次に、「.env」に、Azure Container Registry の USERNAME と PASSWORD を設定します。修正したら必ずファイルを保存します。
VSCode10.jpg

「deployment.template.json」を編集します。
「SimulatedTemperatureSensor」は、template として勝手に作成されているもので不要なため、削除します(赤枠部分)。
VSCode11.jpg

次に、Module 名を「SimulatedTemperatureSensor」→「store-image-to-local-blob」に変更します。
VSCode13.jpg

「createOptions」に、以下の「Env」の定義を追加します。

"Env":[
  "BLOB_ON_EDGE_MODULE=local-blob-storage",
  "BLOB_ON_EDGE_ACCOUNT_NAME=< local account >",
  "BLOB_ON_EDGE_ACCOUNT_KEY=< local account key >",
  "IMAGE_CONTAINER_NAME=photos",
  "BLOB_UPLOAD_DURATION_SEC=60"
]

VSCode14.jpg

Docker Image の種別を変更します。
StoreImageToLocalBlob の後ろに「.」(ピリオド)を入力すると、リストが表示されるので、「arm32v7」を選択します。
修正後、ファイルを保存しておきます。
VSCode15.jpg

「Dockerfile.amd64」と「Dockerfile.amd64.debug」は不要なファイルのため、削除します。今回は Raspberry Pi/Raspbian のみで動作する Module を開発するためです。
VSCode16.jpg

先ほど、「Dockerfile.amd64」と「Dockerfile.amd64.debug」を削除したため、「module.json」から、「amd64」と「amd64.debug」の行を削除します。削除後、ファイルを保存しておきます。
VSCode17.jpg

次に「Dockerfile.arm32v7」を以下で丸っと置き換えます。その後ファイルを保存しておきます。

FROM resin/raspberrypi3-debian:stretch

RUN [ "cross-build-start" ]

WORKDIR /app

RUN apt-get update && \
    apt-get install -y --no-install-recommends libboost-python1.62.0 python3-pip libpython3-dev && \
    rm -rf /var/lib/apt/lists/* 

RUN pip3 install --upgrade pip 
RUN pip install --upgrade setuptools 
COPY requirements.txt ./
RUN pip install -r requirements.txt


RUN [ "cross-build-end" ]

COPY . .

# Expose the port
EXPOSE 80

ENTRYPOINT [ "python3", "./main.py" ]

VSCode18.jpg

「requirements.txt」を開き、以下で置き換えます。置き換えた後、ファイルを保存しておきます。

azure-iothub-device-client
flask
azure-storage-blob
azure-mgmt-storage

VSCode19.jpg

最後に「main.py」を修正します。以下のコードで置き換えます。置き換えた後、ファイルを保存しておきます。
ここまでで、いったん自作 Module の作成が完了です。

# Copyright (c) Microsoft. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for
# full license information.

import os
import random
import time
import sys
import io
import json
import time
import datetime
from azure.storage.blob import BlockBlobService, PublicAccess

# Imports for the REST API
from flask import Flask, request

import iothub_client
# pylint: disable=E0611
from iothub_client import IoTHubModuleClient, IoTHubClientError, IoTHubTransportProvider
from iothub_client import IoTHubMessage, IoTHubMessageDispositionResult, IoTHubError

# messageTimeout - the maximum time in milliseconds until a message times out.
# The timeout period starts at IoTHubModuleClient.send_event_async.
# By default, messages do not expire.
MESSAGE_TIMEOUT = 10000

# global counters
RECEIVE_CALLBACKS = 0
SEND_CALLBACKS = 0

# Choose HTTP, AMQP or MQTT as transport protocol.  Currently only MQTT is supported.
PROTOCOL = IoTHubTransportProvider.MQTT

app = Flask(__name__)

# 4MB Max image size limit
app.config['MAX_CONTENT_LENGTH'] = 4 * 1024 * 1024 

# Blob Upload Setting 
BLOB_UPLOAD_DURATION_SEC = 10 
IMAGE_CONTAINER_NAME = "edgephotos"
blobLastUploadTime = time.time()

blobService = None
storeImageRequestCount = 0

# Default route just shows simple text
@app.route('/')
def index():
    return 'store image to blob on edge harness'

# Like the CustomVision.ai Prediction service /image route handles either
#     - octet-stream image file 
#     - a multipart/form-data with files in the imageData parameter
@app.route('/image', methods=['POST'])
def store_image_handler():
    global storeImageRequestCount
    global blobService
    try:
        storeImageRequestCount += 1
        if ((storeImageRequestCount%10) ==0):
            print("images received - " + str(storeImageRequestCount))
            imageData = None
            if ('imageData' in request.files):
                imageData = request.files['imageData']
            else:
                imageData = io.BytesIO(request.get_data())
                blobService.upload_image_to_blob(imageData)
                print('stored image')
        results = []
        return json.dumps(results)
    except Exception as e:
        print('EXCEPTION:', str(e))
        return 'Error processing image', 500

class BlobService(object):
    def __init__(
            self,
            blobonedge_module_name,
            blob_account_name,
            blob_account_key,
            image_container_name,
            image_upload_duration_sec,
            edge_id
        ):
        localblob_connectionstring = 'DefaultEndpointsProtocol=http;BlobEndpoint=http://'+blobonedge_module_name+':11002/'+blob_account_name+';AccountName='+blob_account_name+';AccountKey='+blob_account_key+';'
        print("Try to connect to blob on edge by "+localblob_connectionstring)
        self.blockBlobService = BlockBlobService(endpoint_suffix='', connection_string=localblob_connectionstring)
        print("Connected to blob on edge")
        self.blockBlobService.create_container(image_container_name)
        print('Created image container - '+image_container_name)
        self.imageContainerName = image_container_name
        self.imageUploadDurationSec = image_upload_duration_sec
        self.edgeId = edge_id
        self.blobLastUploadTime = time.time()

    def upload_image_to_blob(self, image):
        now = datetime.datetime.now()
        currentTime = time.time()
        print('Last Time:'+str(self.blobLastUploadTime)+'->Current:'+str(currentTime))
        if (currentTime - self.blobLastUploadTime > self.imageUploadDurationSec):
            image_file_name = self.edgeId + "-img{0:%Y%m%d%H%M%S}".format(now) +".jpg"
            # image_file_name = "image{0:%Y%m%d%H%M%S}".format(now) +".jpg"
            print('Uploading image as '+image_file_name + ' at ' + str(currentTime))
            self.blockBlobService.create_blob_from_stream(self.imageContainerName, image_file_name, image)
            print('Upload done')
            self.blobLastUploadTime = currentTime


def main(protocol):
    try:
        print ( "\nPython %s\n" % sys.version )

    except KeyboardInterrupt:
        print ( "IoTHubModuleClient sample stopped" )

def initialize_blob_on_edge(blobOnEdgeModule,blobOnEdgeAccountName,blobOnEdgeAccountKey,imageContainerName, blobUploadDurationSec, edgeId):
    blobService = BlobService(blobOnEdgeModule, blobOnEdgeAccountName, blobOnEdgeAccountKey,imageContainerName,blobUploadDurationSec, edgeId)
    return blobService

if __name__ == '__main__':
    print('version : 0.3.0')
    print('app is '+__name__)
    # main(PROTOCOL)
    # for key in os.environ.keys():
    #    print('key='+key+":value="+os.environ[key])

    BLOB_ON_EDGE_MODULE = os.environ['BLOB_ON_EDGE_MODULE']
    BLOB_ON_EDGE_ACCOUNT_NAME = os.environ['BLOB_ON_EDGE_ACCOUNT_NAME']
    BLOB_ON_EDGE_ACCOUNT_KEY = os.environ['BLOB_ON_EDGE_ACCOUNT_KEY']
    IMAGE_CONTAINER_NAME=os.environ['IMAGE_CONTAINER_NAME']
    IOTEDGE_DEVICEID=os.environ['IOTEDGE_DEVICEID']
    BLOB_UPLOAD_DURATION_SEC = float(os.environ["BLOB_UPLOAD_DURATION_SEC"])


    print('IOTEDGE_DEVICEID:'+ IOTEDGE_DEVICEID)
    print('BLOB_ON_EDGE_MODULE:'+BLOB_ON_EDGE_MODULE)
    print('BLOB_ON_EDGE_ACCOUNT_NAME:'+BLOB_ON_EDGE_ACCOUNT_NAME)
    print('BLOB_ON_EDGE_ACCOUNT_KEY:'+BLOB_ON_EDGE_ACCOUNT_KEY)
    print('IMAGE_CONTAINER_NAME:'+IMAGE_CONTAINER_NAME)
    print('BLOB_UPLOAD_DURATION_SEC='+str(BLOB_UPLOAD_DURATION_SEC))

    blobService = initialize_blob_on_edge(BLOB_ON_EDGE_MODULE,BLOB_ON_EDGE_ACCOUNT_NAME,BLOB_ON_EDGE_ACCOUNT_KEY,IMAGE_CONTAINER_NAME,BLOB_UPLOAD_DURATION_SEC,IOTEDGE_DEVICEID)

    app.run(host='0.0.0.0', port=80, debug=True)

8)Blob on Edge Module の追加

ここからは、Raspberry Pi 上の Blob Storage に格納した画像を、Azure 上の Blob コンテナに転送し、Azure 上で受信した画像ファイルを削除する Module を追加します。これは、Marketplace にあるものを利用します。

Visual Studio Code のメニューバー「表示」→「コマンドパレット」を起動し、表示された窓に「Add IoT Edge Module」と入力します。一覧が表示されるので、「Azure IoT Edge: Add IoT Edge Module」を選択します。
VSCode20.jpg

「deployment.template.json」を選択します。
VSCode21.jpg

「Module from Azure Marketplace」を選択します。
VSCode22.jpg

一覧表示されるので、「Azure Blob Storage on IoT Edge」を選択します。
VSCode23.jpg

Import する Module を選択する画面が表示されるので、Tags で「arm32v7」を選択し、「Import」をクリックします。
VSCode24.jpg

「deployment.template.json」に赤枠部分が追加される。この後、追加された部分を修正します。
VSCode25.jpg

先ほど、Marketplace で選択した「arm32v7」の部分を「latest」に修正します。
VSCode26.jpg

Module 名を「AzureBlobStorageonIoTEdge」→「local-blob-storage」に修正します。
VSCode27.jpg

以下のBind 情報を追加します。追加する場所に注意。
今回、バインドマウントを使っているので、サービスが正しく起動するために、Raspberry Pi にコンテナが Pull された後、この手順Linux のコンテナー ユーザーにディレクトリ アクセスを許可するを実行する必要があります。

"Binds":["/srv/containerdata:/blobroot"],

VSCode28.jpg

https://generate.plus/en/base64?gp_base64_base%5Blength%5D=64 にアクセスしてキーを生成し、キーをコピーします。
VSCode29.jpg

上記で作成したキーと、「localaccount」というアカウント名を使用して、下図の2か所を修正し、ファイルを保存します。

"BLOB_ON_EDGE_ACCOUNT_NAME=localaccount",
"BLOB_ON_EDGE_ACCOUNT_KEY=生成したキー",

"LOCAL_STORAGE_ACCOUNT_NAME=localaccount",
"LOCAL_STORAGE_ACCOUNT_KEY=生成したキー"

VSCode30.jpg

次に、Cloud 側の Blob Storage と対応づけるために、以下を「deployment.template.json」に追加します。
追加する際、「,」を忘れないように($edgeHub と local-blob-storage の境界です)してください。

"local-blob-storage":{
  "properties.desired": {
    "deviceAutoDeleteProperties": {
      "deleteOn": true,
      "deleteAfterMinutes": 5,
      "retainWhileUploading":true
    },
    "deviceToCloudUploadProperties": {
      "uploadOn": true,
      "uploadOrder": "OldestFirst",
      "cloudStorageConnectionString": "< cloud storage account connection string >",
      "storageContainersForUpload": {
        "photos": {
          "target": "edgephotos"
        }
      },
      "deleteAfterUpload":true
    }
  }
}

VSCode31.jpg

上記で追加した中で、「"cloudStorageConnectionString": "< cloud storage account connection string >"」を修正します。
接続文字列が不明な方は、ひとつ下の図を参照ください。
VSCode32.jpg

Azure の Storage Account の接続文字列は、以下のように、「作成した Storage Account」→「アクセスキー」で表示される画面から取得可能です。接続文字列の右側にある「コピー」ボタンをクリックしてコピーするのが良いです。この値を使用します。
VSCode33.jpg

9)camera-capture Module の追加

次に camera-capture Module を追加します。この Module は、https://github.com/ms-iotkithol-jp/Custom-vision-service-iot-edge-raspberry-pi のものを流用します。

Visual Studio Code のメニューバー「表示」→「コマンドパレット」を起動し、表示された窓に「Add IoT Edge Module」と入力します。一覧が表示されるので、「Azure IoT Edge: Add IoT Edge Module」を選択します。
VSCode34.jpg

「deployment.template.json」を選択します。
VSCode35.jpg

「Existing Module(Enter Full Image URL)」を選択します。
VSCode36.jpg

「CameraCapture」と入力し、Enter を押下します。
VSCode37.jpg

「embeddedgeorge/cameracapture:0.3.0-arm32v7」と入力し、Enter を押下します。
VSCode38.jpg

下の図のように3か所修正します。
Module 名を「CameraCapture」→「camera-capture」に変更します。
「restartPolicy」の次に以下の「env」ブロックを追加します。

"env": {
  "VIDEO_PATH": {
    "value": 0
  },
  "IMAGE_PROCESSING_ENDPOINT": {
    "value": "http://store-image-to-local-blob:80/image"
  },
  "RESIZE_WIDTH": {
    "value": 256
  },
  "RESIZE_HEIGHT": {
    "value": 256
  },
  "SHOW_VIDEO": {
    "value": "True"
  }
},

さらに、「settings」の「createOptions」に以下を追加します。

"HostConfig": {
  "PortBindings": {
    "5012/tcp": [
      {
        "HostPort": "5012"
      }
    ]
  },
  "Binds": [
    "/dev/video0:/dev/video0"
  ],
  "Devices": [
    {
      "PathOnHost": "/dev/video0",
      "PathInContainer": "/dev/video0",
      "CgroupPermissions": "mrw"
    }
  ]
}

修正、追加が完了したら、ファイルを保存しておきます。
VSCode39.jpg

10)Solution の Build と Azure Container Registry への Push

これまでの手順で、IoT Edge 上の Module の準備が終わったので、Solution を Build していきます。

Visual Studio Code のメニューバー「表示」→「コマンドパレット」を起動し、「Azure IoT Edge: Build and Push IoT Edge Solution」を選択します。
VSCode40.jpg

「deployment.template.json」を選択します。
Build が開始され、終了後 Image が Azure Container Registry に Push されます。
VSCode41.jpg

Push 時に Unauthorized エラーが出た場合、Build で使ったターミナルで以下のコマンドを使ってログインすること。その後、上記の手順を再実行します。

  • acr-user-name : Azure Container Registry のユーザー名
  • acr-password : Azure Container Registry のパスワード
  • acr-url : Azure Container Registry の URL
docker login -u <acr-user-name> -p <acr-password> <acr-url>

Azure Container Registry に Push されていることを確認します。
VSCode42.jpg

11)Module 配置情報を IoT Hub に転送

Azure Container Registry に Docker Image を Push できたので、Module の配置情報を IoT Hub に転送します。
これによって、IoT Edge が IoT Hub から情報を取得し、Azure Container Registry に Pull しにいきます。

Visual Studio Code のメニューバー「表示」→「コマンドパレット」を起動し、「Azure IoT Hub: IoT Hub を選択」を選択します。
VSCode43.jpg

今回利用しているサブスクリプションを選択します。
VSCode44.jpg

転送する IoT Hub を選択します。
VSCode45.jpg

Visual Studio Code のメニューバー「表示」→「コマンドパレット」を起動し、「Azure IoT Edge: 1つの IoT Edge に配置」を選択します。
VSCode46.jpg

IoT Edge のデバイス名を選択します。
VSCode47.jpg

Solution Build 時に、deployment.template.json から生成された config/deployment.arm32v7.json が IoT Hub へ転送されます。
VSCode48.jpg
VSCode49.jpg

IoT Hub へ転送されました。
VSCode50.jpg

12)動作確認

最後に動作確認を行います。
Raspberry Pi に接続したカメラで撮影した画像が、Azure Blob Storage に格納されていれば OK です。

最初に、IoT Edge に作成した Module がデプロイできていることを確認します。
IoT Hub のメニューの中から「IoT Edge」をクリックし、設定したデバイス ID をクリックします。ここでは「raspi3bp」です。
Check01.jpg

5つの Module すべてが「running」の状態となっており、問題なく稼働していることが確認できます。
Check02.jpg

次に Blob Storage の確認を行います。
ストレージを選択し、「BLOB」をクリックし、「edgephotos」をクリックします。
Check03.jpg

「raspi3bp-img20190915060645.jpg」というファイルが格納されているのが分かります。これらは、Raspberry Pi のカメラが撮影した画像になります。無事に転送されていることが確認できました。
Check04.jpg

動かない場合

Raspberry Pi のシェル上(リモート接続の Tera Term 等でも良い)で、以下のコマンドを実行し、Module が正常に動いているか確認します。

sudo iotedge list

正常に動いていない Module がある場合、実行ログを確認します。例えば camera-capture の場合は以下となります。

sudo iotedge logs camera-capture

Pull してきた Module を更新する場合、通常はバージョンやタグをつけなおして Azure Container Registry に Push するが、開発環境で試している場合はやらないケースもあると思います。そのような場合、IoT Edge できちんと新しいものを Pull してくれるように、Docker Image を削除しておく必要があります。

sudo docker images
sudo systemctl stop iotedge
sudo docker image rm -f <docker image id>
sudo systemctl start iotedge

色々と調べていくのも手ですが、新規に作り直すほうが早い場合もあります。

まとめ

いかがでしたでしょうか?無事に画像を Azure Blob Storage に格納できましたでしょうか?
今回 IoT Edge を触ってみて、以下の3点が理解できたのではないかと思います。

  • IoT Edge は Docker 互換のコンテナ―である
  • 開発した IoT Edge のモジュールを Azure Container Registry に Push し、IoT Edge は IoT Hub 経由で配置情報を取得、新しい Docker Image を Pull することができる
  • クラウドのサービスである Azure Functions、Azure Stream Analytics、Azure Machine Learning も IoT Edge 上にデプロイでき、Edge デバイスでクラウドのテクノロジーを利用できる(今回は Marketplace にある Azure Blob Storage on IoT Edge を利用しました)
Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away