ロードアベレージはCPU使用率ではない
一言でいうと
Linuxのロードアベレージは、CPU待ちだけを数えません。ディスクやNFSの応答を待つD状態のタスクも一緒に数えます。そのため、CPUが遊んでいてもロードが24になることがあり、そのときの犯人はストレージです。
なぜ必要なのか
明け方のアラート。8コアのサーバーのロードアベレージが24.31, 22.08, 14.95なのに、topのCPUアイドルは60%を超えています。ここで意見が2つに分かれます。「モニタリングが間違っている」か、「CPUを3倍に増やすべきだ」かです。どちらも間違いです。
単位からして違います。使用率は割合(0–100%)ですが、ロードは個数(タスクの数)です。そして「ロード = CPU待ち行列の長さ」は、ほかのUnixでは正しいものの、Linuxでは誤りです。
どう動くのか
カーネルは5秒に1回、各CPUのランキューをサンプリングします。
nr_active = 실행 중이거나 실행 대기(R) + 중단 불가 대기(D)
Linuxは1993年にTASK_UNINTERRUPTIBLE(D状態)をこの計算に加えました。意図は、CPUの需要だけでなく、システムリソース全般に対する需要を測ることでした。D状態は、シグナルでも起こせないカーネル内部の待機です。ブロックI/Oの完了、ページキャッシュのロック、ハードマウントのNFSの応答待ち、一部のカーネルミューテックスなどがあります。このリストにCPUはありません。
そして、この値は算術平均ではなく指数減衰平均です。階段状に負荷が上がると、1分平均は1分後に約63%しか反映されず、99%に達するまで約5分かかります。ここから2つのことが導かれます。
- 「1分 > 5分 > 15分」なら上昇中、逆なら回復中です
- 障害が終わった直後に15分平均だけが高いのは正常です。その数字でアラートを設定すると、復旧後20分間鳴り続けます
診断の順序は次のとおりです。
cat /proc/loadavg # 네 번째 필드 '실행가능/전체'를 함께 본다
ps -eo state --no-headers | sort | uniq -c # R 과 D 를 센다
vmstat 1 5 # b 열이 D 상태 개수, wa 가 I/O 대기 비율
iostat -x 1 3 # await 와 큐 깊이로 판단 (%util 100% 는 포화가 아닐 수 있다)
ロードが24なのにRが2なら、残りの22はDです。そうなると、答えはCPUではなくストレージ側にあります。
「コア数で割れ」が成り立たなくなる場面も知っておく必要があります。
- D状態による汚染: 上で見たとおりです
- 平均の遅れ: 30秒のスパイクは、1分平均では半分も現れません
- SMT:
nprocが16でも、物理コアは8個かもしれません - cgroupのスロットリング: スロットルされたタスクはランキューから完全に外れるため、ロードに反映されません
- 帰属情報の欠如: グローバルなスカラー値が1つだけなので、アラートでできることは「入って確認しろ」だけです
現場での姿
コンテナの静かな停止。cgroupにCPU上限があると、カーネルは周期(デフォルト100ms)ごとにクォータを配分し、周期の中で使い切ると残りの時間はグループ全体をランキューから外してしまいます。limitが1コアでスレッドが8個なら、100msのクォータを12.5msで使い切り、残りの87.5msは完全に止まります。それなのに平均の使用率は12.5%前後に見え、ロードにも反映されず、stealにも出ません。cpu.statのnr_throttled / nr_periodsの比率だけが、この事実を教えてくれます。5%を超えたら調査、10%を超えたら対処の対象です。
steal time。topのstは、「自分が使いたかったのに、ハイパーバイザーが別のゲストに与えた時間」です。アプリケーションをどれだけ最適化しても、この数値は下がりません。1%未満は正常、5–10%が続くなら対処、10%を超えたら、そのホストではどんなチューニングも無効です。そして、ゲストの中でrebootしても物理ホストは変わりません。クラウドAPIのstop/startで初めて再配置されます。
PSIが答える質問。ロードは「いくつが待っているか」しか語らず、「どれくらい長く止まっていたか」は語りません。/proc/pressure/{cpu,io,memory}のsomeは、少なくとも1つが止まっていた時間の割合、fullは、すべてが同時に止まっていた割合です。ioのfull avg10=61は、直近10秒のうち6秒以上、システム全体がI/Oのために停止していたという意味で、これは失われたスループットにそのまま換算できます。
負荷が高いのにCPUは暇なとき
load averageが40なのにtopのCPU使用率が5%という状況は、故障ではありません。Linuxのロードは、実行待ちだけでなくD状態(ディスク待ち)も一緒に数えます。そのため、ロードが高いということは「忙しい」ではなく、「何かを待っているものが多い」という意味です。
まず、どの種類かを切り分けます。vmstatのr列は実行待ち、b列はブロック待ちです。
vmstat 1 5
# r b swpd free buff cache si so bi bo in cs us sy id wa st
rが大きければCPUが足りておらず、bとwaが大きければストレージがボトルネックです。st(steal)が0でなければ、仮想マシンが物理CPUを受け取れていないということなので、ホスト側の問題です。
CPUが足りないときは、コア数と比較します。ロード4は、1コアでは深刻ですが、8コアでは余裕があります。nprocで割った値が1を超えるかどうかが基準です。ただし、コンテナの中のnprocはホストのコア数を返す場合が多いので、cgroupのcpu.maxも一緒に見ます。
1つのプロセスが1コアを使い切っているのか、複数のスレッドで分け合っているのかを見ます。
top -H -p <pid> # 스레드 단위
pidstat -t -p <pid> 1
CPU使用率がちょうど100%で止まっているなら、単一のスレッドが1コアを使い切っているということで、その場合はコアを増やしても何の意味もありません。
スロットリングを見逃さないようにします。コンテナでCPUの制限がかかっていると、使用率は低く見えるのに、応答は遅くなります。cgroupの統計に、その回数が残ります。
cat /sys/fs/cgroup/cpu.stat # nr_throttled, throttled_usec
nr_throttledが増え続けているなら、制限を引き上げるか、リクエストの処理方式を変えて、短時間の急増を減らします。
iowaitは「CPUが遊んでいて、その間ディスクを待っている」という意味にすぎません。CPUが忙しければ、同じディスクのレイテンシがあってもwaは低く出ます。ストレージが遅いかどうかは、iostat -x 1のawaitと%utilで判断します。
次のラボですること
CPUを使い続けるプロセスを起動して、ロードアベレージが実際に上がる様子を観察し、R状態と累積CPU時間を/procから直接読み取ります。PSIとcgroupのスロットリングカウンターを確認しますが、ない環境なら「ない」と正確に記録します。最後に、コア数で正規化してロードを判定するスクリプトを作ります。