OOM Killer:なぜLinuxはMySQLを「強制終了」させるのか?その理由と対策

Linux tutorial - IT technology blog
Linux tutorial - IT technology blog

サービスが突然「姿を消す」とき

月曜日の朝、ダッシュボードを確認すると、MySQLやRedisがいつの間にか停止していることに気づきます。アプリケーションのログはきれいで、エラーメッセージ一行すらありません。再起動すれば正常に動きますが、数時間後にはまた同じことが繰り返されます。

経験の浅いシステム管理者の多くは、コードのデバッグやアプリの設定確認に一日を費やしてしまいがちです。しかし実際には、犯人はオペレーティングシステム(OS)層にいます。Linuxがシステムを救うために、密かにプロセスを「処刑」したのです。この命令を実行したのが OOM Killer (Out of Memory Killer) です。

この記事では、OOM Killerの正体を突き止め、サーバー上の最も重要なプロセスを守るための「鎧」を装備する方法を紹介します。

なぜLinuxはアプリケーションを強制終了させるのか?

問題の根源は Overcommit Memory という仕組みにあります。Linuxカーネルは、アプリケーションが実際の物理メモリ量よりも多くのメモリを要求(割り当て)することを許可しています。これは、アプリケーションが登録したメモリをすべて同時に使い切ることは稀であるという前提に基づいています。

例えば、サーバーのRAMが8GBであっても、アプリが登録したメモリの合計が12GBに達することがあります。すべてのアプリが一斉にリソースを消費したり、特定のアプリでメモリリークが発生したりすると、RAMとスワップ(Swap)が枯渇します。システム全体のハングアップ(Kernel Panic)を防ぐために、OOM Killerが起動します。そして、メモリを即座に解放するために「犠牲者」を選んで強制終了させます。

ターゲット選定のアルゴリズム

Linuxは無作為に選ぶわけではありません。oom_score(0から1000の値)という指標を算出します。スコアが高いほど、「断頭台」に送られる可能性が高くなります。影響を与える要因は以下の通りです:

  • メモリ使用率: RAMの90%を消費しているアプリは、間違いなく最優先のターゲットになります。
  • 生存時間: 長時間稼働しているプロセスは、新しく生成されたプロセスよりも優先的に保持される傾向があります。
  • 権限: root権限で実行されているプロセスは、一般ユーザーよりもわずかに有利です。
  • oom_score_adjの設定: これはユーザーが介入できる変数です。

サービスがOOM Killerに終了させられたか確認する方法

OOM Killerは外部から作用するため、アプリケーションがログを記録する暇がないことがほとんどです。システムログから証拠を探す必要があります。

1. dmesgによる追跡

これはカーネルバッファからのメッセージを確認する最も速いツールです。以下のコマンドを実行してください:

dmesg -T | grep -i "out of memory"

-T パラメータはタイムスタンプを実時間に変換します。もし “Killed process [PID] (mysqld)” という行が見つかれば、犯人が特定できたことになります。

2. journalctlの使用

Ubuntu 22.04やCentOS 7/8などの最新のOSでは、以下のコマンドで詳細を確認できます:

journalctl -xe | grep -i oom-killer

典型的な「死刑宣告」は以下のように表示されます:

[Oct 25 14:30:02] Out of memory: Killed process 1234 (mysqld) total-vm:2048500kB, anon-rss:1500400kB, oom_score_adj:0

anon-rss の値は、プロセスが強制終了された時点で実際に占有していたRAMの量(約1.5GB)を示しています。

プロセスに「免死鉄券(不死身の守り)」を与える方法

データベースを何としても守りたい場合は、/proc/[PID]/oom_score_adj ファイルに介入します。この値は -1000(絶対に殺さない)から 1000(優先的に殺す)の範囲で設定できます。

一時的な設定

MySQLのPIDが1234であると仮定して、以下のコマンドを実行します:

echo -1000 > /proc/1234/oom_score_adj

この方法簡単ですが、PIDが変わるため、サービスを再起動するとすぐに無効になります。

Systemdによる永続的な設定

これが最もプロフェッショナルな方法です。サービスのユニットファイル(例:mysql.service)を編集します:

sudo systemctl edit mysql

以下の行を追加します:

[Service]
OOMScoreAdjust=-1000

その後、デーモンをリロードして変更を適用します:

sudo systemctl daemon-reload
sudo systemctl restart mysql

実践的なアドバイス:乱用は禁物

すべてのサービスに -1000 を設定するのは致命的な間違いです。OOM Killerはサーバーを守るための最後の砦です。MySQLが深刻なメモリリークを起こしているのに強制終了を禁止すると、システム全体がフリーズしてしまいます。そうなると、リモートで対処するためのSSH接続すらできなくなります。

その場しのぎの対策だけでなく、以下のステップを実行しましょう:

  1. アプリケーションのRAM制限: MySQLの innodb_buffer_pool_size を、全RAMの約60-70%に設定します。
  2. スワップ(Swap)の設定: スワップはRAMより低速ですが、メモリが急増したときにシステムがショックを受けないようにするための安全なバッファとなります。
  3. 監視(Monitoring): PrometheusやZabbixを使用して、RAM使用率が85%を超えたときにアラートが飛ぶように設定します。

まとめ

OOM Killerはバグではなく、保護機能です。ログの読み方を理解し、oom_score_adj を調整することで、システムをより高度に制御できるようになります。次回、アプリケーションが突然消えたときは、慌ててコードをチェックする前に、まずは dmesg を確認しましょう!

Share: