ErlangでDatadogにメトリクスを送信するためのモジュール dogstatsd_sender を作りました

Dec 25, 2017 ( Dec 26, 2017 更新 )

この記事はRecruit Engineers Advent Calendar 2017 の21日目の記事です。大変申し訳ありません!!!!

Datadogにメトリクスを送信するためのモジュールを作りました

Erlang・Datadogの組み合わせでモニタリングする場合はvmstatsが便利です。

vmstatsだと、Erlang VM全体のメトリクスは取得できるのですが、ここからさらにボトルネックの調査をするとなると、自分のアプリケーションの特定のプロセスのメッセージキューの溜まり具合も取りたいなあと思うようになりました。

DatadogにはErlang用のクライアントライブラリはなかったのですが、そんなに作るのも大変ではなさそうだったので、簡単なモジュールを作ってみました。

mookjp/dogstatsd_sender

中身は、簡単なdogstatsd(Datadogにメトリクスを送信するために使うデーモン)のクライアントと、あらかじめ軽量プロセスIDを登録しておいて定期的に決まったメトリクスを送信するモジュールのセットになっています。

dogstatsd_sender:register(Pid, #{
  metrics_list => [message_queue_len],
  common_tag_list => [ pid, mfa, node ],
  tags => [{label, something}]
}).

こんな感じで、あらかじめcommon_tag_listに対応している項目名を書いておくと、

勝手にtagをいれるので、メトリクスの絞り込みが楽になります。

まだ個別の軽量プロセスで監視したい項目が定まってないため、いまのところメトリクスはmessage_queue_lenのみの対応となっています…。

dogstatsd_senderの紹介

使い方はREADMEにも書いているのですが、日本語で概要を紹介します。

導入

rebar3を使っている場合は、reabr.configに以下のように依存関係を書いてください。.srcファイルにもdogstatsd_senderを追加します。

{deps, [
  {dogstatsd_sender, ".*", {git, "https://github.com/mookjp/dogstatsd_sender.git", {branch, "master"}}}
]}.

設定値

必要に応じて以下の設定を変えてください:

[
  %% dogstatsdのアドレス
  {dogstatsd_address, "0.0.0.0"},
  %% dogstatsdのポート番号
  {dogstatsd_port, 8125},
  %% Datadogのメトリクス名の接頭語
  %% e.g. #{ metrics => "my_metrics" } とした場合は、"dogstatsd_sender.my_metrics" がメトリクス名になります
  {metrics_prefix_base, "dogstatsd_sender"},
  %% dogstatsd_sender:register/2 でプロセスを登録した場合の、メトリクス取得の間隔(ミリ秒)
  {metrics_interval_ms, 10000}
]

Dogsatasdのクライアントとして使う

都度カスタムメトリクスを送信したい場合は、dogstatsd_sender:send/1を使うことができます。

dogstatsd_sender:send(#{
  metrics => "my_metrics.point",
  type => g,
  value => 1,
  tags => [{label, sometihng}]
}).

パラメータとして以下を取ります:

  • metrics
    • メトリクス名
  • value
  • type
    • g, c, ms, h, s のatomで指定します。メトリクスのタイプはこちらを参照
  • tags
    • {Label, Value} の形式でタグをproplistsとして指定します

あらかじめ監視するプロセスを指定して定期的にメトリクスを送信する

dogstatsd_sender:register/2 で、あらかじめプロセスIDとタグなどを登録しておくと、dogstatsd_senderが定期的にメトリクスを送信します。

dogstatsd_sender:register(Pid, #{
  metrics_list => [message_queue_len],
  common_tag_list => [ pid, mfa, node ],
  tags => [{label, something}]
}).

パラメータとして以下を取ります:

  • metrics_list
    • 取得するメトリクス。現在はmessage_queue_len(process_infoで取得できるmessage_queue_lenと同じ)のみの対応です
  • common_tag_list
    • 以下のatomを指定すると、対応する情報をタグとして追加します
      • pid: プロセスID e.g. pid:0.100.0
      • mfa: MFA e.g. mfa:gen_server/loop/7
      • node: ノード名。node()の@を_に変換しています e.g. node:nodename_nodehost
      • registered_name: 登録名。process_infoのregistered_nameから取得しています。登録していない場合はundefinedがタグのvalueになります e.g. registered_name:my_server
  • tags
    • dogstatsd_sender:send/1と同じく、タグのproplistsです

登録解除

dogstatsd_sender:register/2 で登録したプロセスが死んでいたら、勝手にdogstatsd_senderはメトリクス監視対象からプロセスを削除します。

手動で登録解除したい場合は、以下のAPIを使ってください。

dogstatsd_sender:unregister(Pid).

その他

READMEを読んでみてください。

まだ作りたてなので、今後いろいろ改良していくと思います。

モジュール作成で得られた知見など

紹介だけだと薄いので、今回モジュールを作るときに得られた知見を書いてみます。

Dogstatsdをローカルで動かす

DatadogのagentはDocekrfileが提供されています。ローカル環境+試験アカウントで事前にメトリクス送信の確認ができます。

DataDog/docker-dd-agent

dogstatsd_senderをローカルで試す場合も、Dockerを使うと結構簡単に環境が用意できるのでおすすめです。

$ docker run -d --name dd-agent \
    -v /var/run/docker.sock:/var/run/docker.sock:ro \
    -v /proc/:/host/proc/:ro \
    -v /sys/fs/cgroup/:/host/sys/fs/cgroup:ro \
    -e API_KEY=<APIキー> \
    -e SD_BACKEND=docker \
    -e LOG_LEVEL=DEBUG \
    -e DD_HOSTNAME=my_hostname \
    -e NON_LOCAL_TRAFFIC=true \ # ローカルホストからデータ送信するために必要
    -p 8125:8125/udp \ # ローカルホストからデータ送信するために必要
    datadog/docker-dd-agent:latest

edocをMarkdown形式で出力

edown を使って、Markdown形式でドキュメントを出力してみました。 こんな感じで設定を書いてrebar3 edocを実行すると、docディレクトリ以下にMarkdownでedocのドキュメントが出力されます。

top_level_readmeを指定すると、指定したREADMEにedownが出力したドキュメントへのリンクが吐かれるのですが、自分で書いたREADMEが上書きされてしまいます。 うまくやれそうな気がするんですが、あまり優先度も高くないのでとりあえずGitでCheckoutしたりしてよしなにやっています。

CircleCIでdialyzerとcommon testの実行

CircleCIでCI環境をつくりました。

悲しいことに、CircleCIはElixir用のコンテナは用意しているのですがErlang用はありません。Elixirって流行ってるんでしょうか?

なので、synlay/docker-erlangのコンテナを使っています。 このコンテナにはrebar3が入っているので、なにも考えずにローカルで実行しているdialyzerとcommon testのコマンドを追加するだけで簡単にCI環境がつくれてしまいました。

設定ファイルはこれだけです

version: 2
jobs:
  build:
    docker:
      - image: synlay/erlang:20.1
    steps:
      - checkout
      - run:
          name: dialyzer
          command: rebar3 dialyzer
      - run:
          name: test
          command: rebar3 ct --verbose

ビルドステータスのバッジは、https://circleci.com/gh/:ユーザ名/:リポジトリ名/edit#badgesでつくれます。

まとめ

  • よかったら使ってみてください
  • CircleCI, ElixirはサポートしているのにErlangはサポートしてない…😥
Retrun to top