load averageはCPU使用率ではない
一言でいうと
Linuxのload averageは、実行可能なプロセス + 中断されない待ち(D状態)のプロセスの数です。ディスクが遅くても上がります。CPU使用率とは別のものを測っています。
なぜ必要なのか
「loadが20だから、CPUを増やそう」は、よくある誤診です。コアが8個のマシンでload 20は、CPUが足りないというサインかもしれませんし、NFSが止まって20個のプロセスがI/O待ちに縛られているというサインかもしれません。後者にCPUを増やしても、何も起きません。
他のUnixは、loadにCPU待ちだけを数えます。LinuxだけがD状態(uninterruptible sleep)も一緒に数えます。これは1993年に「システムが忙しい」をよりよく表すために入れた変更で、それ以来、LinuxのloadはCPU指標ではなく、システム需要の指標になりました。
どう動くのか
3つの数字は、1分・5分・15分の指数移動平均です。方向が情報です。
cat /proc/loadavg
# 8.24 4.10 2.05 3/512 12345
# └1분 └5분 └15분 └실행중/전체 스레드 └마지막 PID
1分 > 15分なら今悪化している最中、逆なら回復している最中です。
何がloadを上げているのかを見るには、状態ごとに数えます。
ps -eo state,comm --no-headers | awk '{print $1}' | sort | uniq -c | sort -rn
# R = 실행 가능, D = 끊기지 않는 대기(대개 I/O), S = 잠듦
このコードブロックの韓国語コメントは、Rは実行可能、Dは中断されない待ちでたいていI/O、Sはスリープ、という意味です。
Dが多ければ、CPUの問題ではありません。
CPUそのものを見るには、/proc/statを2回読んで差を出します。
awk '/^cpu /{print $2+$3+$4+$6+$7+$8, $5}' /proc/stat # (busy, idle)
2つの時点の差で使用率を出します。topがしていることが、まさにこれです。
よくある勘違い
topの%CPUが100を超えるのはバグではありません。デフォルトのモードで%CPUは、コア1つが基準です。4スレッドがそれぞれフル稼働すれば400%です。topでShift+Iを押すと、全コア基準(Irixモード解除)に切り替わります。
コンテナの中のtopはホスト全体を見ます。/procがホストのものなので、コア数もloadもホストの値です。コンテナに実際に割り当てられた分は、cgroupで見る必要があります。
cat /sys/fs/cgroup/cpu.max # "쿼터 주기" — max 면 제한 없음
cat /sys/fs/cgroup/cpu.stat # nr_throttled, throttled_usec
このコードブロックの韓国語コメントは、cpu.maxはクォータと周期の2つの値をこの順に書く形式で、maxなら制限がないという意味です。
nr_throttledが増えたら、CPUが足りないのではなく、制限に引っかかっているのです。この2つは、対処がまったく違います。
ロードアベレージはCPUの指標ではありません
Linuxのload averageは、実行中または実行を待っているプロセス数ですが、ここにD状態(中断のない待ち、たいていディスクI/O)が含まれます。他のUnixとの違いで、誤解がここから生まれます。
$ uptime
load average: 8.42, 6.10, 4.33 ← 코어가 4개인데 8?
$ vmstat 1 3
r b ... ← r 은 실행 대기, b 는 I/O 대기
1 7 ... ← 실행 대기는 1뿐, 나머지 7은 디스크를 기다린다
このコードブロックの韓国語コメントは、順に、コアが4つなのにload 8か、rは実行待ちでbはI/O待ち、実行待ちは1つだけで残りの7つはディスクを待っている、という意味です。
rが小さくbが大きければ、CPUを増やしても、何も良くなりません。ディスクが犯人です。load averageだけを見てインスタンスを大きくするのが、この指標で最も高くつく誤解です。
使用率が同じでも原因は違います
topの1行で、どの欄が大きいかが、調査の方向を決めます。
| 大きい欄 | 意味 | 最初に見る場所 |
|---|---|---|
us (user) |
アプリケーションのコード | プロファイラー、ホットな関数 |
sy (system) |
カーネル。システムコールが多いです | strace -c、ファイル・ソケットの使用パターン |
wa (iowait) |
ディスクを待っています | iostat -x、await列 |
si (softirq) |
ネットワーク割り込み | /proc/softirqs、RSS/RPSの設定 |
st (steal) |
ハイパーバイザーに奪われています | クラウドインスタンスのグレード、ノイジーネイバーの問題 |
stが5%を超えたら、自分の問題ではありません。バースト可能なインスタンス(tファミリー)でクレジットが尽きたか、物理ホストが過密です。コードをどれだけ直しても、解決しません。
CPUを使っているものを探す順序
- 誰が:
top -H -p <pid>で、スレッドまで下りていきます。プロセス全体ではなく、特定のスレッド1つだけが動いているケースが多いのです(GCスレッド、ポーリングループ)。 - どこで:
perf top -p <pid>で、ホットなシンボルを見ます。シンボルが[unknown]だらけなら、デバッグシンボルがないので、言語ごとのプロファイラーを使います。 - なぜ: その関数がなぜそんなに頻繁に呼ばれるのかは、コードで見ます。たいていはO(n²)か、キャッシュが効いていないか、ログを同期で書いています。
perfがないコンテナでは、/proc/<pid>/stackや、言語ランタイムのスレッドダンプ(jstack、py-spy dump)でも、半分はわかります。
実務で本当に大切なこと
KubernetesでCPU limitをかけると、cgroupのクォータがかかります。リクエストが集中するとスロットリングが発生してp99レイテンシが跳ねますが、CPU使用率のグラフはlimitの近くで平らに見えるため、「余裕がある」と誤読しやすいのです。スロットリングは使用率ではなく、cpu.statで確認します。