アルアカ - Arcadia Academia

Arcadia Academiaは「エンジニアリングを楽しむ」を合言葉に日本のデジタル競争力を高めることをミッションとするテックコミュニティです。

「サーバが重いのにCPU使用率は低い」の正体 — PSIとOOMログで診断するメモリスラッシングと応急処置

Featured image of the post

「サーバの応答が異常に遅いのに、CPU使用率を見ると7〜14%しか使っていない」——先日、筆者がクライアントのクラウドサーバで実際に遭遇した症状です。結論から言うと、この「CPUは暇なのに重い」パターンの典型はメモリ枯渇によるスラッシングです。CPUが働けないのではなく、メモリ待ちのI/Oで全プロセスが足止めされているため、CPU使用率には現れません。この記事では、筆者が実際に行った診断(free / uptime / PSI / OOMログの4点セット)と、再起動なしで効く応急処置(スワップファイル追加)、そして恒久策への持って行き方までを、実例の数値つきで解説します。

📣
サーバ運用・システム開発のご相談はお気軽にどうぞ!

[目次を開く]

1. 症状の特徴 — 「CPUは暇なのに重い」

まず、この問題には分かりやすい特徴があります。

  • 体感は明らかに重い。SSHのログインにすら数秒〜数十秒かかる、Webの応答がタイムアウトする
  • なのにCPU使用率は低いtopで見ても平均7〜14%程度で、CPUを食い潰しているプロセスが見当たらない
  • ロードアベレージだけが異常に高い。vCPU 1〜2のサーバでloadが3を超えている

「CPUが暇ならボトルネックはCPU以外にある」——これが調査の出発点です。CPU使用率が低いからといって「サーバは健全」と判断するのは早計で、むしろ低いCPU使用率と高いロードアベレージの組み合わせは危険信号です。

2. なぜCPUが暇なのに重いのか — スラッシングの仕組み

メモリが枯渇すると、Linuxカーネルは物理メモリを空けるためにページ(メモリの管理単位)を追い出します。スワップが未設定の場合、追い出せるのは主に「ディスクから読み直せるページ」、つまり実行中のプログラムのコードページです。

ところが、追い出されたコードは実行のたびにディスクから読み直されます。読み直した矢先にまた別のページが追い出され、また読み直し……という無限の往復が発生します。これがスラッシングです。

flowchart TD
    a["メモリ不足"] --> b["カーネルがコードページを追い出す"]
    b --> c["プログラム実行時にページが無い"]
    c --> d["ディスクから読み直し (I/O発生)"]
    d --> e["I/O待ちでプロセス停止 (D state)"]
    e --> f["CPUは待つだけで暇"]
    f --> a

このとき各プロセスはI/O待ち(割り込み不能スリープ、いわゆるD state)で止まっています。CPUは仕事をしていないので使用率は低いまま。しかしプロセスは大量に「実行待ち」で滞留しているため、ロードアベレージは跳ね上がります。

ここで押さえておきたい一般知識として、ロードアベレージは「実行中+実行待ち+割り込み不能スリープ(D state)のプロセス数」の平均であり、CPU使用率とは別の指標です。だからこそ「CPUは暇なのにloadが高い」という一見矛盾した状態が成立します。

3. 診断4点セット — コマンドと読み方

筆者が実際の調査で使った4つのコマンドです。この4点で証拠を揃えると、推測ではなく確定診断として報告できます。

3-1. free -m — 空きメモリとスワップの有無

free -m
              total        used        free      shared  buff/cache   available
Mem:            981         890          45          10          46          38
Swap:             0           0           0

見るポイントは2つです。availableが数十MBまで枯れていること、そしてSwapの行がすべて0(=スワップ未設定)であること。この2つが揃っていたら、逃げ場のないメモリ枯渇です。

3-2. uptime — ロードアベレージ

uptime
14:02:11 up 32 days,  1:17,  1 user,  load average: 3.21, 2.98, 2.75

このサーバはvCPU 1でした。目安として、ロードアベレージがvCPU数を大きく超えていたら処理が滞留しています。vCPU 1でload 3.2は「常時2件以上が待たされている」状態。CPU使用率が低いのにこの数値なら、待ち先はCPUではなくI/Oです。

3-3. cat /proc/pressure/memory — PSI(決定打)

cat /proc/pressure/memory
some avg10=22.63 avg60=19.40 avg300=18.12 total=482910422
full avg10=8.41 avg60=7.02 avg300=6.55 total=190882145

PSI(Pressure Stall Information)はLinux 4.20以降で使えるカーネルの指標で、「メモリ待ちでプロセスが停止していた時間の割合」を直接教えてくれます。

  • some: 少なくとも1つのプロセスがメモリ待ちで止まっていた時間割合
  • full: 全プロセスがメモリ待ちで止まっていた時間割合

上の例では、直近10秒平均で22.6%の時間、誰かがメモリ待ちで停止し、8.4%の時間はシステム全体が完全に固まっていたことになります。健全なサーバではどちらもほぼ0%です。「体感の重さ」を数値で示せる、報告書に最も効く証拠です。

3-4. journalctl -k | grep -i oom — OOM Killerの発動履歴

journalctl -k | grep -i oom
kernel: Out of memory: Killed process 21437 (node) total-vm:1204512kB, anon-rss:412288kB

OOM Killerの発動履歴が出れば、カーネル自身が「メモリが足りずプロセスを強制終了した」と証言していることになります。「たまにプロセスが勝手に落ちている」という別の症状もこれで説明がつきます。

この4点に加えて、vmstatiostatでスワップ未設定なのに常時ディスク読み込みが観測されれば、「コードページの追い出し→読み直し」のスラッシングと確定できます。コマンドの基本的な使い方はLinuxの基本コマンドまとめも参考にしてください。

4. 応急処置 — スワップファイルの追加(再起動不要・可逆・無料)

根本原因はメモリ不足なので恒久策はメモリ増強ですが、インスタンスタイプの変更には再起動(ダウンタイム)と費用増が伴い、その場では決められないことが多いです。そこで筆者がまず打ったのが、スワップファイルの追加です。再起動不要・いつでも外せる・追加費用ゼロと、応急処置として理想的な性質を持っています。

sudo dd if=/dev/zero of=/swapfile bs=1M count=4096
sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile
# 永続化: /etc/fstab に「/swapfile none swap sw 0 0」を追記(事前にfstabをバックアップ)

ポイントは3つです。

  • count=4096で4GBのスワップファイルを作成(ディスク空き容量を先に確認)
  • chmod 600は必須。スワップにはメモリ内容が書き出されるため、他ユーザーに読ませてはいけません
  • swaponした時点で即座に有効。ただしこれだけでは再起動で消えるので、/etc/fstabへの追記で永続化します。fstabは書き損じると起動不能になり得るファイルなので、必ずバックアップを取ってから編集してください

スワップがあることで、カーネルは「使用頻度の低いデータページ」を退避先に逃がせるようになり、頻繁に使うコードページを追い出さずに済みます。これでスラッシングの悪循環が断ち切れます。

5. 改善の確認 — 適用直後に数値で示す

応急処置は「効いたことを数値で確認して報告する」までがワンセットです。筆者のケースでは、スワップ適用から数分後に同じコマンドを再実行し、次の改善を確認しました。

指標 適用前 適用後
ロードアベレージ 3.2 0.8
PSI some avg 22.6% 8.2%

loadはvCPU数を下回る水準まで低下し、メモリ待ちの停止時間も大幅に減少。SSHやWebの応答も体感で明確に改善しました。この「前後比較の数値」があると、次のステップである恒久策の提案に説得力が生まれます。

6. 恒久策への持って行き方

強調しておきたいのは、スワップはあくまで応急処置だということです。スワップはディスクをメモリ代わりに使う仕組みなので、根本的にメモリが足りない状況が続けば、いずれスワップI/O自体が性能の足かせになります。

恒久策はメモリ増強、クラウドであればインスタンスタイプの変更です。ただしこれには次の2点が伴うため、エンジニアが勝手に決めるのではなく、意思決定者に判断を回すのが正しい進め方です。

  • ダウンタイム: インスタンスタイプ変更には停止・起動が必要
  • 費用増: 月額のランニングコストが上がる

筆者は「現状の診断結果(4点セットの証拠)」「応急処置とその効果(前後の数値)」「恒久策の選択肢とコスト・停止時間」をまとめて提示し、判断を依頼する形にしました。応急処置で当面の火は消えているので、落ち着いて費用対効果を検討してもらえます。

7. よくある質問

Q. スワップを追加すると逆に遅くなると聞きましたが?

スワップI/Oが常態化すれば遅くなるのは事実です。ただし「スワップなしでコードページがスラッシングしている」状態よりは大幅にマシで、実測でもload 3.2→0.8と改善しています。スワップ使用量が恒常的に多いなら、それはメモリ増強のサインと捉えてください。

Q. PSIが確認できません(/proc/pressure が無い)

PSIはLinuxカーネル4.20以降の機能です。古いカーネルでは使えないため、その場合はvmstat 1のsi/so(スワップイン/アウト)やb列(割り込み不能スリープのプロセス数)、free、OOMログの組み合わせで代替診断します。

Q. OOM Killerのログが無ければメモリ枯渇ではない?

いいえ。OOM Killerは「完全に割り当て不能になった」最終局面で発動するもので、その手前のスラッシング段階では発動しないことも多いです。OOMログは「あれば強い証拠」であって、無いことは否定材料になりません。PSIとfreeを主証拠にしてください。

Q. スワップサイズはどのくらいにすべき?

応急処置としては物理メモリの1〜4倍程度、ディスク空き容量と相談して決めれば十分です。今回は1GBメモリのサーバに4GBを割り当てました。あくまで時間稼ぎなので、サイズの厳密な最適化より恒久策の検討を優先しましょう。

8. まとめ

  • 「重いのにCPU使用率が低い(7〜14%程度)」はメモリスラッシングの典型症状
  • 診断は4点セット: free -m(残量とスワップ有無)、uptime(load)、/proc/pressure/memory(PSI)、journalctl -k | grep -i oom(OOM履歴)
  • ロードアベレージは「実行中+実行待ち+D state」の平均で、CPU使用率とは別物
  • 応急処置はスワップファイル追加。再起動不要・可逆・無料で、実測でload 3.2→0.8、PSI some 22.6%→8.2%に改善
  • スワップは時間稼ぎ。恒久策はメモリ増強で、ダウンタイムと費用を添えて意思決定者に判断を回す

CPU使用率だけを見て「サーバは元気」と誤診しないこと。PSIという強力な物差しを手に入れておけば、「なんとなく重い」を数値で語れるようになります。

プログラミング学習でお悩みですか?

現役エンジニアがあなたの学習をマンツーマンでサポートします。

  • 学習の進め方がわからない
  • ポートフォリオの作り方を知りたい
  • 現場で使える技術を学びたい
まずは30分の無料相談

相談は完全無料・オンラインで気軽に

あなたを爆速で成長させるメンタリングプログラムはこちら

メンタープログラムバナー

学習・開発のお悩みは現役エンジニアに相談

メンタープログラムの詳細を見る

エンジニアの基礎学習ゲーム

プログラミング学習支援

無料相談はこちら