【実務・中級編】 オーバーコミットメント(Overcommitment)の限界とスラッシングの発生原因 – クラウドインフラと仮想化ネットワーク実践ガイド

物理の限界を超えた先にある「奈落」――オーバーコミットメントとスラッシングの正体

エンジニアたるもの、一度は夢見るはずです。「物理メモリが64GBしかないサーバーに、合計128GBのメモリを要求するVMを詰め込めば、コスト効率は倍になるのでは?」と。

クラウドネイティブな現代において、コンテナや仮想マシンのオーバーコミットメントは、コスト最適化の常套手段です。しかし、この「魔法のような詰め込み」には、物理法則に近い残酷な限界が存在します。今日は、ハイパーバイザーがその限界を超えた瞬間に発生する「スラッシング(Thrashing)」という名の悪夢と、それを回避するための現場の知恵について語りましょう。

1. オーバーコミットメントの「甘い罠」

オーバーコミットメントとは、物理リソース(CPUやメモリ)の容量に対し、論理的にそれ以上のリソースを仮想マシン(VM)やコンテナに割り当てる手法です。

例えば、物理メモリ16GBのホストに、8GBのメモリを要求するVMを3つ動かす。これが成立するのは、全VMが同時に最大負荷をかけることが稀であるという「統計的多重化」の原理に基づいています。しかし、すべてのVMが同時に活動を開始した瞬間、ハイパーバイザーは「物理メモリの貸し借り」の限界に突き当たります。

ここで発生するのがメモリのスワップです。ハイパーバイザーは、物理メモリに入り切らないゲストのメモリページをディスク(ストレージ)へ退避させます。これが「スラッシング」の序章です。

2. なぜ「スラッシング」はシステムを殺すのか

スラッシングとは、OSやハイパーバイザーが「計算」よりも「データのスワップ(メモリとディスクの往復)」にリソースの大部分を費やしてしまう現象です。

1. ページングの爆発: メモリが不足し、頻繁にページアウト(メモリ→ディスク)とページイン(ディスク→メモリ)が発生する。
2. I/Oの飽和: ディスクI/Oがボトルネックになり、CPUはI/O待ち(iowait)でスタックする。
3. レスポンスの崩壊: Web APIの処理が遅延し、TCP接続がタイムアウト(504 Gateway Timeout)を量産する。

この状況下では、アプリケーション層でいくらリトライ回数を増やしても、根本的な解決にはなりません。むしろ、リトライの嵐がさらなる負荷を呼び、泥沼化します。

3. 実践:現場でのスラッシング検知と調査

現場で「APIが突然重くなった」というアラートを受けた際、まずは以下のコマンドでホスト側の状況を確認してください。特に si (swap in) と so (swap out) の値が継続的にゼロ以外を指しているなら、それはスラッシングの兆候です。

# vmstat 1 を実行し、メモリのページング状況を監視する
# si/so カラムに注目してください
$ vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 2  0  10240  45000  12000 500000    0    0     2     5  100  200  5  2 90  3  0
# もし si や so が常時 0 を超えて変動しているなら、メモリ不足のサインです

4. 対策:リソース制限をコードで制御する

Kubernetes環境であれば、ResourceQuota や LimitRange を設定し、安易なオーバーコミットを防ぐのが定石です。以下は、Podに対するリソース制限のYAML例です。

# k8s-resource-limit.yaml
apiVersion: v1
kind: Pod
metadata:
  name: api-server
spec:
  containers:
  - name: app
    image: my-app:latest
    resources:
      requests:
        memory: "256Mi" # 確実に確保されるメモリ
        cpu: "250m"
      limits:
        memory: "512Mi" # これを超えるとOOMKillされる境界
        cpu: "500m"     # CPUスロットリングが発生する上限

5. アプリケーション層からの防御策

インフラがスラッシングに陥る前に、アプリケーション側で「負荷」を検知してリクエストを弾く、あるいはスローダウンさせる設計(バックプレッシャー)が重要です。Pythonでシンプルなリクエスト制御を行う例を紹介します。

import time
import psutil

def check_system_health():
    # メモリ使用率が90%を超えていたら、APIは新規受付を停止する
    mem = psutil.virtual_memory()
    if mem.percent > 90:
        return False
    return True

def handle_request():
    if not check_system_health():
        # 503 Service Unavailable を返して負荷を下げる
        return {"status": 503, "message": "System Overloaded, try later"}
    
    # 本来のビジネスロジック
    return {"status": 200, "message": "Success"}

最後に:SREとしてのアドバイス

「物理リソースを極限まで使い切る」ことは、運用者としての矜持かもしれません。しかし、可用性を犠牲にしたコスト削減は、ただの「コストの先送り」です。

もしあなたがWeb APIの設計に携わっているなら、監視ツール(Prometheus/Grafanaなど)で container_memory_working_set_bytes を注視し、limit に対する使用率が80%を超えないようなスケーリング戦略を立ててください。

ネットワークのパケットが物理層で止まってしまうような悲劇は、事前の設計と正しいメトリクスで必ず防げます。教科書を読み終えたら、次は現場の vmstat を叩いてみてください。そこには、あなたのシステムの「呼吸」がリアルに刻まれています。

コメント

タイトルとURLをコピーしました