Cloud Intelligence™Cloud Intelligence™

Cloud Intelligence™

本番規模IoTのベストプラクティス:Google Cloud実装編(第1回/全3回)

By Matthew PorterJan 5, 202113 min read

このページはEnglishDeutschEspañolFrançaisItalianoPortuguêsでもご覧いただけます。

1 mzhmgbzadzlrio1 pmnv8w

フルマネージド、オートスケール、サーバーレスなIoT基盤をクラウド上に構築する方法に興味はありませんか?

全3回でお届けする本シリーズ「IoT on GCP」では、デバイス登録からデータのストリーミング・長期保存、さらには分析・可視化まで、IoTワークフローの全体像を一通り取り上げます。

本番規模IoTのベストプラクティス

第1回を読み終える頃には、数百万台規模のIoTデバイスを安全に登録し、テレメトリデータをGoogle Cloud環境へストリーミングする方法が身についているはずです。

本記事は全3回シリーズの第1回です。続編では以下を予定しています。

  • 第2回:高スループットなデータストリームの適切な保存と可視化
  • 第3回:IoTデータを使った実用的な機械学習モデルの構築

シリーズを通じて、クラウドプロバイダーにはGCPを、IoTデバイスの例として温度センサー付きRaspberry Piを使用します。

概要

本記事は次のセクションで構成しています。

  1. Raspberry Piのソフトウェア・ハードウェアのセットアップ
  2. デバイス登録と認証情報のプロビジョニング(実践)
  3. デバイス接続テストと温度データのストリーミング
  4. Google Cloud IoTの機能上の留意点
  5. ストリーミングデータの保存(第2回)
  6. ストリーミングデータの可視化(第2回)
  7. IoTデータを使った効果的な機械学習モデルの構築(第3回)

読み進めるにあたって必要なのは、BashとPythonの利用経験、そしてGoogle Cloud Consoleの基本操作だけです。

Raspberry Piのソフトウェアとハードウェアをセットアップする

まずはRaspberry Piを複数台用意して稼働させます。本稿ではPi 3を使用します。microSDカードへのOSインストールを自動化するために、Raspbian OS Imagerの利用をおすすめします。

Pythonパッケージをインストールする

デスクトップが立ち上がり、デバイスのアップデートが完了したら、次のコマンドでGoogle Cloud IoT用のSDKをインストールします。

pip3 install -U pyjwt paho-mqtt

先に進む前に、なぜ汎用のgoogle-api-python-client(Google Cloud用Python SDK)ではなくpaho-mqttを入れるのかを押さえておきましょう。よく使われるPython SDKやgcloud GCP CLIはいずれもHTTPベースで、ほとんどの操作を素早く実行するには便利ですが、接続が断続的になりがちな長時間接続でメッセージを送信し、データが主にデバイスから外側へ流れるような用途には向いていません。

MQTTは、paho-mqttのようなパッケージを介してIoTで広く使われているプロトコルで、接続が不安定な環境でもデバイスが頻繁にメッセージをパブリッシュし、まれにメッセージを受信できるように設計されています。

デジタル温度センサーを接続する

必要なPythonパッケージのインストールが終わったら、次はデジタル温度センサーをRaspberry Piに接続します。本記事に沿って手を動かしたい方には、こちらのDS18B20センサーがおすすめです。

センサーをRaspberry Piに接続するために、以下も用意してください。

これらの到着待ちでも、シミュレートした温度をストリーミングするスクリプトを後ほど用意していますので、そのまま読み進めて問題ありません。

部品が揃っている方は、このチュートリアルの冒頭5分間で、センサーをRaspberry Piに接続し温度値が取得できるかを確認する手順を学べます。

上記チュートリアルの手順に加えて、再起動のたびにmodprobeを実行する代わりに、起動時にonewireモジュールが読み込まれるよう、/etc/modulesに以下を追記しておくのがおすすめです。

w1-gpio
w1-therm

IoTデバイスを登録する

ここからは、安全に登録されたデバイスを含むIoT Registryのセットアップを、実際に動く完成例として紹介します。これらのデバイスからGoogle Cloud IoTプラットフォームへ温度データをストリーミングしていきます。

デバイス用のレジストリを作成する

Google Cloud Consoleで次の操作を行います。

  1. 新規プロジェクトを作成(例:「IoTTempStreaming」)
  2. IoT Coreサービスへ移動し、IoT APIを有効化
  3. 「Create registry」をクリックし、IoTデバイスを追加するためのレジストリを作成

GCPへストリーミングするIoTデバイスは、IoT Registryに認証情報付きで登録されたデバイスとして検証されて初めて、IoT Coreにメッセージを送信できます。そのためレジストリの作成は欠かせない最初のステップです。

1 x6ytdhxs pffshahwra2mqメッセージが投入されるのを待つ、新規作成直後のIoT Core環境

Pub/Subトピックを作成する

IoT Coreに送信されたメッセージは、デバイス固有のdeviceIdなどの重要なメタデータとともに、Pub/Sub上の「トピック」へ裏側で転送されます。Pub/SubはGoogle Cloudが提供するフルマネージド、オートスケール、サーバーレスのメッセージキューです。

そこで、IoT Registryの作成画面でPub/Subトピックも併せて作成し、テレメトリデータを最終的にPub/Subで受け取って下流処理に渡せるようにします。

IoT Registry作成画面でPub/Subトピックを作る手順は次のとおりです。

  1. Registry IDを「RaspberryPiDevices」とし、リージョンはus-central1を選択
  2. デフォルトでIoTテレメトリメッセージが届くPub/Subトピック「sensordata」を作成
  3. 「Additional topics」で、「temperature」という名前のIoTサブフォルダにパブリッシュされたメッセージが届くPub/Subトピック「temperature」を新規作成

こうしておけば、特定のIoTサブフォルダに送られた温度メッセージだけが「temperature」トピックに届き、その他の(想定外の)テレメトリイベントはデフォルトの「sensordata」トピックに集約されます。

あわせて、詳細オプションを開いてHTTPのチェックを外し、MQTTプロトコルのみを許可しておくのもおすすめです。

1 hehi7hqvsqephvxoaagvsq温度センサーデータのストリーミング向けに構成したIoT Core Registry

作成後のレジストリは次のような表示になります。

1 mydo62tfghtw76giklkc6qセットアップが完了したIoT Core Registry

登録済みデバイスを作成する

デバイスレジストリができたので、次は登録済みデバイスを作成します。

まず必要になるのは、(1) デバイスに紐付ける公開鍵・秘密鍵のペアと、(2) Googleルート証明書です。

以下に、Raspberry Pi上で楕円曲線(EC)鍵を生成し、Googleルート証明書を取得するためのコマンドを示します(Google Cloudの該当ドキュメントに基づいています)。EC鍵は従来のRSA鍵よりデータサイズが小さく、回線品質の悪い環境でもIoTデバイスのスループットを保ちやすいというメリットがあります。

$ mkdir -p /home/pi/GCP/iot_keys/
$ cd /home/pi/GCP/iot_keys/
$ wget

https://pki.goog/roots.pem

$ openssl ecparam -genkey -name prime256v1 -noout -out ec_private.pem
$ openssl ec -in ec_private.pem -pubout -out ec_public.pem
$ cat ec_public.pem
$ cd ../

上記コマンドを実行したら、続けて次の操作を行います。

  1. ターミナルに表示された公開鍵をコピー
  2. IoT Registryを開き、Devicesに移動
  3. Add Deviceをクリック
  4. デバイスに名前を付け(例:「device1」)、下図のようにAuthentication欄にES256公開鍵を貼り付け
  5. Createをクリック

1 vkia4oezrnypz6y1knxdsg楕円曲線鍵の入力を求めるIoTデバイス登録画面

作成が完了すると、IoT Coreに登録されたデバイスに紐付く認証情報がRaspberry Piで利用できるようになります。

デバイスに割り当てられたNumeric ID、Project ID、Registry IDは控えておいてください。

これらの値と、デバイス上の鍵およびGoogleルート証明書を使って、gcp_iot_config.txtという設定ファイルを作成します。このファイルには、特定のデバイスが利用する認証情報とIoTエンドポイントの詳細を定義します。本記事のもう少し先では、この設定ファイルを使って温度値をストリーミングするPythonスクリプトを紹介します。

$ pwd
/home/pi/GCP/
$ cat gcp_iot_config.txt
[SETTINGS]
KEY_PATH = /home/pi/GCP/iot_keys/
PRIVATE_KEY = ec_private.pem
GOOGLE_ROOT_CERT = roots.pem
REGISTRY_ID = <REGISTRY_ID>
DEVICE_ID = <DEVICE_NUMERIC_ID>
REGION = us-central1
PROJECT_ID = <PROJECT_ID>

これで完了です。GCPのIoT Core認証プラットフォーム経由で、デバイスからPub/Subトピックへ安全にテレメトリデータをストリーミングする準備が整いました。新しいデバイスを追加するときは、上記の手順を繰り返して固有の認証情報を生成し、登録してください。

デバイス接続をテストする

次のスクリプト「publish_temps.py」を実行して、デバイス接続をテストしてみましょう。107行目と108行目のどちらをコメントアウトするかによって、デバイスから取得した実温度値、またはシミュレートした温度値のいずれかをストリーミングします。

(このスクリプトは、Google Cloudの2つのサンプル:#1#2を改変したものです)

$ pwd
/home/pi/GCP/
$ ./publish_temps.py

https://gist.github.com/doit-mattporter/7e548158a5dc03b3674282cda19ef663

このスクリプトには多くの処理が詰まっているので、安全な接続とデータストリーミングがどのように実現されているかを把握するためにも、コードを一読することを強くおすすめします。

実行すると、次のような出力が表示されるはずです。

1 xwupjjvzrfeowndrus3kgaMQTT経由でGCP IoT Coreへ流れる実温度値

このスクリプトをcrontabで再起動時に実行するように設定しておけば(onewireモジュールの読み込みを待つため、冒頭で30秒スリープ)、予期せぬ再起動が発生してもIoTデバイスはそのままストリーミングを再開できます。

$ crontab -e
@reboot sleep 30 && /home/pi/GCP/publish_temps.py -c /home/pi/GCP/gcp_iot_config.txt >> /home/pi/GCP/publish_temps.log 2>&1

ご自宅などに設置するすべてのRaspberry Piに対して、オンボーディングとストリーミングの手順を繰り返してください。これで、大規模なIoTデータの取り扱いに大きく前進したことになります。

データがストリーミングされていることを確認する

Pub/Subに届くテレメトリデータを確認して、メッセージがGoogle Cloudに到達しているかを素早くチェックしましょう。

  1. 「temperature」トピックに対するPub/Subサブスクリプションを作成
  2. そのサブスクリプションを開く
  3. View Messagesをクリックし、Pullラジオボタンを選択

到着したメッセージが順次表示され、メッセージ本文の温度・タイムスタンプ情報に加えて、どのデバイスから送信されたかを一意に識別する属性メタデータも確認できます。属性メタデータは分析やエンドユーザー向けダッシュボードに役立つだけでなく、不正利用やハッキングされたデバイスを特定・遮断する際にも有効です。

1 upi0lt7zazoqrrpyarx8fg届いた温度テレメトリメッセージの確認用に作成するPub/Subサブスクリプション

「temperature」トピックへのサブスクリプションは次のように表示されます。

1 gqxuwk5oyv0q8bpaits7nq「temperature」トピック向けPub/Subサブスクリプションのメッセージ表示画面

「PULL」をクリックすると、メッセージとその属性が表示され始めます。

1 1aj1ua6vjuje1s0rdqmtaPub/SubトピックからPub/Subサブスクリプションへ届いた温度データ:メッセージ本文1 j3pbnwvayyqmr0cgkgz1sgPub/SubトピックからPub/Subサブスクリプションへ届いた温度データ:一意識別用の属性

ここまで進められた方、本当におつかれさまでした。IoTデバイスからの実データがPub/Subに流れてくる様子は感動的ですが、これはまだ入り口に過ぎません。

このデータを本当に活かすには、必要に応じて変換し、データウェアハウスへ移動またはバッチロードしたうえで分析する必要があります。しかも、初期テストからペタバイト規模の本番運用、さらにその先まで、開発のあらゆる段階で安定的・大規模・コスト効率よく動かしつつ、インフラの保守はほとんど発生させたくありません。それを実現するGCPサービスのエコシステムについては、ぜひ第2回・第3回もお見逃しなく。

次回予告:保存と可視化

第2回では、大規模なIoTストリーミングデータを比較的手軽に実用レベルで活用できる、適切なETL・保存・可視化サービスについて解説します。お楽しみに。

Google Cloud IoTの機能上の留意点

フルマネージドGCPサービスにおけるdeviceIdの扱い、もう一歩進めてほしい点

お気づきかもしれませんが、このPythonスクリプトはメッセージ本文にdevice_idのキー・値ペアを含めてストリーミングしています。これは、IoT Coreがストリーミング元デバイス固有のキーペアとの紐付けを通じて自動付与する、Pub/SubメッセージのdeviceId属性と内容が重複しています。

では、なぜハッキングされたデバイスでは簡単になりすませてしまうdevice_idをわざわざスクリプトから送らせるのでしょうか?長期保存先に取り込まれたときに、データウェアハウスの内容が汚染されかねないのに、です。

少し話を先取りしますが、第2回で見るように、Pub/SubメッセージをApache BeamのGoogle Cloudフルマネージド版であるDataflowで変換しBigQueryへ送り込む、フルマネージド・オートスケール・ほぼサーバーレスな便利なワークフローがあります。

残念ながら、Dataflowに用意されているGCP製のデフォルトテンプレート「Pub/Sub-to-BigQuery」では、メッセージ属性をBigQueryへ取り込む手段がなく、メッセージ本文の中身しか転送できません。複数のGCPエンジニアにも確認しましたが、デフォルトテンプレートに対してカスタムのDataflow JavaScript UDFを書いて、BigQueryなどのDataflowシンクにメッセージ属性を追加することはできない、とのことでした。代わりにJavaでDataflowジョブを書く手もありますが、ゼロから作る場合でも既存のGCP製テンプレートを改変する場合でも、テンプレートの継続的な保守・更新が必要な手間のかかる作業であり、多くのGCPユーザーが望むものではないでしょう。

結果として、デフォルトの仕組みのままでは、Pub/Subに届き宛先データシンクへ送られていくIoTメッセージを、そのdeviceIdと紐付けるシンプルかつ安全な方法が存在しません。

この見落としについてはGCPに指摘済みで、以降、IoTユースケースで貴重かつ不可欠なこれらの値をデフォルトテンプレートでも引き継げるようにするためのパブリックなフィーチャーリクエストが立ち上がっています。このFRが対応され次第、本記事も更新する予定ですが、それまでの間、デモ目的であれば、本来デフォルトで備わっているべきDataflowテンプレートを書き直すよりも、メッセージ本文にdevice_idを載せて挙動を試す方がはるかに手軽です。

デバイス固有の認証情報を、理想的にはどのようにプロビジョニングしたいか?

単一のIoTデバイスを登録するプロセスは、一般に次のように説明されます。

  1. GCP上でIoT権限を付与した公開鍵・秘密鍵のペアを作成
  2. これらのファイルを工場でデバイスにプロビジョニング・配置
  3. 工場側でその認証情報をIoT Registryに登録
  4. その鍵ペアを使って、データをクラウドへストリーミングするIoT API呼び出しを許可

ただ、これだけでは理想的とは言えません。実世界のIoTでは、数百万台規模のデバイスがクラウド環境にストリーミングするのが普通だからです。フリート内の各デバイスには固有の認証情報セットを割り当てる必要があり、そうすればデバイスやそのGoogle Cloud認証情報が侵害され不正利用された場合でも、当該の認証情報セットだけを無効化し、他のデバイスに影響を与えずに済みます。

GCPにはこの機能があり、上述のスクリプトのとおり、デバイスごとに固有のトピックに対してのみパブリッシュを許可することも可能です。とはいえ、現状で利用できる認証情報のプロビジョニング手段を踏まえると、数百万台規模で鍵の作成・デプロイ・登録を整然と回すのは大きな課題です。

では、デバイス固有の認証情報の作成プロセスを、どうすれば限りなくシンプルにできるでしょうか。事前に数百万件もの証明書を発行し、製造時に重複なく一台ずつ正しいデバイスへ配置する、といった調整なしに実現できないものか。そして、そのような重複のない確実な配置を、本当にメーカーに委ねたいでしょうか?

あるいは、製造ラインを流れてくるデバイスにソフトウェアと認証情報をブートストラップするタイミングで、メーカー側からIoT Registryへオンデマンドで認証情報作成のAPIを叩く、という構成は避けられないでしょうか?万一、IoT登録を担うAPI Gatewayへの接続が落ちたら、製造ラインはどうなるでしょうか?

こうした方式は複雑でミスを誘発しやすく、メーカーに余計な負担を強いるため、できれば避けたいところです。

GCPに対応してほしい、安全で現実的にスケールするデバイス登録の仕組みは、次のような姿です。

  1. すべてのIoTデバイスに共通で配置する単一のIoT証明書を作成する。「ブートストラップ」証明書と呼ぶこの証明書には、(a) リクエストが承認された場合にのみデバイス固有の認証情報を作成・取得でき、(b) 自身をIoT Registryに追加できる、という権限のみを付与する。
  2. GCP IoT Coreは認証情報作成リクエストを受け付け、承認すると新たな鍵ペアを生成する。付与されるIoT権限は、現在のGoogle Cloudと同様に、そのデバイス固有のトピックへのパブリッシュのみを許可するべきである。
  3. デバイス固有の認証情報の作成許可は、検証ステップによってゲートする。これは利用者自身が記述するCloud Functionで、リクエストに含めた一意の識別子を、ホワイトリスト(例:製造済みのシリアル番号や使用済みのMACアドレス一覧)やブラックリスト(例:侵害・不正利用・登録済みのデバイスに紐付くシリアル番号一覧)と照合する仕組みになる。
  4. 検証ステップで承認された場合、当該デバイスがIoT Registryに登録され、新しい認証情報ファイルがデータストリーミング用にデバイスへ配信される。

このフローであれば、IoTデバイスメーカー側はブートストラップ証明書を各デバイスに載せるだけで済みます。データストリーミング用のデバイス固有鍵ペアは、必要なら製造工場内で即座に作成・取得することも、エンドユーザーの手元に渡ってから生成することもできます。

認証情報をいつ取得するかにかかわらず、メーカーは利用者が用意したソフトウェアをデバイスに組み込むだけでよく、次の条件が揃った時点でブートストラップ証明書ベースのデバイス固有鍵ペア生成プロセスが走ります。

  • デバイスが起動した
  • インターネット接続が利用可能
  • デバイス固有の鍵ペアが見つからない

残念ながら、このような機能は現時点では実装されていません。とはいえ、大量デバイス登録の課題を除けば、Google CloudのIoTサービスは、下流のフルマネージドなメッセージ処理サービス(第2回で解説)と組み合わせることで、強固で安全、かつ容易にスケールするクラウドベースのIoTソリューションになります。