こんにちは、エンジニアの id:tsub511 です。 ここ数日気温の寒暖差が凄いですね。昨日あまりにも寒すぎて一度しまった冬用の布団を引っ張りだしたら、また気温が上がってきたので片付けることになりそうです。
最近、Datadog でプロセスがオープンしているファイルディスクリプタ数のメトリクスを取る必要があり、色々と考えた結果良い方法を思いついたため、今回ご紹介します。
Datadog 標準の system.processes.open_file_descriptors
メトリクスを取るには root 権限が必要
Datadog では標準で、Process Check という機能を使うことで system.processes.open_file_descriptors
メトリクスを取ることができます。
ただし、説明文にも書いてある通り dd-agent
ユーザーが実行したプロセスしかこのメトリクスを取得することが出来ません。
そのため、例えば Rails アプリケーションを動かすために puma
プロセスを dev
ユーザーで動かしていた場合、以下のような設定を書いても system.processes.open_file_descriptors
メトリクスを取得することが出来ません。
init_config: instances: - name: puma_worker search_string: ["puma: cluster worker"] exact_match: False
これは何故かというと、プロセスがオープンしているファイルディスクリプタ数を取得するためには /proc/<PID>/fd
以下にアクセスする必要があるためです。
/proc/<PID>/fd
ディレクトリはそのプロセスを実行したユーザーにしか read 権限がありません。
$ ls -al /proc/1/fd ls: cannot open directory /proc/1/fd: Permission denied $ sudo ls -al /proc/1 | grep fd dr-x------. 2 root root 0 Apr 18 06:58 fd dr-x------. 2 root root 0 May 11 06:12 fdinfo
そのため、dd-agent
はそのメトリクスを取得できないというわけです。
ただし、dd-agent
に root 権限を与えることで、閲覧は可能になります。
公式ドキュメントではそのやり方が提示されていますが、セキュリティ的にリスクがあるため、推奨はされていません。
さて、この記事の内容は dd-agent
に root 権限を与えずに system.processes.open_file_descriptors
メトリクスを取得するということでしたが、どうやれば良いのでしょう?
DogStatsD を使う
Datadog には DogStatsD という仕組みがあります。
DogStatsD は任意のカスタムメトリクスを Datadog に送る方法の一つです。
通常は以下のような言語毎のライブラリを公式が提供してくれているため、こちらを使うことで任意のカスタムメトリクスを送ることができます。
DogStatsD を通してメトリクスを送る際は、その送り側のプロセスは任意のユーザーで実行できます。
そのため、上記の例にあったように dev
ユーザーが puma
プロセスを実行している場合は dev
ユーザーで DogStatsD にメトリクスを送るプロセスを実行すれば、
同じ dev
ユーザーのため /proc/<PID>/fd
への read 権限があります。
思いついてみれば簡単なことでしたね。
でもプログラミング言語で実装するのは面倒じゃない?
少し本題とは反れますが、もう少しお手軽に DogStatsD にカスタムメトリクスを送りたいな、とも思います。
そこで、調べてみたところ「DogStatsD には単純に専用のフォーマットで UDP パケットを送るだけで良い」ということを知りました。
On Linux:
vagrant@vagrant-ubuntu-14-04:~$ echo -n "custom_metric:60|g|#shell" >/dev/udp/localhost/8125
or
vagrant@vagrant-ubuntu-14-04:~$ echo -n "custom_metric:60|g|#shell" | nc -4u -w0 127.0.0.1 8125
https://docs.datadoghq.com/developers/dogstatsd/#sending-metrics
上記のように、DogStatsD のエンドポイントである localhost:8125
に custom_metric:60|g|#shell
のようなフォーマットで UDP パケットを送ってやれば良いです。
そのため、プロセスがオープンしているファイルディスクリプタ数のカスタムメトリクスを送るには、以下のコマンドを実行すれば良いです。
$ echo -n "open_file_descriptors.puma_worker:$(ls /proc/$(pgrep -f -u dev 'puma: cluster worker' | head -1)/fd/ | wc -l):g" | nc -u -4 localhost 8125
上記のコマンドを crontab
などで毎分実行してやれば open_file_descriptors.puma_worker
メトリクスを送ることができます。
ただし、実際に本番で利用しているコマンドはそこまで単純ではなく、以下のようなシェルスクリプトを書いて実行しています。
#!/bin/sh if [ $# -ne 2 ]; then echo "Require 2 arguments" 1>&2 exit 1 fi PROCESS_NAME=$1 USER=$2 # pgrep でシェルスクリプト自身のプロセスがマッチしてしまうため `grep -v` で除外する # CentOS 6 では pgrep に -a オプションがないため注意 # # 複数のプロセスが見つかっても無視する PROCESS=$(pgrep -f -a -u "${USER}" "${PROCESS_NAME}" | grep -v "$0" | head -1 | cut -f 1 -d ' ') if [ -z "${PROCESS}" ]; then echo "${PROCESS_NAME} does not exists" 1>&2 exit 1 fi ls /proc/"${PROCESS}"/fd/ | wc -l
#!/bin/bash if [ $# -ne 3 ]; then echo "Require 3 arguments" 1>&2 exit 1 fi METRIC_NAME=$1 VARUE=$2 METRIC_TYPE=$3 echo -n "${METRIC_NAME}:${VARUE}|${METRIC_TYPE}" | nc -u -4 localhost 8125
$ crontab -l * * * * * /path/to/send-to-dogstatsd.sh open_file_descriptors.puma_cluster_worker $(/path/to/get-open-fd.sh "puma: cluster worker" dev) g > /dev/null