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として公開させていただくことにしました。
今回は、その 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 は、ざっくりいいますと、次のような挙動になっています。
- gmond の起動時に、 /proc/interrupts をみて、eth{0,1,2,3} に割り込んでる CPU をチェックする
- bonding してると active 側の NIC が eth0 と eth1 で切り替わるので、eth0以外からの割り込みもチェックする
- eth{0,1,2,3} からの割り込みをチェックして、割り込まれてるNICごとに CPU をグループ分けする
- もし割り込まれてないNICがあれば、結線されてないと考えて、監視対象から外す
- 次の三種類の値を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 の合算値を求める - 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)である
- 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なりスライドなりで公開したいなぁと思ってます。興味のある方は気長にお待ち下さい。