goodbyegangsterのブログ

備忘録的な

AWS IoT Coreについて

AWS IoT Coreを触ってみたので、そのまとめです。

下記がAWSのIoTサービスの全体像です。

f:id:goodbyegangster:20200301032838p:plain

Blackeblet - AWS が提供する IoT ソリューションと ITS における活用

IoT Coreで持っている機能の一覧となります。

AWS IoT デバイス SDK: AWS IoT デバイス SDK を使用すれば、ハードウェアデバイスまたはモバイルアプリケーションを簡単かつすばやく AWS IoT Core に接続できます。

バイスゲートウェイ:バイスゲートウェイは IoT デバイスAWS に接続する入口の役割を果たします。デバイスゲートウェイはすべてのアクティブなデバイスの接続を管理し、複数のプロトコルに対するセマンティックスを実装して、デバイスAWS IoT Core と安全で効率的に通信できるようにします。現在、デバイスゲートウェイでは MQTT、WebSocket、HTTP 1.1 プロトコルがサポートされています。

メッセージブローカー: メッセージブローカーは高スループットの pub/sub メッセージブローカーで、ユーザーのすべての IoT デバイスとアプリケーションと低レイテンシーで安全にメッセージを送受信します。

認証と認可: AWS IoT Core では、接続するすべてのポイントでの相互認証と暗号化が提供されており、デバイスAWS IoT Core 間では身元が証明されたデータのみが交換されます。

レジストリ: レジストリによって、デバイスの ID が確定され、デバイスの属性や機能といったメタデータが追跡されます。

バイスシャドウ: AWS IoT Core では、それぞれのデバイスについて「シャドウ」、つまり永続的な仮想バージョンを作成できます。デバイスシャドウにはデバイスの最新の状態が保存されるため、アプリケーションや他のデバイスからのメッセージの読み出し、およびデバイスとの通信が実行できます。デバイスシャドウには、デバイスがオフライン状態のときでも、各デバイスについて最後に報告された状態と、希望する今後の状態が保持されます。

AWS IoT Core の特徴

基本的には、MQTTを扱うためのPub/Subブローカーと、その仲間たちって感じです。

今回は、MQTTにてIoT Core向けにPublishして、Subscribeするまでの手順を確認してみます。

環境情報

認証・認可設定

認証

AWS IoTでは、MQTTを受け取るエンドポイントが、AWSアカウント単位で1つ勝手に作成されます。そのエンドポイントの認証にて、以下の方法がサポートされています。

  • X.509証明書
  • IAM User
  • Cognito
  • IDフェデレーション
  • カスタム(Lambdaを利用して独自認証の作成)

IoTデバイスを対象とする場合、通常時であれば、 X.509証明書 を利用することになります。X.509証明書とは、公開鍵証明書のことらしいです。

暗号において、X.509とは、ITU-Tの公開鍵基盤 (PKI)の規格である。X.509は、公開鍵証明書の標準形式や証明書パス検証アルゴリズム(英語版)などを定めている。

Wikipedia - X.509

自前の証明書をAWSにインポートすることもできるようですが、今回はIoT Coreの機能にて新規証明書を作成します。

以下のコマンドで、証明書一式を作成できます。

> aws iot create-keys-and-certificate \
--set-as-active \
--certificate-pem-outfile /tmp/certificate.pem.crt \
--public-key-outfile /tmp/public.pem.key \
--private-key-outfile /tmp/private.pem.key

AWS CLI - create-keys-and-certificate

作成された証明書は、Publish/SubScribeする時のConnection処理にて利用されます。

認可

認可(というかポリシー付与)の方法は、利用する認証方法により異なってきます。

プロトコル ポート SDK 認証タイプ 認可タイプ
MQTT over mutual authentication 8883
443
AWS IoTDevice SDK X.509証明書 AWS IoTポリシー
MQTT over WebSocket 443 AWS Mobile SDK Cognito
IAM User
IDフェデレーション
AWS IoT IDのCognitoポリシー
他のIDのIAM ポリシー
HTTP over server authentication 443 AWS CLI Cognito
IAM User
IDフェデレーション
AWS IoT IDのCognitoポリシー
他のIDのIAM ポリシー
HTTP over mutual authentication 8443 未サポート X.509証明書 AWS IoTポリシー

AWS IoT のデータプレーン API とポリシータイプ

今回のMQTTプロトコルにて、X.509証明書を利用する方法では、AWS IoTポリシーがサポートされています。

IoTポリシーも、IAMポリシーのようにJSONベースでポリシーを定義することになります。AWS IoTコンソールより、「Secure」-「Policies」画面の、「Create」ボタンから作成できます。

以下が今回設定したIoTポリシー。ConnectできるClientIDを限定、Publishできるtopicを限定させています。topicについては後述。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "iot:Connect"
      ],
      "Resource": [
        "arn:aws:iot:ap-northeast-1:1234567890123:client/PC-0001"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "iot:Publish"
      ],
      "Resource": [
        "arn:aws:iot:ap-northeast-1:1234567890123:topic/Ubuntu/CpuUtil"
      ]
    }
  ]
}

IoTポリシーの書き方については、下記の公式ドキュメントを参考。

AWS IoT ポリシー

同様処理は下記コマンドからも実行できます。が、今回はポリシー定義をCLI内で書くのがあまりに面倒で、断念しました。。。

AWS CLI - create-policy-version


作成したポリシーを、その前に作成しているcertificate(証明書たち)にアタッチします。コマンドは下記。

aws iot attach-policy \
--target "arn:aws:iot:ap-northeast-1:1234567890123:cert/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
--policy-name "sample-policy"

AWS CLI - attach-policy

Publish

Ubuntu上のCPU使用率を、AWS IoTに送信してみます。

topic

送信してみる前に、 topic という概念について。AWS IoTでは、AWSアカウント単位でエンドポイントを1つ用意してくれます。送信するデータを振り分けるために、 topic という概念が出てきます。

AWSドキュメント - トピック

各データ毎にキューみたいなイメージでしょうか。

QoS

利用できるQoSについても。AWS IoTのMQTTプロトコルで用意されているQoSは「0」と「1」になります。

  • QoS 0
    • At Most One
    • 1回だけ送信
  • QoS 1
    • At Least One
    • 少なくとも、1回送信できまで再送

細かい解説は、公式のドキュメントにて。

AWSドキュメント - MQTT

Publishする際に指定することになります。

AWSIoTPythonSDK

で、実際にPublish処理をするスクリプトを作成します。今回、スクリプトPythonで記述しており、 AWS IoT Device SDK for Python というSDKを利用しています。スクリプトの書き方は、GitHub上にあるサンプルや、リファレンスを参考に。

GitHub - AWS IoT Device SDK for Python

AWSIoTPythonSDK - Reference

とりあえず、UbuntuSDKをインストールします。

$ sud0 apt-get update
$ sudo apt-get upgrade
$ sudo apt install python3-pip
$ sudo pip3 install AWSIoTPythonSDK

CPU使用率を取得するにpsutilを利用するので、ライブラリをインポートしておきます。

$ sudo pip3 install psutil

コネクション時にルートCA証明書が必要となるため、AWS提供のものをダウンロードしておきます。

$ wget https://www.amazontrust.com/repository/AmazonRootCA1.pem

Amazon Trust Services Endpoints

実行するPythonスクリプトです。

from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient
from datetime import datetime
import os
import json
import psutil
import time

CLIENT_ID = "PC-0001"
IOT_ENDPOINT_URL = "xxxxxxxxxxxxxxxxxxx.iot.ap-northeast-1.amazonaws.com"
IOT_ENDPOINT_PORT = 8883

PATH = os.getcwd()
ROOT_CA_PATH = PATH + "/credentials/root.pem"
PRIVATE_KEY_PATH =  PATH + "/credentials/private.pem.key"
CERTIFICATE_PATH =  PATH + "/credentials/certificate.pem.crt"

KEEP_ALIVE_TIME = 60

TOPIC = "Ubuntu/CpuUtil"

myMQTTClient = AWSIoTMQTTClient(CLIENT_ID)
myMQTTClient.configureEndpoint(IOT_ENDPOINT_URL, IOT_ENDPOINT_PORT)
myMQTTClient.configureCredentials(ROOT_CA_PATH, PRIVATE_KEY_PATH, CERTIFICATE_PATH)

myMQTTClient.configureOfflinePublishQueueing(-1)
myMQTTClient.configureDrainingFrequency(2)
myMQTTClient.configureConnectDisconnectTimeout(10)
myMQTTClient.configureMQTTOperationTimeout(5)

def GetTimeStamp():
  return datetime.now().strftime("%Y/%m/%d %H:%M:%S")

def GetCpuUtil():
  return psutil.cpu_percent(interval=1)

if __name__ == '__main__':
  myMQTTClient.connect(KEEP_ALIVE_TIME)

  while True:
    message = {}
    message['time'] = GetTimeStamp()
    message['cpu_util'] = GetCpuUtil()
    payload = json.dumps(message)

    myMQTTClient.publish(TOPIC, payload, 1)
    time.sleep(5)

エンドポイント名をちゃんと記載し、これまでに用意した各種証明書を配置したら、スクリプトを実行します。AWS IoTにデータが送信され始めます。

Subscribe

テスト用にAWSコンソール上でSubscribeできる機能を用意してくれています。Publish処理と同じくSDKで処理を記載することもできますが、疎通テストするだけであれば、こいつがすごい便利です。

AWSコンソール上の「Test」ページから、Subscribeしたいtopicを入力してSubscribeの実行。

f:id:goodbyegangster:20190805145105p:plain

正しくPublishされていれば、メッセージが表示されてきます。

f:id:goodbyegangster:20190805145111p:plain

Kinesisと違い、IoT Coreではメッセージをバッファしてくれる機能はないので、SubScribeできなかったメッセージは闇に消えることになります。

トラブルシューティング

設定時のトラシューで役立った情報を記載しておきます。

ログ

CloudwatchLogsにログ出力設定を実施できます。AWS IoTコンソールの、 setting ページより設定できます。LogGroup AWSIotLogs という名前でログが作成されるので、それを確認。

タイムアウト

エンドポイントとのコネクションを貼った際、タイムアウトのエラーとなる場合。

>>> myMQTTClient.connect()
Connect timed out
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.6/dist-packages/AWSIoTPythonSDK/MQTTLib.py", line 513, in connect
    return self._mqtt_core.connect(keepAliveIntervalSecond)
  File "/usr/local/lib/python3.6/dist-packages/AWSIoTPythonSDK/core/protocol/mqtt_core.py", line 199, in connect
    raise connectTimeoutException()
AWSIoTPythonSDK.exception.AWSIoTExceptions.connectTimeoutException
  • 考えられる原因
    • CertificateとIoT Policyが紐付いていない
    • IoT Policyの権限に不足がある

証明書の確認

IoTエンドポイントへの接続テストを実施できます。以下が公式の手順です。

接続関連の問題の診断

実行コマンドはこんな感じです。

$ openssl s_client \
-connect xxxxxxxxxxxxx.iot.ap-northeast-1.amazonaws.com:8443 \
-CAfile ./root.pem \
-cert ./certificate.pem.crt \
-key ./private.pem.key