この記事はRecruit Engineers Advent Calendar 2017 の21日目の記事です。大変申し訳ありません!!!!
Datadogにメトリクスを送信するためのモジュールを作りました
Erlang・Datadogの組み合わせでモニタリングする場合はvmstatsが便利です。
vmstatsだと、Erlang VM全体のメトリクスは取得できるのですが、ここからさらにボトルネックの調査をするとなると、自分のアプリケーションの特定のプロセスのメッセージキューの溜まり具合も取りたいなあと思うようになりました。
DatadogにはErlang用のクライアントライブラリはなかったのですが、そんなに作るのも大変ではなさそうだったので、簡単なモジュールを作ってみました。
中身は、簡単な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
- 以下のatomを指定すると、対応する情報をタグとして追加します
tags
dogstatsd_sender:send/1
と同じく、タグのproplistsです
登録解除
dogstatsd_sender:register/2
で登録したプロセスが死んでいたら、勝手にdogstatsd_senderはメトリクス監視対象からプロセスを削除します。
手動で登録解除したい場合は、以下のAPIを使ってください。
dogstatsd_sender:unregister(Pid).
その他
READMEを読んでみてください。
まだ作りたてなので、今後いろいろ改良していくと思います。
モジュール作成で得られた知見など
紹介だけだと薄いので、今回モジュールを作るときに得られた知見を書いてみます。
Dogstatsdをローカルで動かす
DatadogのagentはDocekrfileが提供されています。ローカル環境+試験アカウントで事前にメトリクス送信の確認ができます。
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はサポートしてない…😥
- ちなみにTravisはErlangをサポートしているようです。もう作っちゃったので移行しないと思うけど、複数のバージョンでビルドしたいときにはTravisよさそう