FirehoseのHTTP配信機能でMackerelにメトリックを投稿する

先日、Amazon Kinesis Data Firehose が任意の HTTP エンドポイントに対しての配信機能をサポートしました。

aws.amazon.com

従来の S3 / Redshift / Elasticsearch Service などのマネージドなリソースに対してデータを配信する機能に加えて、自分で作った HTTP(s) のエンドポイントに対しても Firehose からデータを投げ込んでくれる機能です。

Amazon Kinesis Data Firehose now supports data delivery to Datadog

既に Datadog や New Relic へもマネージドでデータを配信できていて、それらに送りたいデータはとりあえず Firehose に流しておけば、実際の送信やバッファリングやリトライは Firehose が面倒を見てくれるという嬉しい状態ですね。

で、ここで思ったのは「Mackerelにも対応したい」

…ということで作ってみました。

github.com

Firehose に Mackerel のサービスメトリックを流して、この HTTP endpoint を設定すると Mackerel API に送信してくれるものです。

fujiwara/ridge を使うことで、ひとつのバイナリで単体での Go の http server としても動きますし、Lambda (API Gateway / ALB) としても動くようになっています。

動かしてみる

普通にビルドしてどこかのサーバーで動かしてもよいのですが、Firehose の配信先は https が必須で多少面倒かもしれません。ここではとりあえず Amazon API Gateway で動かすことにします。Go が必要です。

$ GOARCH=amd64 GOOS=linux make
$ ROLE_ARN=arn:aws:iam::123456789012:role/lambda make deploy

Linux (Lambda) 用のバイナリをビルドして、適当な IAM Role (権限は AWSLambdaBasicExecution のみで十分です) を指定して fujiwara/lambroll でデプロイできる Makefile がサンプルとして入っています。function の定義は以下で、Role 以外に必要な設定は特にありません。

{
  "Description": "A PoC of Firehose HTTP endpoint for Mackerel",
  "FunctionName": "firehose-http-endpoint-for-mackerel",
  "Handler": "firehose-http-endpoint-for-mackerel",
  "MemorySize": 128,
  "Role": "{{ must_env `ROLE_ARN` }}",
  "Runtime": "go1.x",
  "Timeout": 30,
  "TracingConfig": {
    "Mode": "PassThrough"
  }
}

デプロイした Lambda を使う API Gateway の HTTP Integration を設定します。Route は $default をこの関数に向けるだけです。

f:id:sfujiwara:20200803104848p:plain
API Gateway HTTP integration

Firehose の Destination 設定は以下の通りにしてください。

  • HTTP endpoint URL: 作成した API Gateway の URL。path は /service (https://xxxx/service
  • Content encoding: Disabled
  • Access key: Mackerel の API Key
  • Parameters:
    • service: メトリックを投稿する Mackerel のサービス名
  • Buffer interval: 何秒でも動作はしますがラグが少ない方がよいので 60

Firehose に投稿したいサービスメトリックを送ります。

$ aws firehose put-record --delivery-stream-name mackerel-endpoint \
    --record "Data=Base64にしたサービスメトリックの値"

値の形式は JSON {"name":"metric.name","time":1596382129,"value":27759} とテキスト形式(mackrel plugin が出力するタブ区切りのもの) metric.name 27759 1596382129 に対応しています。

あとは Firehose が60秒ごとにまとめて API Gateway を叩いて来るタイミングで、Mackerel API へ送信されるはずです。

うれしいこと

直接 Mackerel API を叩いたり mkr コマンドなどで送信するのと比べて、Firehose を介することで嬉しいことはなにかというと…

  • Mackerel のメンテナンス時、送信失敗時のリトライ、バッファリングを Firehose に任せられる
    • 最大 7200 秒までリトライ可能
    • 失敗したデータは S3 にバックアップも可能
  • Mackerel API key を送信元に配る必要がない
    • Firehose 側に設定されていれば OK
    • 送信元には Firehose に対してのアクセス権が与えられていればよい
  • mkr / mackerel-agent などのツールがなくても aws-cliSDK のみで送信できる

ぐらいでしょうか。mackerel-agent を EC2 で動かしていれば再送やバッファリングは自動で行われますが、直接 API を叩いて値を送信する場合は失敗時のケアを自分でやらないとメトリックが欠落してしまいます。特に Lambda などから送信する場合は状態を維持するのが面倒なので、欠落の心配が減るのがいいですね。

ご要望

Firehose http endpoint を使って Mackerel へメトリックを送るコードを書いてみましたが、これはあくまで PoC ということで、Mackerel の公式で http endpoint がサポートされてほしいなと思います。そうすると Firehose の設定で公式 endpoint に向けるだけで済みますし、API を直接アクセスする以外のメトリック送信手段として AWS のユーザーには嬉しいんじゃないでしょうか。ご検討いただければ幸いです。