6年くらい前に自作した metric がそこそこ有用だと思うので、OSSで公開します

こんにちわ。せじまです。

秋くらいから艦これ再開したので、ちょうどよいWindowsタブレットはないものかと物色しており、 Surface GO LTE Advanced(一般向け)の発売を待ちわびている今日この頃です。

はじめに

はるか昔kernel 2.6 の頃、Load Average が低めに出てしまうというバグがありました。

当時、弊社では Load Average を監視項目の一つにしていたため、これには大いに困りました。

また、その頃、私は resource monitoring に力を入れていたのですが、以前 blog でも書かせていただいた通り、MySQLのサーバ一台あたり200以上のmetricを収集していたため、「多すぎてどれを見ていいかわからない」といった声が社内から上がっていました。

そういった諸々の問題を解決するため、6年くらい前、私はあれこれ思案して、新しい metric を作成しました。

いまなおその metric は社内で広く普及しており、一定の有用性を示せたかと思いますので、せっかくなのでOSSとして公開させていただくことにしました。

gree/sysload

今回は、その metric と、その metric を生成するためのスクリプトの実装について、解説させていただきます。

sysload

私は作った metric に sysload と名付けました。なぜこの metric を sysload と名付けたかというと

  • Process ではなく System Resource の使用率を考慮する
  • Load Average のように、サーバの負荷を示す指標値として使えることを目標とする

といったところからです。

sysloadは、端的に言いますと、次の三種類の負荷を評価して、最も負荷の高いものを sysload として表示するようにしています。

  • software interrupt を受けているCPUの使用率(NICからの割り込みを受けているCPUの使用率)
  • block device の負荷(iostat -x でいうところの %util)
  • すべてのCPUの使用率(すべてのCPU使い切ったときの最大値は100とする)

上記3つはすべて0-100の値を取るので、sysload の最大値は100となっています。
sysload が 100 になったとき、

  • NICからの割り込み
  • disk I/O
  • CPU

これらのいずれかが飽和状態にあるということですね。CPUバウンドなサーバもdiskバウンドなサーバも、NICからの割り込みが激しいサーバも、とりあえずsysloadさえ監視しておけば、検知できるわけです。

sysload を集計するための cpustats.py

6年前、私は ganglia で resource monitoring することに注力していたので、 sysload は ganglia の python module で導入されました。

sysload を集計するための具体的な実装が、 cpustats.py になります。ganglia の python module なので、 python2.x 向けに書いてあります。 cpustats.py を書くときに sysstat や Linux の kernel など読んでいたので、それらのソフトウェアのライセンスを尊重し、 GPLv2 で公開させていただきます。

cpustats.py はこのまま ganglia で sysload を集計するためにも使えるのですが、コマンドラインから実行することも可能です。もしこの blog を読んでいる方が、 ganglia 以外で sysload を集計してみたいと思ったときは、その挙動を確認するために使えるでしょう。

※当時、ganglia の python module を書く際、 hirose31/ganglia-pymodule_skeleton を参考にさせていただきました。この場を借りて hirose31 さんにはお礼申し上げます。

cpustats.py では、次のような metric を収集しています。

  • sys_load・・・load average の代わり。最大値 100。
  • sys_load_{one,five,fifteen}・・・sys_load の単純移動平均。それぞれ 1min/5min/15min
  • si_cpu_{idle, intr, sintr, system, user, wio} ・・・ software interrupt を受けている CPU 群の idle, hardware interrupt, software interrput, system, user, iowait の合算値
  • {sda, cciss, fioa}_io_util ・・・block device ごとの使用率。 iostat -x でいうところの %util
  • proc_ctxt ・・・Context Switch
  • proc_intr ・・・Interrupts

cpustats.py は、ざっくりいいますと、次のような挙動になっています。

  1. gmond の起動時に、 /proc/interrupts をみて、eth{0,1,2,3} に割り込んでる CPU をチェックする
    • bonding してると active 側の NIC が eth0 と eth1 で切り替わるので、eth0以外からの割り込みもチェックする
  2. eth{0,1,2,3} からの割り込みをチェックして、割り込まれてるNICごとに CPU をグループ分けする
    • もし割り込まれてないNICがあれば、結線されてないと考えて、監視対象から外す
  3. 次の三種類の値を15sec間隔で集計する
    a. 2.で分類した CPU のグループごとに CPU usage の合算値を求めて、もっとも software interrupt の多いグループの値を、それぞれ si_cpu_{idle, intr, sintr, system, user, wio} とする
    b. block device ごとに使用率(sda_io_util, cciss_io_util, fioa_io_utilなど)を求める
    c. すべてのCPU usage の合算値を求める
  4. 3.で集計した値 a, b, c を比較して、100 - idle が最大となる値を sys_load とする
    • kernel 2.6 は NIC からの interrupt を受けた CPU (最終的には software interrupt を受けたCPU) と同じCPUで process を動かそうとするので、 software interrupt を受けているCPUは、 idle が低くなりがちである。ゆえに、 si_cpu_system + si_cpu_sintr + si_cpu_intr > N を超えていた場合のみ、100 - si_cpu_idle を sys_load の候補にする。Nのデフォルトは40.0であり、設定ファイルで変更可能な値(interrupt_threshold)である
  5. 1min/5min/15min ごとに sys_load の単純移動平均を求めて、それぞれ sys_load_{one,five,fifteen} とする

sys_load_{one,five,fifteen} を求めているのは、ある程度の外れ値を許容できるようにするためです(log rotate で disk I/O によるスパイクがしばしば発生するなど)。 auto scaling によるインスタンス起動時の激しい disk I/O などではちょっと難しいかもしれませんが、 log rotate 程度のスパイクであれば、 sys_load_five くらいになると、あまり影響を受けなくなります。

sysloadで、実際にどのような運用をしているのか

弊社では、オンプレミス環境とパブリッククラウドの区別なく、 cpustats.py が動く環境であれば、ほとんどの環境でsysloadは収集されています。PrometheusとGrafanaで resource monitoring されている環境でも、 gmond 経由で cpustats.py を実行し、sysload を収集しています。一部のマネージドサービスやコンテナなど、 cpustats.py が動作しない環境であっても、 sysload と似たような値を計算して求めるようにしているようです。

そうやって収集された sysload は、次のように用いられています。

  • sys_load_five が一定値を超えたら alert が発砲されるようになっています。
  • キャパシティプランニングするときの指標値として sysload を用いています。ゲームなどでは「前回のイベントでは、ピーク時にDBサーバの sysload がNにまで達してしまったので、今回はもう少し下げられるように、サーバを増設しておこう」といったキャパシティプランニングに用いられています。
  • オンプレミス環境では、大部分のサーバで、「一日でもっとも sys_load_five が高かった時間とその値」を毎日MySQLなどに保存し、 daily report や monthly report のメールを投げられるようにしています。集計時には、DBのクラスタや、アプリケーションサーバのサービス単位で平均値を求めるようにしているので、サービス単位でピーク時のsysload平均値がわかるようにしています。

sysloadは、CPU, disk I/O, NICからの割り込みといった負荷を一つの metric で監視できるので、アプリケーションサーバでもDBサーバでも、弊社におけるだいたいのサーバの監視に利用できています。

もちろん、sysloadは万能ではありません。弊社はMySQLのmonitoringに注力していますが、sysloadでは、InnoDBのmutexの競合やspinlockやthreadのconcurrencyやdirty pageのflushやその他諸々の監視はできません。TCP関係のerrorを検知することもできません。しかし、 sysload を導入することで、大部分の問題が、0-100の値で表現できるようになり、社内で幅広く活用することできるようになるのです。

おわりに

そういったわけで、弊社で幅広く使われている sysload とその実装を公開させていただきました。 cpustats.py は500行程度しかないごくごく短いものなので、興味のある方は読んでみて、自分の好きな言語で再実装されても良いのではないでしょうか。NICがeth0というのはbiosdevname的にいささか古いので、そういったところに手を入れられるのも良いでしょう。

今回はmonitoringの話をさせていただきましたが、MySQLでディープなネタが2つくらいありますので、今後、blogなりスライドなりで公開したいなぁと思ってます。興味のある方は気長にお待ち下さい。