Skip to content
Latchkey

CIにおけるOOM killとは?解説

OOM killとは、マシンがメモリ不足になったためにOSがプロセスを強制終了することで、キャッチもクリーンアップもできないSIGKILLを使います。

メモリに制約のあるCI runnerでは、利用可能な量を超えて確保するbuildやテストのプロセスは、丁寧なエラーを受け取れません。カーネルのout-of-memory killerが介入し、メモリを取り戻すために最も重いプロセスを終了させます。その結果は、トレースバックのない突然の失敗で、多くのチームが初めてのときに戸惑います。

OOM killerの仕組み

システム(またはcgroupのメモリ制限)がメモリ不足になると、カーネルはただちにいくらか解放する必要があります。カーネルはプロセスを、通常は最大のものを選び、SIGKILL(シグナル9)を送ります。SIGKILLはキャッチ、ブロック、ハンドルができないため、プロセスはログを残したりクリーンアップしたりする間もなく即座に死にます。

どう現れるか

通常、むき出しの「Killed」という単語、137(128 + 9)というexit code、そしてスタックトレースなしが見られます。プロセスがそれを生成する前に死んだからです。コンテナでは「OOMKilled」というステータスも見られることがあります。通常のエラーがないこと自体が手がかりです。

なぜCIでよく起こるのか

  • runnerのメモリは固定でしばしば控えめです。buildは平均使用量をはるかに超えてスパイクします。
  • 並列テストworkerはそれぞれメモリのコピーを保持し、ピーク使用量を倍増させます。
  • 言語ランタイム(JVM、Node)は大きなheapを予約し、制限を超えることがあります。
  • コンテナのメモリ制限はホストより低いことがあり、その制限が先にヒットします。

一時的か決定論的か?

OOM killはどちらにもなり得ます。buildが本当にrunnerの持つ以上のメモリを必要とするなら、それは決定論的で、より大きなrunnerかピーク使用量の低減が必要です。しかしrunnerが一瞬だけ負荷が高かった、あるいはスパイクがわずかだった場合は、新しいrunnerでのリトライがしばしば成功し、一時的なものになります。

Latchkeyの視点

Latchkeyの自己修復managed runnerは、out-of-memory killを機械的な失敗クラスとして検出し、影響を受けたjobを自動的にリトライします。そのため一度きりのわずかなメモリスパイクでbuildが失敗することはありません。runnerに対して本当に大きすぎるbuildは依然として表面化するため、適切にサイズ調整できます。

重要なポイント

  • OOM killは、キャッチ不能なSIGKILLでカーネルがプロセスを終了させることです。
  • 「Killed」、exit code 137、そしてスタックトレースなしとして現れます。
  • 並列workerや大きなランタイムのheapが、CIでこれをよく起こします。
  • わずかなスパイクは一時的でリトライ可能ですが、本当の不足には大きなrunnerが必要です。

関連ガイド