ぐるっとぐりっど

日曜プログラマがいろいろ試してみたことを、後の自分のためにまとめておく場所

topで負荷の高いプロセスN個を取り出す

以下の記事の通り、最近は、influxdbをはじめとするTICKスタックで、サーバのメトリクスを収集して問題ないかみている。

grugrut.hatenablog.jp

で、現在存在していないメトリクスで欲しいものは、自分でプラグインを書いてプルリク送ってみたりしてる。

github.com

裕福な人は違うかもしれないけど、一般的にVPSとかクラウド系のリソースで最初に厳しくなるのはメモリだと思う。ディスクだったりCPUが不足することはそうそうないけど、たいていメモリは50%は軽く越えてしまう。僕自身もその例に違わず、メモリが常日頃から不足する状況が続いていた。

でもろくに分析してないので、メモリが不足している原因が、1つのプロセスが食ってるのか、複数のプロセスに原因があるのかわかっていなかった。

直感的には、Jenkins(つまりはjava)プロセスがリソース食ってるのはわかってたんだけど、メモリ使用率に変動があったときに、どこに原因があるかはわからずじまいで対策が取りようになかったので、プロセスごとのCPU使用率、メモリ使用率をとってみることにした。全部取ると大変なことになってしまうので、今回はトップ5だけ取ってみる。

データの取得方法

もちろんgolangでちゃんとプラグインとして書いてもよいのだけど、面倒なので今回はtelegrafのexecプラグインを使う。execプラグインはコマンドを実行し、規定のフォーマットで標準出力に出力されたものを値として取り込むことができる。詳細は以下を見てほしい。

telegraf/README.md at master · influxdata/telegraf · GitHub

で、その形式というのが、

メトリクス名,タグ フィールド

なので、それに合わせて出力してやる必要がある。

スクリプト

topコマンドでさくっととれる。

#!/bin/bash

top -b -n 1 | tail -n +8 | head -5 | awk '{print "process,process="$12" cpu="$9}'
top -b -n 1 -o %MEM| tail -n +8 | head -5 | awk '{print "process,process="$12" mem="$10}'

topは -bオプションをつけるとバッチモードで標準出力にそのまま結果が流れる。 -nオプションはデータを取る回数。通常は無限ループするけど、-n 1としてやることで1回出力すればコマンドが終了する。その後のtailは、topコマンドのヘッダを消すため。その後のheadは、上位のプロセス5個を取り出している。

topコマンドはデフォルトだとCPU使用率の高い順に出力されるが、-oオプションにより、ソートするカラムを選ぶことができる。2つめのtopコマンドでは、-o %MEMとして、メモリ使用率の高い順にソートされるようにしている。

これをターミナルで実行すると以下のような結果になる。

$ process.sh
process,process=top cpu=12.5
process,process=systemd cpu=0.0
process,process=kthreadd cpu=0.0
process,process=ksoftirqd/0 cpu=0.0
process,process=kworker/0:+ cpu=0.0
process,process=java mem=29.1
process,process=influxd mem=8.4
process,process=node mem=3.2
process,process=php-fpm mem=3.1
process,process=php-fpm mem=2.9

あとは、telegrafの設定を変えて、execプラグインでこのシェルスクリプトを実行してやればOK。

 [[inputs.exec]]
   ## Commands array
    commands = [
      "/path/to/process.sh"
    ]
#   commands = [
#     "/tmp/test.sh",
#     "/usr/bin/mycollector --foo=bar",
#     "/tmp/collect_*.sh"
#   ]
#
#   ## Timeout for each command to complete.
   timeout = "5s"
#
#   ## measurement name suffix (for separating different commands)
#    name_suffix = "_mycollector"
#
#   ## Data format to consume.
#   ## Each data format has it's own unique set of configuration options, read
#   ## more about them here:
#   ## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md
   data_format = "influx"

このコマンドのデータフォーマットは、influx形式なので、data_formatもinfluxとしておくこと。

Chronografで見てみる

適当なタイミングでキャプチャした結果がこんな感じ。画面左側がCPU使用率、右側がメモリ使用率。そして、上側がサーバ全体、下側が上位5プロセスのもの。

f:id:grugrut:20170710002829p:plain

こうやって見ると、CPU使用率は一瞬を切り取るためか、あまり相関がないが、メモリ使用率は相関がある。はっきりわかんだね。

左側の赤色の丸は、プロセスが追加で起動したためサーバ全体のメモリ使用率が増え、右側の緑色の丸では、トップのプロセスのメモリ使用率が下がったのでサーバ全体のメモリ使用率も下がっていることがきれいにわかる。
プロセスごとのグラフは、現状それぞれの値で描いているけれど、スタックしてつみあげグラフにしてみてもおもしろそう。

上記のスクリプトは自由に使ってもらって構いませんが、既知バグがあって、同じ名前のプロセスがある場合にうまくinfluxdbに追加できないので要注意(タグがプロセス名なので、かぶってしまう)。

きれいにしてから、golangでリライトしてプラグインにしてもいいんだけど、すでにprocessesプラグインがあって名前に迷うなー。