AWS IoTの利用状況を可視化するCloudwatchカスタムメトリクスを作る

# AWS IoTのカスタムメトリクス

AWS IoTのコンソール画面が変更され、dashボードが表示されるようになりました。が、thing単位での通信状況はわからないので、rule engineからLambdaへ送り、通信状況をcloudwatchのカスタムメトリクスで表示できるようにしてみました。
マルチテナント型で使う、や、想定外にメッセージが多いときにどのthingがどの位使っているかがわかれば解析が楽になるかなと思います。

現状のdashbordで見れる情報は、アカウント全体の利用状況で以下のように見えます。
![スクリーンショット 2017-01-09 9.02.10.png](https://qiita-image-store.s3.amazonaws.com/0/100893/67c21fb0-6297-6390-2a5c-a8fc627bb93c.png)

では、カスタムメトリクス対応を作ってみましょう。

# AWS操作

## IAM
Lambdaから、cloudwatch からput_metric_dataを使うので、以下２つの権限を持った role を作成してください
AWSLambdaBasicExecutionRole
CloudWatchFullAccess

## AWS Lambda
イベントトリガはAWS IoTから作成するので関数だけ作っておいてください。
Lambda から "create a lambda function" -> "configure function"

Name:任意 (ここでは IoT-payload-cal)
Description：任意
Runtime: Python2.7
として以下のコードを利用してください。

前提として dataは jsonで thingNameとしてthingの情報が上がってくるものとします。
例えば以下のようなjsonを想定しています。

```json:example_event.json
{
    "thingName": "xxxx",
    <任意>
}
```

Lambdaの説明
[AWS IoTの課金](https://aws.amazon.com/jp/iot/pricing/)は、512byteを1-messageとしているのでmessage部分を 512byteで割って小数点を繰り上げしています。これで大まかには詳細利用状況が可視化できるかと思います。floatでcastしているのは、intのままだと小数点が切り捨てられるためです。

コードにはつけてないですが、通信量を見たい場合は、取得したbyte数を同様にカスタムメトリクスにputしてもらえれば、byte数で見ることができると思います。こちらは例えばSIMなどを使ってSIMのコストの概算をみるのによいかと思います。

```py
import json
import math
import datetime
import boto3

CLW = boto3.client('cloudwatch')

def put_cloudwatch(thingName, value):
    try:
        now = datetime.datetime.now()
        CLW.put_metric_data(
            Namespace  = "IoT_count",
            MetricData = [{
                "MetricName" : thingName,
                "Timestamp" : now,
                "Value" : value,
                "Unit" : "Count"
            }]
        )
    except Exception as e:
        print e.message
        raise


def lambda_handler(event, context):

    thingName = event['thingName']
    #JSON to String, and count string bytes
    str_event = json.dumps(event)
    byte_len = len(str_event)
    #count message, AWS IoT 1-message = 512byte
    msg_cnt = float(byte_len)/512

    try:
        put_cloudwatch(thingName, math.ceil(msg_cnt))
    except Exception as e:
        raise

    return 
```

roleはここで作った role を選択してください。

## AWS IoT

AWS IoTから Rules => create

Name:任意 (ここでは、aws_iot_cal)
Description:任意
Using SQL version:beta

すべてのメッセージを通すので、
Attribute:*
Topic filter:#
とします。

Add Actionから、Invoke a Lambda function passing the message data
を選択configure actionから、先ほど作成したLambdaを指定します。

# テスト
Lambdaからtest用jsonを作成して実行、もしくはAWS IoTへデータを送ります。
Lambdaのプログラムでも説明したましたが、jsonの中に **thingName** の情報を必ず入れてください。
lambdaのみで実行確認するには、例えば以下のようなjsonで確認出来ます。

```json:test.json
{
  "thingName": "testThing-1",
  "key3": "value3",
  "key2": "value2",
  "key1": "value1"
}
```

testThing-1でメトリクスが作成されます。時間単位の積算データはcloudwatchのsumで見てください。
![スクリーンショット 2017-01-09 9.40.26.png](https://qiita-image-store.s3.amazonaws.com/0/100893/aa62d660-d59d-99ca-d35b-1ad3fa4c3e4a.png)

1つのNamespaceに複数のthingからputすると以下のようになります。
![スクリーンショット 2017-01-09 9.53.16.png](https://qiita-image-store.s3.amazonaws.com/0/100893/f15fd13b-fc37-6a9f-f4bf-6781f6072358.png)

# 最後に
マルチテナント型や工場/設置場所別で分けたい場合はAWS IoTでtopicとrule enginによる打ち分けで、lambdaを分けて、namespaceを分けるなどすると、良いかもしれません。
大規模なthing登録の場合、全イベントでlambdaが発火されるので、lambdaの同時起動数などは注意が必要です。
lambdaの同時起動数が気になる場合にはlambdaの前段にkinesisを挟むつまり、AWS IoT -> kinesis streams -> Lambda とするとよいかと思います。
同じくcloudwatchの[limit](http://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/cloudwatch_limits.html)も意識しておいたほうが良いでしょう。
また、カスタムメトリクスもメトリクス単位で少額ですが、[課金](https://aws.amazon.com/jp/cloudwatch/pricing/)が発生します。

# 免責
本投稿は、個人の意見で、所属する企業や団体は関係ありません。
また掲載しているsampleプログラムの動作に関しても保障いたしませんので、参考程度にしてください。