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が有効か。
MemoryMaxとMemorySwapMaxの単位・既定値を確認したか。
- earlyoomがメモリとswapのどの条件で動くか確認したか。
- すべての起動経路が同じ制限を通るか。
- 上限到達時に、対象処理の終了とログ記録を確認したか。
- 制限機構自体が利用できないときのフォールバックを決めたか。
earlyoomは最後の安全網として役立ちます。しかし、システム全体を守る主役にするなら、まずプロセス単位で使えるメモリを制限するほうが、挙動を予測しやすくなります。WSL2で重い処理を並列化するほど、「足りなくなったら殺す」だけでなく「最初から使い過ぎられない」境界を設けることが重要です。