WSL2のメモリ枯渇を防ぐ: earlyoomの条件とcgroup封じ込め

WSL2のメモリ枯渇を防ぐ: earlyoomの条件とcgroup封じ込め

WSL2でビルドや複数の開発ツールを同時に動かしていると、Linux側のプロセスが想定以上にメモリを確保し、仮想マシン全体が応答しなくなることがあります。swapを足したり、メモリ不足時にプロセスを終了させるツールを入れたりする方法は有効ですが、設定を誤ると「守っているつもりなのに止められない」状態になり得ます。

この記事では、earlyoomの判定条件を確認し、プロセスごとの上限をcgroupで先に設定する考え方を整理します。

earlyoomのAND条件に注意する

earlyoomは、カーネルのOOM killerが動く前に、メモリを多く消費しているプロセスを終了させる仕組みです。ただし、設定によっては空きメモリと空きswapの両方が閾値を下回ったときだけ動きます。これは論理積、つまりAND条件です。

この条件でswapを大きく増やすと、RAMがほとんど残っていない状態でもswapに余裕がある限り、earlyoomは終了処理を始めません。結果として、プロセスが大量のメモリを使い切るまで保護が遅れ、WSL2のVMそのものが固まる可能性があります。swapは退避先を増やしますが、プロセスの使用量に上限を設ける機能ではありません。

もう一つの落とし穴は、閾値オプションを片側だけ指定した場合の既定値です。SIGTERM用とSIGKILL用の閾値を別々に設定できるツールでは、片方を省略すると意図しない値が補われることがあります。設定例をそのまま貼り付けるのではなく、利用中のバージョンのマニュアルで、メモリとswapそれぞれの判定およびシグナルの閾値を確認してください。

後追いの終了より、先に上限を置く

earlyoomは「不足が起きたあとに誰かを終了させる」仕組みです。一方、cgroupのメモリ制限は「このグループはここまでしか確保できない」という境界を、プロセスの実行前に作ります。暴走する処理があっても、その影響を一つのグループ内に閉じ込められるため、VM全体の生存性を守りやすくなります。

systemdとcgroup v2が利用できる環境なら、ユーザー権限のscopeとして処理を起動できます。

systemd-run --user --scope \
  -p MemoryMax=6G \
  -p MemorySwapMax=2G \
  --collect -- your-command --arg

MemoryMaxはグループのメモリ上限、MemorySwapMaxはswapに退避できる量の上限です。値は処理の性質とWSL2全体の割り当て量から決めます。上限に達した場合、対象scope内のプロセスが終了することはありますが、無制限に膨らんだプロセスがVM全体を巻き込む事態は避けやすくなります。

なお、systemd-run --userが使えるかどうかは、コマンドの存在だけでは判断できません。ユーザーセッションのsystemd、D-Bus、cgroup v2が実際に利用可能かを確認し、失敗時には安全な素通しや明示的なエラーなど、運用に合ったフォールバックを用意します。

複数AIエージェントを並走させる基盤での適用例

複数のAIエージェントを同時に起動する開発基盤では、各CLIを個別のscopeでラップする方法が使えます。起動、再起動、モデル切り替えなど複数の経路がある場合は、経路ごとに制限を実装するのではなく、共通のコマンド生成関数にまとめるのがポイントです。

たとえば、共通関数が次の役割を持つようにします。

1. 設定からメモリ上限とswap上限を読み込む。 2. systemd-runとユーザーセッションが利用可能か確認する。 3. 利用可能なら、同じ制限を付けたscope起動コマンドを返す。 4. 利用できない場合の挙動を、無制限実行ではなく設定で選べるようにする。

この構造なら、起動経路が増えても制限の付け忘れを減らせます。実際の導入では、低い上限でテスト用コマンドを実行し、対象プロセスだけが終了すること、ホスト側のメモリ状況が異常に悪化しないこと、ログに原因が残ることを順に確認します。

対策の優先順位

メモリ枯渇対策は、次の順で考えると整理しやすくなります。

  • 第一に、重い処理をcgroupのMemoryMaxで局所化する。
  • 第二に、earlyoomのメモリ・swap判定とシグナル閾値を意図どおりに調整する。
  • 第三に、必要性を測定したうえでWSL2のメモリ割り当てやswapを見直す。

メモリを増やすだけでは、異常な処理が使える範囲も広がります。上限、観測、終了条件を組み合わせて、失敗しても影響範囲が広がらない構成にすることが大切です。

導入前のチェックリスト

  • cgroup v2とユーザーsystemdが有効か。
  • MemoryMaxMemorySwapMaxの単位・既定値を確認したか。
  • earlyoomがメモリとswapのどの条件で動くか確認したか。
  • すべての起動経路が同じ制限を通るか。
  • 上限到達時に、対象処理の終了とログ記録を確認したか。
  • 制限機構自体が利用できないときのフォールバックを決めたか。

earlyoomは最後の安全網として役立ちます。しかし、システム全体を守る主役にするなら、まずプロセス単位で使えるメモリを制限するほうが、挙動を予測しやすくなります。WSL2で重い処理を並列化するほど、「足りなくなったら殺す」だけでなく「最初から使い過ぎられない」境界を設けることが重要です。