AWS IoT + CloudWatchでさらっと可視化

記事タイトルとURLをコピーする

こんにちは。技術3課の森です。 東京での生活をしている最中、乗り換える必要のない電車に乗ってたはずなのに、人がいっぱい降りるから一緒に降りてしまい、 目的地をあっさりと通過してしまった経験を本日しました。

AWS IoTとCloudWatchで可視化

以前、nakamuraが書いたブログに似たことをやってみました。 ただやるだけではなく、できるだけManagementConsoleを使わずにやってみたかったので、いくつかプログラミングを交えてやってみました。

実行環境の準備

まずは環境の準備から始めます。以下の3つをインストールしてください。

  • AWS CLI
  • AWS SDK for Python
  • mosquitto (MQTT Broker)
  • jq

実行環境の作成

実行環境の構成

まずは構成です。

今回デバイスは自分のPCを使い、mosquittoでPublishするようにします。

環境作成スクリプト

まずはAWS IoTのPolicyとThingsを作成します。 ここは簡単に作りたかったので、 AWS CLI + Bash を使って作成しています。

#!/bin/sh
#ポリシー名を入力してもらいます
echo -n "INPUT PolicyName>>"
read POLICY_NAME
#AWS IoTのThingの名前を入力してもらいます
echo -n "INPUT NEW ThingName>>"
read THING_NAME

#AWS IoTのThingを作成します
aws iot create-thing --thing-name ${THING_NAME}

#Thingに紐づける鍵を作成します。
aws iot create-keys-and-certificate --set-as-active > cert.json
cat cert.json | jq .keyPair.PublicKey -r > public-key.pem
cat cert.json | jq .keyPair.PrivateKey -r > private-key.pem

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

CERT_ARN=`cat cert.json | jq .certificateArn -r`
CERT_ID=`cat cert.json | jq .certificateId -r`
CURRENT_DIR=`dirname $0`

aws iot describe-certificate --certificate-id ${CERT_ID} --output text --query certificateDescription.certificatePem > cert.pem

cat <<EOF > policy.json
{
    "Version": "2012-10-17", 
    "Statement": [{
        "Effect": "Allow",
        "Action":["iot:*"],
        "Resource": ["*"]
    }]
}
EOF

#AWS IoTのポリシーを作成とThingと鍵の紐付けを行います
aws iot create-policy --policy-name ${POLICY_NAME} --policy-document file://${CURRENT_DIR}/policy.json

aws iot attach-principal-policy --principal ${CERT_ARN} --policy-name ${POLICY_NAME}

aws iot attach-thing-principal --thing-name ${THING_NAME} --principal ${CERT_ARN}

データ

PublishするJSONデータを作成します。 単純なものであれば、手で作成する方が早いですが、今回はより多く送ってみようということで、 初心者PythonistがJSONで300項目を用意するプログラムを作成してみました。

ここだけは事前にCloudWatchのPutMetricDataを許可したRoleを作成しておいてください。

# -*- coding: utf-8 -*-
import boto3

client = boto3.client('iot')

vRoleArn = 'arn:aws:iam::123456789012:role/service-role/Role_IoT-CloudWatch'
vMetricNamespace = 'SampleNameSpace'

#30個のルールを作成するためのループ
for i in range(1, 31):
    vRuleName='iot20_cloud01_' + str(i)
    vTopicName="'myTopic/data300'"
    vDescription='description-' + '{0:03d}'.format(i)
    vDataDataName=[]
    vMetricValue=[]
    #10個のActionを作成するためのループ
    for j in range(1, 11):
        vDataDataName.append('${devicePlace}/Temp' + '{0:02d}'.format(i) + '{0:02d}'.format(j))
        vMetricValue.append('${Temp' + '{0:02d}'.format(i) + '{0:02d}'.format(j) + '}')
    #AWS IoTのRuleを作成します
    #10個のActionはベタ書きしています
    #パラメータはDocumentを参照してください
    #http://boto3.readthedocs.io/en/latest/reference/services/iot.html#IoT.Client.create_topic_rule
    response = client.create_topic_rule(
        ruleName=vRuleName,
        topicRulePayload={
            'sql':"SELECT * from " + vTopicName,
            'description': vDescription,
            'actions': [
                {
                    'cloudwatchMetric': {
                        'roleArn': vRoleArn,
                        'metricNamespace': vMetricNamespace,
                        'metricName': vDataDataName[0],
                        'metricValue': vMetricValue[0],
                        'metricUnit': 'None'
                    }
                },
                {
                    'cloudwatchMetric': {
                        'roleArn': vRoleArn,
                        'metricNamespace': vMetricNamespace,
                        'metricName': vDataDataName[1],
                        'metricValue': vMetricValue[1],
                        'metricUnit': 'None'
                    }
                },
                {
                    'cloudwatchMetric': {
                        'roleArn': vRoleArn,
                        'metricNamespace': vMetricNamespace,
                        'metricName': vDataDataName[2],
                        'metricValue': vMetricValue[2],
                        'metricUnit': 'None'
                    }
                },
                {
                    'cloudwatchMetric': {
                        'roleArn': vRoleArn,
                        'metricNamespace': vMetricNamespace,
                        'metricName': vDataDataName[3],
                        'metricValue': vMetricValue[3],
                        'metricUnit': 'None'
                    }
                },
                {
                    'cloudwatchMetric': {
                        'roleArn': vRoleArn,
                        'metricNamespace': vMetricNamespace,
                        'metricName': vDataDataName[4],
                        'metricValue': vMetricValue[4],
                        'metricUnit': 'None'
                    }
                },
                {
                    'cloudwatchMetric': {
                        'roleArn': vRoleArn,
                        'metricNamespace': vMetricNamespace,
                        'metricName': vDataDataName[5],
                        'metricValue': vMetricValue[5],
                        'metricUnit': 'None'
                    }
                },
                {
                    'cloudwatchMetric': {
                        'roleArn': vRoleArn,
                        'metricNamespace': vMetricNamespace,
                        'metricName': vDataDataName[6],
                        'metricValue': vMetricValue[6],
                        'metricUnit': 'None'
                    }
                },
                {
                    'cloudwatchMetric': {
                        'roleArn': vRoleArn,
                        'metricNamespace': vMetricNamespace,
                        'metricName': vDataDataName[7],
                        'metricValue': vMetricValue[7],
                        'metricUnit': 'None'
                    }
                },
                {
                    'cloudwatchMetric': {
                        'roleArn': vRoleArn,
                        'metricNamespace': vMetricNamespace,
                        'metricName': vDataDataName[8],
                        'metricValue': vMetricValue[8],
                        'metricUnit': 'None'
                    }
                },
                {
                    'cloudwatchMetric': {
                        'roleArn': vRoleArn,
                        'metricNamespace': vMetricNamespace,
                        'metricName': vDataDataName[9],
                        'metricValue': vMetricValue[9],
                        'metricUnit': 'None'
                    }
                },

            ],
            'ruleDisabled': False,
            'awsIotSqlVersion': '2016-03-23'
        }
    )

データをPublish

ここでやっとPublishします。何度でも実行できるようにシェルスクリプトで実装しました。

実装したシェルスクリプトを実行します。

#!/bin/sh

CURRENT_DIR=`dirname $0`
#AWS IoTのEndpointを取得します
ENDPOINT=`aws iot describe-endpoint | jq .endpointAddress`

JSON_FILE="awsiot-cloudwatch.json"
#mosquittoを使ってAWS IoTにPublishします
mosquitto_pub --cafile rootCA.pem --cert cert.pem  --key private-key.pem -h ${ENDPOINT} -p 8883 -q 1 -d -t myTopic/data300 -f ${CURRENT_DIR}/${JSON_FILE}

Publishしたデータを可視化

先ほど実行したシェルスクリプト。結果はCloudWatchのメトリクスから確認することができます。

今回は少し異なるデータも用意し、Publishしたため、下の図のようにデータ出力結果が斜めの線になっています。

まとめ

今回はシェルスクリプトやPythonを使って何度でも簡単に実行できるようにしたことと OSSでも実現はできるものの安価で簡単に可視化できるCloudWatchを使いました。

ただ、これが正解ではなく、適材適所、そのシチュエーションを考えて、 どういったAWSサービスを使って、DataSourceや可視化するツールを選択する必要があります。

「こんな感じでするともっとかっこいい」とかあれば、教えていただけると次のネタにさせてもらいます。