質問はほとんどいつも同じです。夜のあいだサイトの応答に二十秒かかっていたが、朝までに何もしないうちに直っていた、と。もう見るものは残っていません — top が答えるのは現在の一秒であって、尋ねられているのは午前三時のことだからです。以下では、負荷の数値が実際には何を意味するのか、そのどれを対で読むべきか、そして単発の計測が助けになるより誤解を招くことのほうが多い理由を扱います。
Load average: 三つの数字と、よくある誤解
uptime
cat /proc/loadavg
nproc
三つの数字は一分、五分、十五分の平均です。比べる相手はゼロではなく、nproc が示すコア数です。八コアのマシンでの 8 は、満杯ではあっても健全な負荷です。サーバーがこなせるだけの仕事がちょうどある状態です。一コアの VPS での 2 は、処理能力の二倍の長さの行列であり、どの要求も順番を待つことになります。
三つの数字の関係は方向を示します。一分値が十五分値より明らかに高ければ、負荷は今まさに上がっています。逆であれば、ピークは過ぎており、見えているのはその尾です。
ここで、この数値の読み方を最も狂わせる誤解に触れます。Linux の load average は「CPU 使用率」ではありません。他の Unix 系とは異なり、Linux はここに、実行中または実行可能なプロセスだけでなく、状態 D にあるプロセス — 割り込み不可能なスリープ状態のもの — も数えます。つまりディスクやネットワークファイルシステムを待っているプロセスです。負荷 12 なのに CPU がほとんど遊んでいるサーバーは、ここから生まれます。仕事は進んでおらず、全員が行列に並んでいるのです。
CPU かディスクか
この二つを切り分けるのが、まずやる価値のあることです。
vmstat 1 5
iostat -x 1 3
vmstat の出力では三つの列が効いてきます。r は CPU 待ちの行列に並んでいるプロセス数、b は入出力待ちでブロックされている数、wa は CPU が何もせずディスクを待っていた時間の割合です。us と sy が控えめなまま wa が 10〜15 % を超えて居座るなら、サーバーはディスクで詰まっています。コアを足しても意味はありません。今あるコアがすでに空いているからです。
どのプロセスが待っているかは名前で並べられます。
ps -eo state,pid,comm,wchan:30 | awk '$1 ~ /^D/'
自分たちの実務からの一例です。既定の設定のままだと Suricata は三十種類ほどのイベントを書き出し、そのためのローテーションは何も設定しません。稼働中のサーバーでは、これが二日で 15 GB のログになりました。そのときの負荷は CPU の使用ではなく、まさに I/O wait として現れていました。システムが休みなく書き続け、ほかのすべてがその後ろに並んでいたのです。対処は上位プランではなく、イベント種別を絞ることと、ローテーションを設定することです。
逆の例は、純粋に CPU 側の話で、こちらははるかに気づきにくいものです。あるウェブページが、PHP-FPM の動作ユーザーで apt を呼び出していました。root は /var/cache/apt/pkgcache.bin にバイナリキャッシュを保持していて — リポジトリが十数個あるホストでは 70 MB になります — これをコストなしでメモリにマップします。一般ユーザーはそのディレクトリに書き込めないため、呼び出しのたびにキャッシュを作り直します。同一のマシンでの計測値は、root で 0.01 秒の CPU 時間に対し、権限のないユーザーで 4.2 秒でした。同じコマンドで四百倍の差が、ページを開くたびに掛け算されていたわけです。
どちらの例からも結論は同じです。負荷は測るものであって、当てるものではありません。怪しいコマンドを一つずつ計測するのに三十分ほどかかりますが、たいていは最初に疑っていたのとは別の場所を指し示します。
メモリ: free は見た目どおりのものを示さない
free -h
used の列は単体ではほとんど何も語らず、free の列にいたっては明確に誤解を招きます。Linux は使っていないメモリをページキャッシュに回し、要求があれば即座にアプリケーションへ返すからです。読むべき列は available — swap に落ちずにどれだけ確保できるか、です。buff/cache が大きいのは問題ではなく、システムが想定どおりに動いている印です。
swap で重要なのは現在値ではなく形です。上がってゼロに戻った場合は、短いピークがあったということ。一度上がってそのままなら、ピークはすでに起きており、追い出されたページを誰も戻していないということです。サーバーは穏やかに見えても、ある時点でメモリが足りていなかったわけです。いま実際に入れ替えが起きているかどうかは、vmstat の si と so の列に出ます。そこがゼロでない状態こそ、利用者が最もはっきり体感する遅さです。
夜間の落ち込みのあいだにプロセスがそのまま消えていたなら、説明はたいていここにあります。
journalctl -k --since yesterday | grep -i "out of memory"
dmesg -T | grep -i "killed process"
Out of memory: Killed process 1234 (mysqld) のような一行は、どんなグラフよりもはっきり決着をつけます。データベースは「勝手に落ちた」のではなく、メモリが尽きたためにカーネルが止めたのです。
誰がやっているのか
ps aux --sort=-%cpu | head -10
ps aux --sort=-%mem | head -10
一つ注意点があります。ps の %CPU はプロセスの生存期間全体の平均なので、短時間の急上昇はこの一覧では埋もれてしまいます。それを見るには、区間で計測する top か pidstat 1 5 が必要です。
負荷がセキュリティの問題になるとき
夜間に平坦な 100 % の CPU 使用が続き、名前に意味のないプロセスが /tmp や /dev/shm を作業ディレクトリにしている — これはサイトが成長した姿ではなく、マイナーの典型像です。受信トラフィックの急増とログ書き込みの増加が同時に起きていれば、パスワード攻撃が進行中です。ログ量が急に跳ね上がるのは、たいていどこかのフィルタの誤検知の嵐です。
防御ツールそのものも一つの分類です。当社のあるサーバーでは、訪問者が一人もいない状態で負荷が三前後に張り付いており、累積 CPU 時間で見た ps の先頭にいたのはサイトでもデータベースでもなく、CrowdSec、fail2ban、Falco、Suricata でした。障害ではありませんし、止める理由にもなりませんが、防御の代価は数字で知っておく価値があります。小さな VPS では体感できる大きさです。
観測する意味
ここまでの内容はすべて「いま何が起きているか」への答えです。朝の問い — 午前三時に何が起きたのか — はこれらのコマンドではカバーできません。誰も記録していなければ、過ぎた夜のデータはどこにもないからです。かといって VPS 一台のために Prometheus と Grafana を立てるのも疑問で、観測する側のスタックのほうが観測される側のサーバーより重くなってしまいます。
五分ごとにデータベースへ一行、そしてそこから直近二十四時間を描く一ページがあれば十分です。load average、CPU 使用率と別建ての I/O wait、メモリと swap、ディスクの読み書き、パーティションの使用率、inode、ファイルディスクリプタ、接続数。同じ時間軸に並べれば、対になる指標は目で読み取れます。CPU が静かなのに wa が高い、ゼロに戻らなかった swap、上限に近づくディスクリプタ — これから起きる too many open files エラーが、発生の数時間前から見えるようになります。まとまった形でどう見えるかは、下のデモページで確認できます。