IoT CoreからAMP(Amazon Managed Service for Prometheus)へデータ登録するLambdaを作った話

IoTデータをPrometheusに入れてGrafanaで見れたらおしゃれな気がする。理由はないけど。

しかーし

**IoT CoreのルールアクションにPrumetheusがない（泣）**

2022/4/3現在の指定できるアクションはこちら。

![ap-northeast-1.console.aws.amazon.com_iot_home_region=ap-northeast-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/2cd343f6-a326-b811-93fe-932ab775761d.png)

ないならLambdaで作ろうと思い立ち、作ったのがこちら。

https://github.com/moritalous/iot2aps-lambda

構成はこのようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/85d2428e-16b0-2abf-5010-288ee9bd4a28.png)


完成に至るまでに色々調べた結果をログとして残します。

# PrometheusはPullモデル

Prometheusはサーバー側から定期的にメトリクスを取得しに行くPullモデルが採用されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/b3965f72-e515-7733-68c4-1512901c2ae7.png)

出典：https://prometheus.io/docs/introduction/overview/

FAQには`Why do you pull rather than push?`とあり、内容を翻訳すると、

> HTTPをプルオーバーすると、いくつかの利点があります。
> * 変更を開発するときに、ラップトップで監視を実行できます。
> * ターゲットがダウンしているかどうかをより簡単に見分けることができます。
> * 手動でターゲットに移動し、Webブラウザを使用してターゲットの状態を検査できます。
> 
> 全体として、プルの方がプッシュよりも少し良いと思いますが、監視システムを考える上で重要なポイントと見なすべきではありません。
> プッシュする必要がある場合のために、PushGatewayを提供しています。

PushGatewayというものも提供されていますが、サーバーを構えて中継をする形なので、Lambda単体では実現ができません。

# Prometheusにはremote writeがある

PrometheusはPullしてメトリクスを収集しますが、収集したメトリクスを別のPrometheusに転送するためにremote writeという機能も用意されています。

remote writeの開発の経緯はここに詳しく書かれています。（何故かGrafanaのブログ）
https://grafana.com/blog/2021/05/26/the-future-of-prometheus-remote-write/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/99da20af-cf3a-f60a-3b5c-208c6d080050.png)

出典：https://grafana.com/blog/2021/05/26/the-future-of-prometheus-remote-write/

# AWSのドキュメントのremote writeのAPIについて記載あり

AWSはPrometheusのマネージドサービスをAmazon Managed Service for Prometheus(以降AMP)を提供しています。AMPのドキュメントにremote writeのAPIについての記載がありました。

https://docs.aws.amazon.com/prometheus/latest/userguide/AMP-APIReference.html#AMP-APIReference-Prometheus-Compatible-Apis

このAPIを呼び出せば良いということになります。ただ、残念ながらPrometheus互換APIについてはBoto3では実装されていません。

さらに、、

> Note
> For the request body syntax, see to the protocol buffer definition at https://github.com/prometheus/prometheus/blob/1c624c58ca934f618be737b4995e22051f5724c1/prompb/remote.pb.go#L64.

**なんだよ、protocol bufferって、、今までのAWSのAPIは全部JSON送るだけだったじゃないか。。Go言語もわからんよ。。**


# 初めてのprotocol buffer

protocol bufferがなにものかもわからない状態で色々調べた結果、以下の様なことがわかり、なんとか実装にたどり着きました。

* protocol bufferにはスキーマ定義があり、Prometheusのスキーマ定義が先程のリンク先にあるようだ
* スキーマ定義をコンパイルして使用するようだ
* スキーマ定義はGo言語で書かれているが、他の言語向けにもコンパイルできるようだ
* ネット上にほとんど情報がないが、GitHubに1件だけ情報発見！

```shell:Prometheusのスキーマ定義をPython向けのコンパイルする方法
git clone --depth 1 https://github.com/prometheus/prometheus.git
cd prometheus/prompb
curl -L --create-dirs https://raw.githubusercontent.com/gogo/protobuf/master/gogoproto/gogo.proto -o gogoproto/gogo.proto
protoc --python_out=/workspace ./*.proto gogoproto/*.proto
```

出来上がったスキーマを使用するコードはこちら。（robskillingtonさん、ありがとうございます。）

https://gist.github.com/robskillington/fb82ee5c737b79a3bc891df3dce7a9aa

# SigV4認証も必要

Boto3が使えないため、AWSのSigV4認証も自前で行う必要があります。（自前と言ってもライブラリーのお世話になりますが）

requests-aws4authとbotocoreを使って以下の実装で無事、AMPにremote wrieteすることができました。

```python:botocoreで取得したクレデンシャル情報を使用しSigV4署名して、リクエスト送信
    auth = requests_aws4auth.AWS4Auth(
                refreshable_credentials=botocore.session.Session().get_credentials(),
                region=region, service=service)

    response = requests.post(endpoint, headers=headers, auth=auth, data=body)
```



長い戦いでした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/e0cab4a0-d1ad-3ccb-a3c5-9562dd4e4bd7.png)

よかったら使ってください。

https://github.com/moritalous/iot2aps-lambda


# ちなみに

Prometheusにこだわらなければ、IoT Core -> CloudWatch Metrics -> Grafana のルートはLambdaを作らなくてもできると思います。