はじめに
ある運用中のサーバーで、謎の現象が発生しました。
定期的に実行されている重いバッチ処理が、途中で「スッ...」と消えてしまうのです。
- アプリケーションのエラーログ:何も出力されていない。
- 例外処理(try-catch):引っかかっていない。
- クラッシュダンプ:残っていない。
「嘘だろ...プロセスが跡形もなく消滅したぞ...?」
まるで完全犯罪。自分のコードに未知のバグがあるのか?フレームワークの不具合か?私は何時間もソースコードを睨みつけ、見当違いのデバッグコードを仕込み続けていました。
しかし、犯人はアプリケーションの外、Linuxカーネルそのものにいたのです。
彼の名は「OOM Killer(Out Of Memory Killer)」。
技術解説:沈みゆく船を守る「冷酷な船長」
LinuxというOSは、メモリを効率よく使うために「実際にある物理メモリ以上のメモリを、プロセスに割り当てる約束(オーバーコミット)」をすることがあります。
これは「全員が一度に全額引き下げることはないだろう」とたかをくくっている銀行のような仕組みです。
しかし、もし複数のプロセスが一気にメモリを要求して、システム全体のメモリが本当に枯渇しそうになったらどうなるか?
そのままではOS全体がフリーズ(システムダウン)してしまいます。
そこで登場するのが OOM Killer です。
彼は、沈みゆく船(OS)を救うため、一番体重の重い乗客(メモリを大量に消費しているプロセス)から容赦なく海へ突き落とします(強制終了:SIGKILL)。
真実を見つけるコマンド
OOM Killerは、アプリケーションに「終了してね」と優しく頼む(SIGTERM)ことはしません。問答無用で首を刎ねます(SIGKILL)。だからアプリ側にはエラーログすら残る暇がないのです。
しかし、OOM Killerは自分が手を下した記録を、システムのログに必ず誇らしげに残しています。
「プロセスが突然消えた」と思ったら、まずはカーネルのメッセージを確認する dmesg を叩きましょう。
# OOM Killerの発動履歴を探す
$ dmesg -T | grep -i oom
またはシステムのログから探します。
# Ubuntu/Debian系の場合
$ sudo grep -i 'killed process' /var/log/syslog
# CentOS/RHEL/Amazon Linuxの場合
$ sudo grep -i 'killed process' /var/log/messages
すると、こんな絶望的な(しかし真実を告げる)1行が見つかるはずです。
Out of memory: Killed process 12345 (java) total-vm:4096000kB, anon-rss:2048000kB...
犯人がわかるという「救い」
自分が書いたコードを疑い続け、泥沼のデバッグにハマっていた私にとって、システムログに刻まれた Out of memory: Killed process の文字は、絶望ではなく「光」でした。
「なんだ、アプリのバグじゃなかったんだ。メモリが足りなかっただけか!」
原因がわかれば、対処はできます。
- アプリのメモリ使用量をチューニングする
- サーバーのメモリ容量を増やす
- スワップ領域を追加する
- OOMスコア(殺されやすさ)を調整する
「プロセスが突然消える」という現象に出くわしたとき、OOM Killerの存在を知らないと、永遠に解決できない迷宮に迷い込むことになります。
もし今後、あなたのアプリが遺書の1つも残さずに姿を消したときは、カーネルのログを覗いてみてください。冷血だけどOSを守ってくれた船長からの、確かなメッセージがそこにあるはずです。