メインコンテンツまでスキップ

アプリケーションロジックをクラウドに移行する

このレッスンの概要を示すスケッチノート

スケッチノート: Nitya Narasimhan. 画像をクリックすると大きなバージョンが表示されます。

このレッスンは、Microsoft ReactorIoT 01 for Beginners Project 2 - Digital Agriculture seriesの一環として教えられました。

サーバーレスコードでIoTデバイスを制御する

講義前のクイズ

講義前のクイズ

はじめに

前回のレッスンでは、植物の土壌水分モニタリングとリレー制御をクラウドベースのIoTサービスに接続する方法を学びました。次のステップは、リレーのタイミングを制御するサーバーコードをクラウドに移行することです。このレッスンでは、サーバーレス関数を使用してこれを行う方法を学びます。

このレッスンでは以下の内容をカバーします:

サーバーレスとは何か?

サーバーレス、またはサーバーレスコンピューティングは、さまざまな種類のイベントに応じてクラウドで実行される小さなコードブロックを作成することを含みます。イベントが発生すると、コードが実行され、イベントに関するデータが渡されます。これらのイベントは、Webリクエスト、キューに置かれたメッセージ、データベースのデータの変更、またはIoTデバイスによってIoTサービスに送信されたメッセージなど、さまざまなものから発生する可能性があります。

IoTサービスからサーバーレスサービスに送信されるイベント、すべてが同時に実行される複数の関数によって処理される

💁 データベーストリガーを使用したことがある場合、これは同じことと考えることができます。行の挿入などのイベントによってトリガーされるコードです。

多くのイベントが同時に送信されると、サーバーレスサービスはそれらをすべて同時に実行するためにスケールアップします

コードはイベントが発生したときにのみ実行され、他の時間にはコードが生きていることはありません。イベントが発生すると、コードがロードされて実行されます。これにより、サーバーレスは非常にスケーラブルになります。多くのイベントが同時に発生した場合、クラウドプロバイダーは必要なだけ多くの関数を同時に実行することができます。これの欠点は、イベント間で情報を共有する必要がある場合、メモリに保存するのではなく、データベースなどの場所に保存する必要があることです。

コードは、イベントに関する詳細をパラメーターとして受け取る関数として記述されます。これらのサーバーレス関数を記述するために、さまざまなプログラミング言語を使用できます。

🎓 サーバーレスは、各イベントトリガーがコード内の関数として実装されるため、Functions as a service (FaaS) とも呼ばれます。

名前にもかかわらず、サーバーレスは実際にはサーバーを使用します。名前は、開発者としてコードを実行するために必要なサーバーについて気にする必要がないためです。クラウドプロバイダーには、サーバー、ネットワーキング、ストレージ、CPU、メモリなど、コードを実行するために必要なすべてを管理するサーバーレスランタイムがあります。このモデルでは、サービスに対してサーバーごとに支払うことはできません。代わりに、コードが実行されている時間と使用されているメモリの量に対して支払います。

💰 サーバーレスは、クラウドでコードを実行する最も安価な方法の1つです。たとえば、執筆時点では、あるクラウドプロバイダーは、すべてのサーバーレス関数が月に合計1,000,000回実行されるまで料金を請求せず、その後は1,000,000回の実行ごとにUS$0.20を請求します。コードが実行されていないときは、支払いは発生しません。

IoT開発者として、サーバーレスモデルは理想的です。クラウドホストされたIoTサービスに接続されている任意のIoTデバイスから送信されたメッセージに応じて呼び出される関数を記述できます。コードは送信されたすべてのメッセージを処理しますが、必要なときにのみ実行されます。

✅ MQTTを介してメッセージをリッスンするサーバーコードとして記述したコードを振り返ってください。これがサーバーレスを使用してクラウドでどのように実行されるかを考えてみてください。サーバーレスコンピューティングをサポートするためにコードがどのように変更されるかを考えてみてください。

💁 サーバーレスモデルは、コードの実行に加えて、他のクラウドサービスにも移行しています。たとえば、サーバーレスデータベースは、クエリや挿入などのデータベースに対して行われたリクエストごとに支払うサーバーレス料金モデルを使用してクラウドで利用できます。たとえば、主キーに対して1行を選択するだけの単一のクエリは、多くのテーブルを結合し、数千行を返す複雑な操作よりもコストが低くなります。

サーバーレスアプリケーションを作成する

MicrosoftのサーバーレスコンピューティングサービスはAzure Functionsと呼ばれます。

Azure Functionsのロゴ

以下の短いビデオは、Azure Functionsの概要を示しています

Azure Functions概要ビデオ

🎥 画像をクリックしてビデオを視聴

✅ 少し時間を取って調査し、Microsoft Azure FunctionsドキュメントのAzure Functionsの概要を読んでください。

Azure Functionsを記述するには、選択した言語でAzure Functionsアプリを開始します。Azure Functionsは、Python、JavaScript、TypeScript、C#、F#、Java、およびPowershellをサポートしています。このレッスンでは、PythonでAzure Functionsアプリを記述する方法を学びます。

💁 Azure Functionsはカスタムハンドラーもサポートしているため、HTTPリクエストをサポートする任意の言語で関数を記述できます。古い言語(COBOLなど)も含まれます。

Functionsアプリは、イベントに応答する関数であるトリガーの1つ以上で構成されます。1つのFunctionsアプリ内に複数のトリガーを持つことができ、すべてが共通の構成を共有します。たとえば、Functionsアプリの構成ファイルにはIoT Hubの接続詳細が含まれており、アプリ内のすべての関数がこれを使用して接続し、イベントをリッスンできます。

タスク - Azure Functionsツールのインストール

執筆時点では、Azure FunctionsコードツールはPythonプロジェクトでApple Siliconで完全には動作していません。代わりにIntelベースのMac、Windows PC、またはLinux PCを使用する必要があります。

Azure Functionsの優れた機能の1つは、ローカルで実行できることです。クラウドで使用されるのと同じランタイムをコンピューターで実行でき、IoTメッセージに応答するコードを記述してローカルで実行できます。イベントが処理されるときにコードをデバッグすることもできます。コードに満足したら、クラウドにデプロイできます。

Azure FunctionsツールはCLIとして利用可能で、Azure Functions Core Toolsと呼ばれます。

  1. Azure Functions Core Toolsドキュメントの指示に従って、Azure Functionsコアツールをインストールします。

  2. VS Code用のAzure Functions拡張機能をインストールします。この拡張機能は、Azure Functionsの作成、デバッグ、およびデプロイをサポートします。VS Codeにこの拡張機能をインストールする手順については、Azure Functions拡張機能ドキュメントを参照してください。

Azure Functionsアプリをクラウドにデプロイすると、アプリケーションファイルやログファイルなどを保存するために少量のクラウドストレージを使用する必要があります。Functionsアプリをローカルで実行する場合でも、クラウドストレージに接続する必要がありますが、実際のクラウドストレージを使用する代わりに、Azuriteと呼ばれるストレージエミュレーターを使用できます。これはローカルで実行されますが、クラウドストレージのように動作します。

🎓 Azureでは、Azure Functionsが使用するストレージはAzure Storage Accountです。これらのアカウントは、ファイル、ブロブ、テーブル内のデータ、またはキュー内のデータを保存できます。1つのストレージアカウントを複数のアプリ(FunctionsアプリやWebアプリなど)で共有できます。

  1. AzuriteはNode.jsアプリなので、Node.jsをインストールする必要があります。ダウンロードとインストールの手順はNode.jsウェブサイトで見つけることができます。Macを使用している場合は、Homebrewからもインストールできます。

  2. 次のコマンドを使用してAzuriteをインストールします(npmはNode.jsをインストールするとインストールされるツールです):

    npm install -g azurite
  3. Azuriteがデータを保存するためのフォルダーを作成します:

    mkdir azurite
  4. この新しいフォルダーを渡してAzuriteを実行します:

    azurite --location azurite

    Azuriteストレージエミュレーターが起動し、ローカルFunctionsランタイムが接続できるようになります。

    ➜  ~ azurite --location azurite  
    Azurite Blob service is starting at http://127.0.0.1:10000
    Azurite Blob service is successfully listening at http://127.0.0.1:10000
    Azurite Queue service is starting at http://127.0.0.1:10001
    Azurite Queue service is successfully listening at http://127.0.0.1:10001
    Azurite Table service is starting at http://127.0.0.1:10002
    Azurite Table service is successfully listening at http://127.0.0.1:10002

タスク - Azure Functionsプロジェクトを作成する

Azure Functions CLIを使用して新しいFunctionsアプリを作成できます。

  1. Functionsアプリ用のフォルダーを作成し、そこに移動します。soil-moisture-triggerと呼びます

    mkdir soil-moisture-trigger
    cd soil-moisture-trigger
  2. このフォルダー内にPython仮想環境を作成します:

    python3 -m venv .venv
  3. 仮想環境をアクティブにします:

    • Windowsの場合:

      • コマンドプロンプトまたはWindows Terminalを使用している場合は、次のコマンドを実行します:

        .venv\Scripts\activate.bat
      • PowerShellを使用している場合は、次のコマンドを実行します:

        .\.venv\Scripts\Activate.ps1
    • macOSまたはLinuxの場合は、次のコマンドを実行します:

      source ./.venv/bin/activate

    💁 これらのコマンドは、仮想環境を作成したのと同じ場所から実行する必要があります。.venvフォルダーに移動する必要はなく、仮想環境をアクティブにするコマンドとパッケージをインストールするコマンドは、仮想環境を作成したフォルダーから実行する必要があります。

  4. 次のコマンドを実行して、このフォルダーにFunctionsアプリを作成します:

    func init --worker-runtime python soil-moisture-trigger

    これにより、現在のフォルダー内に3つのファイルが作成されます:

    • host.json - このJSONドキュメントには、Functionsアプリの設定が含まれています。これらの設定を変更する必要はありません。
    • local.settings.json - このJSONドキュメントには、IoT Hubの接続文字列など、アプリがローカルで実行されるときに使用する設定が含まれています。これらの設定はローカルのみであり、ソースコード管理に追加しないでください。アプリをクラウドにデプロイするとき、これらの設定はデプロイされず、代わりにアプリケーション設定から設定が読み込まれます。これはこのレッスンの後半で説明します。
    • requirements.txt - これは、Functionsアプリを実行するために必要なPipパッケージを含むPip requirementsファイルです。
  5. local.settings.jsonファイルには、Functionsアプリが使用するストレージアカウントの設定があります。これはデフォルトで空の設定になっているため、設定する必要があります。ローカルストレージエミュレーターAzuriteに接続するには、この値を次のように設定します:

    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
  6. requirementsファイルを使用して必要なPipパッケージをインストールします:

    pip install -r requirements.txt

    💁 必要なPipパッケージはこのファイルに含まれている必要があります。Functionsアプリがクラウドにデプロイされるとき、ランタイムは正しいパッケージをインストールすることを確認します。

  7. すべてが正しく動作していることを確認するために、Functionsランタイムを起動できます。次のコマンドを実行してこれを行います:

    func start

    ランタイムが起動し、ジョブ関数(トリガー)が見つからなかったことを報告します。

    (.venv) ➜  soil-moisture-trigger func start
    Found Python version 3.9.1 (python3).

    Azure Functions Core Tools
    Core Tools Version: 3.0.3442 Commit hash: 6bfab24b2743f8421475d996402c398d2fe4a9e0 (64-bit)
    Function Runtime Version: 3.0.15417.0

    [2021-05-05T01:24:46.795Z] No job functions found.

    ⚠️ ファイアウォール通知が表示された場合、funcアプリケーションがネットワークに読み書きできるようにアクセスを許可します。

    ⚠️ macOSを使用している場合、出力に警告が表示されることがあります:

    (.venv) ➜  soil-moisture-trigger func start
    Found Python version 3.9.1 (python3).

    Azure Functions Core Tools
    Core Tools Version: 3.0.3442 Commit hash: 6bfab24b2743f8421475d996402c398d2fe4a9e0 (64-bit)
    Function Runtime Version: 3.0.15417.0

    [2021-06-16T08:18:28.315Z] Cannot create directory for shared memory usage: /dev/shm/AzureFunctions
    [2021-06-16T08:18:28.316Z] System.IO.FileSystem: Access to the path '/dev/shm/AzureFunctions' is denied. Operation not permitted.
    [2021-06-16T08:18:30.361Z] No job functions found.

    Functionsアプリが正しく起動し、実行中の関数をリストしている限り、これらの警告は無視できます。 Microsoft Docs Q&Aのこの質問で述べられているように、無視できます。

  8. ctrl+cを押してFunctionsアプリを停止します。

  9. VS Codeで現在のフォルダーを開きます。VS Codeを開いてこのフォルダーを開くか、次のコマンドを実行します:

    code .

    VS CodeはFunctionsプロジェクトを検出し、次の通知を表示します:

    フォルダー "soil-moisture-trigger" にVS Codeの外部で作成された可能性のあるAzure Functionsプロジェクトが検出されました。VS Codeで最適に使用するために初期化しますか?

    通知

    この通知からはいを選択します。

  10. VS CodeターミナルでPython仮想環境が実行されていることを確認します。必要に応じて終了して再起動します。

IoT Hubイベントトリガーを作成する

Functionsアプリはサーバーレスコードのシェルです。IoT Hubイベントに応答するには、このアプリにIoT Hubトリガーを追加できます。このトリガーは、IoT Hubに送信されるメッセージのストリームに接続し、それに応答する必要があります。このメッセージのストリームを取得するには、トリガーがIoT Hubのイベントハブ互換エンドポイントに接続する必要があります。

IoT Hubは、Azure Event Hubsと呼ばれる別のAzureサービスに基づいています。Event Hubsはメッセージの送受信を可能にするサービスであり、IoT Hubはこれを拡張してIoTデバイス向けの機能を追加します。IoT Hubからメッセージを読み取る方法は、Event Hubsを使用する場合と同じです。

✅ 調査を行い、Azure Event HubsドキュメントのEvent Hubsの概要を読んでください。基本機能がIoT Hubとどのように比較されるかを確認してください。

IoTデバイスがIoT Hubに接続するには、接続を許可されたデバイスのみが接続できるようにする秘密鍵を使用する必要があります。メッセージを読み取るために接続する場合も同様で、コードには秘密鍵を含む接続文字列とIoT Hubの詳細が必要です。

💁 デフォルトの接続文字列にはiothubownerの権限があり、これを使用するコードにはIoT Hubの完全な権限があります。理想的には、必要な最低限の権限で接続する必要があります。これは次のレッスンで説明します。

トリガーが接続されると、IoT Hubに送信されたすべてのメッセージに対して関数内のコードが呼び出されます。メッセージをパラメーターとして渡されます。

タスク - イベントハブ互換エンドポイントの接続文字列を取得する

  1. VS Codeターミナルから次のコマンドを実行して、IoT Hubのイベントハブ互換エンドポイントの接続文字列を取得します:

    az iot hub connection-string show --default-eventhub \
    --output table \
    --hub-name <hub_name>

    <hub_name>を使用したIoT Hubの名前に置き換えます。

  2. VS Codeでlocal.settings.jsonファイルを開きます。Valuesセクション内に次の追加値を追加します:

    "IOT_HUB_CONNECTION_STRING": "<connection string>"

    <connection string>を前のステップの値に置き換えます。この行の後にカンマを追加して、有効なJSONにする必要があります。

タスク - イベントトリガーを作成する

イベントトリガーを作成する準備が整いました。

  1. VS Codeターミナルから次のコマンドをsoil-moisture-triggerフォルダー内で実行します:

    func new --name iot-hub-trigger --template "Azure Event Hub trigger"

    これにより、iot-hub-triggerという名前の新しい関数が作成されます。トリガーはIoT Hubのイベントハブ互換エンドポイントに接続するため、イベントハブトリガーを使用できます。特定のIoT Hubトリガーはありません。

これにより、soil-moisture-triggerフォルダー内にiot-hub-triggerというフォルダーが作成され、この関数が含まれます。このフォルダーには次のファイルが含まれます:

  • __init__.py - これは、標準のPythonファイル名の命名規則を使用して、このフォルダーをPythonモジュールに変えるトリガーを含むPythonコードファイルです。

    このファイルには次のコードが含まれます:

    import logging

    import azure.functions as func


    def main(event: func.EventHubEvent):
    logging.info('Python EventHub trigger processed an event: %s',
    event.get_body().decode('utf-8'))

    トリガーの中心はmain関数です。この関数がIoT Hubからのイベントとともに呼び出されます。この関数にはeventというパラメーターがあり、EventHubEventが含まれます。IoT Hubにメッセージが送信されるたびに、この関数が呼び出され、そのメッセージがeventとして渡され、前回のレッスンで見た注釈と同じプロパティが渡されます。

    この関数の中心はイベントをログに記録することです。

  • function.json - これはトリガーの設定を含む構成ファイルです。主な構成はbindingsセクションにあります。バインディングは、Azure Functionsと他のAzureサービス間の接続を指す用語です。この関数にはイベントハブへの入力バインディングがあります。イベントハブに接続してデータを受信します。

    💁 関数の出力を別のサービスに送信する出力バインディングもあります。たとえば、データベースへの出力バインディングを追加し、関数からIoT Hubイベントを返すと、自動的にデータベースに挿入されます。

    ✅ 調査を行い、Azure Functionsトリガーとバインディングの概念ドキュメントでバインディングについて読んでください。

    bindingsセクションにはバインディングの設定が含まれます。注目すべき値は次のとおりです:

    • "type": "eventHubTrigger" - これは関数がイベントハブからのイベントをリッスンすることを示します

    • "name": "events" - これはイベントハブイベントのパラメーター名です。これはPythonコードのmain関数のパラメーター名と一致します。

    • "direction": "in" - これは入力バインディングであり、イベントハブからのデータが関数に入ります

    • "connection": "" - これは接続文字列を読み取る設定の名前を定義します。ローカルで実行する場合、この設定はlocal.settings.jsonファイルから読み取られます。

      💁 接続文字列はfunction.jsonファイルに保存できず、設定から読み取る必要があります。これは接続文字列を誤って公開しないようにするためです。

  1. Azure Functionsテンプレートのバグにより、function.jsoncardinalityフィールドの値が間違っています。このフィールドをmanyからoneに更新します:

    "cardinality": "one",
  2. function.jsonファイルのconnectionの値を、local.settings.jsonファイルに追加した新しい値を指すように更新します:

    "connection": "IOT_HUB_CONNECTION_STRING",

    💁 これは設定を指す必要があり、実際の接続文字列を含む必要はありません。

  3. 接続文字列にはeventHubName値が含まれているため、function.jsonファイルのこの値を空の文字列に設定する必要があります。この値を次のように更新します:

    "eventHubName": "",

タスク - イベントトリガーを実行する

  1. IoT Hubイベントモニターを実行していないことを確認します。Functionsアプリと同時に実行されている場合、Functionsアプリは接続してイベントを消費できません。

    💁 複数のアプリが異なるコンシューマーグループを使用してIoT Hubエンドポイントに接続できます。これらは後のレッスンで説明します。

  2. Functionsアプリを実行するには、VS Codeターミナルから次のコマンドを実行します:

    func start

    Functionsアプリが起動し、iot-hub-trigger関数を検出します。その後、過去1日に送信されたイベントを処理します。

    (.venv) ➜  soil-moisture-trigger func start
    Found Python version 3.9.1 (python3).

    Azure Functions Core Tools
    Core Tools Version: 3.0.3442 Commit hash: 6bfab24b2743f8421475d996402c398d2fe4a9e0 (64-bit)
    Function Runtime Version: 3.0.15417.0

    Functions:

    iot-hub-trigger: eventHubTrigger

    For detailed output, run func with --verbose flag.
    [2021-05-05T02:44:07.517Z] Worker process started and initialized.
    [2021-05-05T02:44:09.202Z] Executing 'Functions.iot-hub-trigger' (Reason='(null)', Id=802803a5-eae9-4401-a1f4-176631456ce4)
    [2021-05-05T02:44:09.205Z] Trigger Details: PartitionId: 0, Offset: 1011240-1011632, EnqueueTimeUtc: 2021-05-04T19:04:04.2030000Z-2021-05-04T19:04:04.3900000Z, SequenceNumber: 2546-2547, Count: 2
    [2021-05-05T02:44:09.352Z] Python EventHub trigger processed an event: {"soil_moisture":628}
    [2021-05-05T02:44:09.354Z] Python EventHub trigger processed an event: {"soil_moisture":624}
    [2021-05-05T02:44:09.395Z] Executed 'Functions.iot-hub-trigger' (Succeeded, Id=802803a5-eae9-4401-a1f4-176631456ce4, Duration=245ms)

    各関数呼び出しは、出力内のExecuting 'Functions.iot-hub-trigger'/Executed 'Functions.iot-hub-trigger'ブロックで囲まれます。各関数呼び出しで処理されたメッセージの数が表示されます。

  3. IoTデバイスが実行されていることを確認します。Functionsアプリに新しい土壌水分メッセージが表示されます。

  4. Functionsアプリを停止して再起動します。以前のメッセージは再処理されず、新しいメッセージのみが処理されることがわかります。

💁 VS CodeはFunctionsのデバッグもサポートしています。コードの各行の開始部分をクリックしてブレークポイントを設定するか、コード行にカーソルを置いて実行 -> ブレークポイントの切り替えを選択するか、F9を押します。実行 -> デバッグの開始を選択するか、F5を押すか、実行とデバッグペインを選択してデバッグの開始ボタンを選択してデバッガーを起動できます。これにより、処理されているイベントの詳細を確認できます。

トラブルシューティング

  • 次のエラーが発生した場合:

    The listener for function 'Functions.iot-hub-trigger' was unable to start. Microsoft.WindowsAzure.Storage: Connection refused. System.Net.Http: Connection refused. System.Private.CoreLib: Connection refused.

    Azuriteが実行されており、local.settings.jsonファイルのAzureWebJobsStorageUseDevelopmentStorage=trueに設定されていることを確認します。

  • 次のエラーが発生した場合:

    System.Private.CoreLib: Exception while executing function: Functions.iot-hub-trigger. System.Private.CoreLib: Result: Failure Exception: AttributeError: 'list' object has no attribute 'get_body'

    function.jsonファイルのcardinalityoneに設定されていることを確認します。

  • 次のエラーが発生した場合:

    Azure.Messaging.EventHubs: The path to an Event Hub may be specified as part of the connection string or as a separate value, but not both.  Please verify that your connection string does not have the `EntityPath` token if you are passing an explicit Event Hub name. (Parameter 'connectionString').

    function.jsonファイルのeventHubNameが空の文字列に設定されていることを確認します。

サーバーレスコードから直接メソッドリクエストを送信する

これまでのところ、Functionsアプリはイベントハブ互換エンドポイントを使用してIoT Hubからのメッセージをリッスンしています。次に、IoTデバイスにコマンドを送信する必要があります。これは、レジストリマネージャーを介してIoT Hubに異なる接続を使用して行います。レジストリマネージャーは、IoT Hubに登録されているデバイスを確認し、クラウドからデバイスへのメッセージ、直接メソッドリクエスト、またはデバイステインの更新を送信するためのツールです。また、IoT HubからIoTデバイスを登録、更新、削除することもできます。

レジストリマネージャーに接続するには、接続文字列が必要です。

タスク - レジストリマネージャーの接続文字列を取得する

  1. 接続文字列を取得するには、次のコマンドを実行します:

    az iot hub connection-string show --policy-name service \
    --output table \
    --hub-name <hub_name>

    <hub_name>を使用したIoT Hubの名前に置き換えます。

    接続文字列は、--policy-name serviceパラメーターを使用してServiceConnectポリシーのために要求されます。接続文字列を要求するときに、その接続文字列が許可する権限を指定できます。ServiceConnectポリシーは、コードが接続してIoTデバイスにメッセージを送信することを許可します。

    ✅ 調査を行い、IoT Hubの権限ドキュメントでさまざまなポリシーについて読んでください。

  2. VS Codeでlocal.settings.jsonファイルを開きます。Valuesセクション内に次の追加値を追加します:

    "REGISTRY_MANAGER_CONNECTION_STRING": "<connection string>"

    <connection string>を前のステップの値に置き換えます。この行の後にカンマを追加して、有効なJSONにする必要があります。

タスク - デバイスに直接メソッドリクエストを送信する

  1. レジストリマネージャーのSDKはPipパッケージを介して利用可能です。requirements.txtファイルに次の行を追加して、このパッケージへの依存関係を追加します:

    azure-iot-hub
  2. VS Codeターミナルで仮想環境がアクティブになっていることを確認し、次のコマンドを実行してPipパッケージをインストールします:

    pip install -r requirements.txt
  3. __init__.pyファイルに次のインポートを追加します:

    import json
    import os
    from azure.iot.hub import IoTHubRegistryManager
    from azure.iot.hub.models import CloudToDeviceMethod

    これは、システムライブラリとレジストリマネージャーと直接メソッドリクエストを操作するためのライブラリをインポートします。

  4. mainメソッド内のコードを削除しますが、メソッド自体は保持します。

  5. mainメソッドに次のコードを追加します:

    body = json.loads(event.get_body().decode('utf-8'))
    device_id = event.iothub_metadata['connection-device-id']

    logging.info(f'Received message: {body} from {device_id}')

    このコードは、IoTデバイスから送信されたJSONメッセージを含むイベントの本文を抽出します。

    次に、メッセージとともに渡される注釈からデバイスIDを取得します。イベントの本文にはテレメトリとして送信されたメッセージが含まれ、iothub_metadataディクショナリには、送信者のデバイスIDやメッセージが送信された時間など、IoT Hubによって設定されたプロパティが含まれます。

    この情報はログに記録されます。Functionsアプリをローカルで実行するときに、このログをターミナルで確認できます。

  6. これに続けて次のコードを追加します:

    soil_moisture = body['soil_moisture']

    if soil_moisture > 450:
    direct_method = CloudToDeviceMethod(method_name='relay_on', payload='{}')
    else:
    direct_method = CloudToDeviceMethod(method_name='relay_off', payload='{}')

    このコードはメッセージから土壌水分を取得します。次に、値に応じて、relay_onまたはrelay_off直接メソッドリクエストのヘルパークラスを作成します。メソッドリクエストにはペイロードが必要ないため、空のJSONドキュメントが送信されます。

  7. これに続けて次のコードを追加します:

    logging.info(f'Sending direct method request for {direct_method.method_name} for device {device_id}')

    registry_manager_connection_string = os.environ['REGISTRY_MANAGER_CONNECTION_STRING']
    registry_manager = IoTHubRegistryManager(registry_manager_connection_string)

    このコードは、local.settings.jsonファイルからREGISTRY_MANAGER_CONNECTION_STRINGを読み込みます。このファイルの値は環境変数として利用可能であり、これらはos.environ関数を使用して読み取ることができます。この関数はすべての環境変数のディクショナリを返します。

    💁 このコードがクラウドにデプロイされると、local.settings.jsonファイルの値はアプリケーション設定として設定され、環境変数から読み取ることができます。

    次に、接続文字列を使用してレジストリマネージャーのヘルパークラスのインスタンスを作成します。

  8. これに続けて次のコードを追加します:

    registry_manager.invoke_device_method(device_id, direct_method)

    logging.info('Direct method request sent!')

    このコードは、レジストリマネージャーに直接メソッドリクエストを送信するように指示します。

    💁 MQTTを使用してメッセージをリッスンするサーバーコードとして記述した以前のバージョンのアプリでは、リレー制御コマンドはすべてのデバイスに送信されました。コードは1つのデバイスしかないと仮定していました。このバージョンのコードは、単一のデバイスにメソッドリクエストを送信するため、複数の土壌水分センサーとリレーのセットアップがある場合でも、正しいデバイスに正しい直接メソッドリクエストを送信できます。

  9. Functionsアプリを実行し、IoTデバイスがデータを送信していることを確認します。メッセージが処理され、直接メソッドリクエストが送信されるのがわかります。土壌水分センサーを土壌に出し入れして値を変化させ、リレーがオンとオフになるのを確認します。

💁 このコードはcode/functionsフォルダーにあります。

サーバーレスコードをクラウドにデプロイする

コードがローカルで動作しているので、次のステップはFunctionsアプリをクラウドにデプロイすることです。

タスク - クラウドリソースを作成する

Functionsアプリは、前のレッスンで作成したIoT Hub用のリソースグループ内にあるAzureのFunctions Appリソースにデプロイする必要があります。また、ローカルで実行しているエミュレートされたものを置き換えるために、Azureで作成されたストレージアカウントも必要です。

  1. 次のコマンドを実行してストレージアカウントを作成します:

    az storage account create --resource-group soil-moisture-sensor \
    --sku Standard_LRS \
    --name <storage_name>

    <storage_name>をストレージアカウントの名前に置き換えます。これは、ストレージアカウントにアクセスするために使用されるURLの一部を形成するため、グローバルに一意である必要があります。この名前には小文字と数字のみを使用でき、他の文字は使用できません。また、24文字に制限されています。smsのような名前を使用し、ランダムな単語や名前などの一意の識別子を追加します。

    --sku Standard_LRSは、最低コストの汎用アカウントを選択する価格設定ティアを選択します。無料ティアのストレージはなく、使用した分だけ支払います。コストは比較的低く、最も高価なストレージでも1ギガバイトあたり月額US$0.05未満です。

    ✅ 価格についてはAzure Storage Account価格ページで確認してください。

  2. 次のコマンドを実行してFunction Appを作成します:

    az functionapp create --resource-group soil-moisture-sensor \
    --runtime python \
    --functions-version 3 \
    --os-type Linux \
    --consumption-plan-location <location> \
    --storage-account <storage_name> \
    --name <functions_app_name>

    <location>を前のレッスンでリソースグループを作成した場所に置き換えます。

    <storage_name>を前のステップで作成したストレージアカウントの名前に置き換えます。

    <functions_app_name>をFunctions Appの一意の名前に置き換えます。これは、Functions Appにアクセスするために使用されるURLの一部を形成するため、グローバルに一意である必要があります。soil-moisture-sensor-のような名前を使用し、ランダムな単語や名前などの一意の識別子を追加します。

    --functions-version 3オプションは、使用するAzure Functionsのバージョンを設定します。バージョン3は最新バージョンです。

    --os-type Linuxは、これらの関数をホストするためにLinuxをOSとして使用するようにFunctionsランタイムに指示します。Functionsは、使用するプログラミング言語に応じてLinuxまたはWindowsでホストできます。PythonアプリはLinuxでのみサポートされています。

タスク - アプリケーション設定をアップロードする

Functionsアプリを開発するときに、IoT Hubの接続文字列などの設定をlocal.settings.jsonファイルに保存しました。これらは、コードで使用できるようにFunctions Appのアプリケーション設定に書き込む必要があります。

🎓 local.settings.jsonファイルはローカル開発設定専用であり、ソースコード管理(GitHubなど)にチェックインしないでください。クラウドにデプロイされると、アプリケーション設定が使用されます。アプリケーション設定はクラウドでホストされるキー/値ペアであり、コード内またはランタイムがIoT Hubに接続するときに環境変数から読み取られます。

  1. 次のコマンドを実行して、Functions Appのアプリケーション設定にIOT_HUB_CONNECTION_STRING設定を設定します:

    az functionapp config appsettings set --resource-group soil-moisture-sensor \
    --name <functions_app_name> \
    --settings "IOT_HUB_CONNECTION_STRING=<connection string>"

    <functions_app_name>をFunctions Appの名前に置き換えます。

    <connection string>local.settings.jsonファイルのIOT_HUB_CONNECTION_STRINGの値に置き換えます。

  2. 上記の手順を繰り返し、REGISTRY_MANAGER_CONNECTION_STRINGの値をlocal.settings.jsonファイルの対応する値に設定します。

これらのコマンドを実行すると、Functions Appのすべてのアプリケーション設定のリストも出力されます。これを使用して、値が正しく設定されていることを確認できます。

💁 AzureWebJobsStorageの値が既に設定されていることがわかります。local.settings.jsonファイルでは、この値はローカルストレージエミュレーターを使用するように設定されていました。Functions Appを作成するときに、ストレージアカウントをパラメーターとして渡し、この設定に自動的に設定されます。

タスク - Functions Appをクラウドにデプロイする

Functions Appが準備できたので、コードをデプロイできます。

  1. VS Codeターミナルから次のコマンドを実行してFunctions Appを公開します:

    func azure functionapp publish <functions_app_name>

    Replace <functions_app_name> with the name you used for your Functions App.

コードがパッケージ化され、Functions Appに送信され、デプロイされて起動されます。多くのコンソール出力があり、デプロイメントの確認とデプロイされた関数のリストで終了します。この場合、リストにはトリガーのみが含まれます。

Deployment successful.
Remote build succeeded!
Syncing triggers...
Functions in soil-moisture-sensor:
iot-hub-trigger - [eventHubTrigger]

IoTデバイスが動作していることを確認してください。土壌の湿度を調整するか、センサーを土壌の中に出し入れすることで湿度レベルを変更します。土壌の湿度が変化すると、リレーがオンとオフを繰り返すのがわかります。


🚀 チャレンジ

前のレッスンでは、リレーがオンの間およびオフにした直後にMQTTメッセージの購読を解除することで、リレーのタイミングを管理しました。ここではこの方法を使用することはできません - IoT Hubトリガーの購読を解除することはできません。

Functions Appでこれを処理するためのさまざまな方法を考えてみてください。

講義後のクイズ

講義後のクイズ

レビューと自己学習

Assignment

コードがパッケージ化され、Functions Appに送信され、デプロイされて起動されます。多くのコンソール出力があり、デプロイメントの確認とデプロイされた関数のリストで終了します。この場合、リストにはトリガーのみが含まれます。