Edited at

AWS IoTとRuby製MQTTクライアントでPub/Subしてみた

More than 3 years have passed since last update.


この文書について

先日ベータ版が公開されたAWS IoT(AWS上でサーバーインスタンスを自分で用意する事なくMQTTブローカー(+α)のサービスを利用できる)をRubyクライアント(ruby-mqtt)から試してみた個人的なメモです。

本家のクイックスタートを見つつ、AWS IoT Message BrokerのMQTTでpub/subをやってみたも参考にさせて頂きつつやってた覚書ですが、現時点(2015/10/14)でAWS IoTはベータ版であり、今後サービスの仕様が変わる可能性があるため、できるだけ本家のドキュメントを参照される事をおすすめします。

AWS IoTではクライアント側のSDKも提供されているようですが、今回はオープンソースで公開されているMQTTクライアントを使ってPub/Subを試してみます。

AWS IoTの概念についてはLearn how AWS IoT works with this interactive tutorialで図解入りでわかりやすく説明されているので、こちらを参照。以下は同ページのスクリーンショット。RGBのスイッチの状態がMQTTブローカーを通して別のデバイスやストレージ、AWS上の別のサービスと連携する様子が示されています。


AWS CLIの準備

すでに最新のAWS CLIをインストール済みの場合は、この項目は飛ばしてかまいません。

$ sudo pip install awscli --upgrade

なお、AWS CLIの詳細なインストール方法に関してはGetting Set Up with the AWS Command Line Interfaceを参照のこと。


Thingを登録する

MQTTクライアントからMQTTブローカーに接続するには Thing を登録する必要があるとのこと。以下のようにして sample_things という名前で登録しました。

$ aws iot create-thing --thing-name "sample_things"

現在登録されているThingのリストは以下のコマンドで確認できます。

$ $ aws iot list-things

{
"things": [
{
"attributes": {},
"thingName": "sample_things"
},
{
"attributes": {},
"thingName": "my_things"
}
]
}

先ほど登録した sample_things の他に以前私が登録した my_things も表示されています。


AWS IoTとThing間でのセキュアな通信のための準備


証明書の用意

先ほど登録した ThingAWS IoT 間のセキュアな通信のために、X.509証明書を作成します。

以下のコマンドで、証明書とキーペアを cert.json というファイルに書き出します。

$ aws iot create-keys-and-certificate --set-as-active > cert.json

cert.json の中身はJSONフォーマットですので、ファイルから秘密鍵と公開鍵をそれぞれ thing-public-key.pemprivate-key.pem というファイルに書き出します。

(なお、ローカルに jq コマンドがインストールされていない場合は $ brew install jq すること)

$ cat cert.json | jq .keyPair.PublicKey -r > thing-public-key.pem

$ cat cert.json | jq .keyPair.PrivateKey -r > private-key.pem

証明書を cert.pem という名前のファイルとして書き出します。

$ aws iot​ describe-certificate --certificate-id "<here-is-your-certificate-id>" --output text --query certificateDescription.certificatePem > cert.pem

'<here-is-your-certificate-id>' の部分に先ほどの cert.json の最終行付近にある "certificateId" として与えられた文字列を入れて実行します。


AWS IoTポリシーを作成して証明書に紐付ける

以下のような内容で policy.json というファイルを新規に作成します。


policy.json

{

"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action":["iot:*"],
"Resource": ["*"]
}]
}

作成したファイルへのパスをコマンド引数に渡して PubSubToAnyTopic という名前のポリシーを作成します。

$ aws iot create-policy --policy-name "PubSubToAnyTopic" --policy-document file://policy.json

作成した証明書とポリシーを紐付けます。

$ aws iot attach-principal-policy --principal "<certificate-arn>" --policy-name "PubSubToAnyTopic"

'<certificate-arn>' のところには、先ほどの cert.json 中で "certificateArn" として渡された文字列( "arn:aws:iot:ap-northeast-1::..." ) を入れます。


証明書をThingに紐付ける

さて、最後に証明書とThingを紐付けます。

$ aws iot attach-thing-principal --thing-name "<thing-name>" --principal "<certificate-arn>"

<thing-name> のところには最初に登録したThingの名前が入ります。今回で言えば sample_things になりますね。

<certificate-arn> のところには、先ほどと同じく cert.json の中で、"certificateArn" として与えられた文字列( "arn:aws:iot:ap-northeast-1::..." )が入ります。

お疲れ様でした、これでAWS IoTを使ってPub/Subする準備が整いました。


Ruby ClientでPub/Subしてみる


MQTT クライアントライブラリのインストール

AWSのクイックスタートではMosquittoのPub/Subクライアントを使ってテストしていますが、私はRubyプログラマなのでRubyを使ってやってみます。

MQTTのRubyクライアントとしてはgithub.com/njh/ruby-mqttがあります。

インストールはgemで

$ gem install mqtt


ルート証明書のダウンロード

また、ルート証明書を rootCA.pem というファイル名でカレントディレクトリに落としておきます。

$ wget https://www.symantec.com/content/en/us/enterprise/verisign/roots/VeriSign-Class%203-Public-Primary-Certification-Authority-G5.pem -O rootCA.pem


MQTTブローカーエンドポイントの確認

それと、MQTTブローカーのエンドポイントはAWSのアカウントごとに用意されています。自分のアカウントのエンドポイントを確認するには、以下のようにします。

$ aws iot describe-endpoint

{
"endpointAddress": "AAAAAAAAAAA.iot.ap-northeast-1.amazonaws.com"
}

この場合は AAAAAAAAAAA.iot.ap-northeast-1.amazonaws.com が自分のアカウントのエンドポイントということになります。

ポートは共通で 8883 です。


Ruby製Subscriberの用意

データを待機するSubscrberは以下のように書きました。


sub.rb

require "mqtt"

MQTT::Client.connect(host: "AAAAAAAAAAA.iot.ap-northeast-1.amazonaws.com",
port: 8883,
ssl: true,
cert_file: "cert.pem",
key_file: "thing-private-key.pem",
ca_file: "rootCA.pem") do |client|

client.subscribe("topic/test")
topic,message = client.get #ここでブロックする
p [topic, message]
end



Ruby製Publisherの用意

データを送信するPublisherは以下のように


pub.rb

require "mqtt"

MQTT::Client.connect(host: "AAAAAAAAAAA.iot.ap-northeast-1.amazonaws.com",
port: 8883,
ssl: true,
cert_file: "cert.pem",
key_file: "thing-private-key.pem",
ca_file: "rootCA.pem") do |client|

client.publish("topic/test", "From Ruby!")
end



実際にPub/Subしてみる

まずは sub.rb を実行。

$ ruby sub.rb

この時点でSubscriberがデータを待機してブロックします。


続いて pub.rb を実行。

$ ruby pub.rb 

pub.rb はデータをパブリッシュしたらすぐに終了します。直後にブロックしていた sub.rb 側で以下のように受信したデータを表示します。

$ ruby sub.rb

["topic/test", "From Ruby!"]


料金

AWS IoTの課金体系については、この文書を書いている段階で以下のように公表されています。



  • US East (N. Virginia): $5 per million messages


  • US West (Oregon): $5 per million messages


  • EU (Ireland): $5 per million messages


  • Asia Pacific (Tokyo): $8 per million messages

東京リージョンだけ、ちょっとお高いですね。ただ実装するアプリケーションがリアルタイム性を重視するなら、やはり日本国内では東京リージョンを使うことになるでしょう。


感想など

最初の手順が若干必要ですが、順を追って一つづつやっていけば簡単にMQTTブローカーを利用できるようになります。EC2で自分でサーバーインスタンスを立ち上げる必要もなく、またサーバーインスタンスの保守・運用についても考慮することなくセキュリティを考慮されたMQTTブローカーが使用できるというのは、これからMQTTを使ったアプリケーション開発を始める人にとってはとっても楽チンで、とりあえずMQTTを使って端末間の通信を実現したい場合には第一候補になるサービスと感じました。

ただ、本当にシビアな、多数のクライアントから多数のメッセージが送られてくるような場合は、どこかで自前のサーバーを用意したほうが安くつく分岐点があるのだろうと予想しますが、その分岐点がどこになるかは今の私にはわかりません。

AWS IoTは純粋なMQTTブローカーとしても使用できますが、やはりAWSでMQTTする場合の最大の魅力は、AWSの他のサービスとの連携だと思います。次回以降でそのあたりも色々やってみたいと思います。それと Shadow という機能も気になりますね。こっちも色々やってみます。