TT Lab
はじめる
学ぶ 学習パス コース

CPU・メモリリークの見極め

load averageはCPU使用率ではない

TT Labで続きを見る

一言でいうと

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を使っているものを探す順序

  1. 誰が: top -H -p <pid>で、スレッドまで下りていきます。プロセス全体ではなく、特定のスレッド1つだけが動いているケースが多いのです(GCスレッド、ポーリングループ)。
  2. どこで: perf top -p <pid>で、ホットなシンボルを見ます。シンボルが[unknown]だらけなら、デバッグシンボルがないので、言語ごとのプロファイラーを使います。
  3. なぜ: その関数がなぜそんなに頻繁に呼ばれるのかは、コードで見ます。たいていはO(n²)か、キャッシュが効いていないか、ログを同期で書いています。

perfがないコンテナでは、/proc/<pid>/stackや、言語ランタイムのスレッドダンプ(jstack、py-spy dump)でも、半分はわかります。

実務で本当に大切なこと

KubernetesでCPU limitをかけると、cgroupのクォータがかかります。リクエストが集中するとスロットリングが発生してp99レイテンシが跳ねますが、CPU使用率のグラフはlimitの近くで平らに見えるため、「余裕がある」と誤読しやすいのです。スロットリングは使用率ではなく、cpu.statで確認します。