OOM Killer に関連するカーネルパラメータまとめ
Written by @ryysud Oct 8, 2018 17:26 · 2390 words · 5 minutes read
TL;DR
- OOM Killer は /proc/${pid}/oom_score の値が大きいものから kill 対象を決定する
- oom_score は /proc/${pid}/oom_score_adj と /proc/${pid}/oom_adj とその他プロセス情報を元に算出される
- oom_score_adj = -1000 もしくは oom_adj = -17 とすると OOM Killer の kill 対象から除外することが出来る
OOM Killer とは
Linux カーネルから提供されている機能の一つで、OS のメモリー領域が枯渇することを避けるために、プロセスを強制終了する仕組みが OOM Killer です。ただし、サービスとして稼働させるために必要不可欠なプロセスもあるので、どのプロセスをどのようなルールで強制終了するかは、設定により制御することが可能です。
OOM Killer でプロセスが kill される様子
以下スペックのサーバーで実際に OOM Killer でプロセスが kill される様子を確認していきます。
- CentOS 7.5
- CPU 2core
- Memory 2GB
$ cat /etc/redhat-release
CentOS Linux release 7.5.1804 (Core)
$ grep processor /proc/cpuinfo | wc -l
2
$ free -h
total used free shared buff/cache available
Mem: 3.7G 97M 3.3G 16M 337M 3.4G
Swap: 0B 0B 0B
stress コマンドを使ってメモリに負荷をかけてみると、stress コマンドの worker process である pid 1477 のプロセスに SIGKILL シグナルが投げられ kill されたことで、stress プロセス全体がエラーで終了することがわかります。
# stres コマンドをインストール
$ sudo yum install -y stress
# 4プロセスがそれぞれ1GBのメモリを確保する(積んである実メモリ相当の負荷)
$ stress --vm 4 --vm-bytes 1G --vm-hang 0
stress: info: [1474] dispatching hogs: 0 cpu, 0 io, 4 vm, 0 hdd
stress: FAIL: [1474] (415) <-- worker 1477 got signal 9
stress: WARN: [1474] (417) now reaping child worker processes
stress: FAIL: [1474] (451) failed run completed in 2s
実際にプロセスが kill されたので /var/log/messages を確認すると、OOM Killer により stress コマンドの worker process である pid 1477 のプロセスが kill されたことがわかります。
# カーネルダンプでログが多いので一部抜粋
$ tail -f /var/log/messages
Oct 8 09:27:09 ip-172-31-27-18 kernel: stress invoked oom-killer: gfp_mask=0x280da, order=0, oom_score_adj=0
Oct 8 09:27:09 ip-172-31-27-18 kernel: Out of memory: Kill process 1477 (stress) score 270 or sacrifice child
Oct 8 09:27:09 ip-172-31-27-18 kernel: Killed process 1477 (stress) total-vm:1055888kB, anon-rss:1048656kB, file-rss:0kB, shmem-rss:0kB
このように OS のメモリー領域が枯渇することを避けるために、OOM Killer がプロセスを強制終了する役割を担っていることがわかりました。
OOM Killer に関連するカーネルパラメータ
以下コマンドで、目ぼしいパラメータを洗い出した上で列挙していきます。なお、オーバコミット関連のカーネルパラメータ vm.overcommit_* は OOM Killer を制御する上で直接的には関係がないので、今回は割愛します。
$ sysctl -a | grep oom
$ ls -l /proc/* | grep oom
また、OS によっても微妙にカーネルパラメータが異なる可能性があるので、今回は RedHat系 と Debian系 にフォーカスしてまとめていきたいと思います。なお RedHat系 は CentOS 7.5 で Debian系 は Debian GNU/Linux 9 (stretch) を使用して確認していきます。
vm.oom_dump_tasks
OOM Killer が実行される際に、システム全体のタスク・ダンプ (カーネルスレッドを除く) を生成するかを制御するパラメータ。
値 | 説明 | RedHat系デフォルト値(CentOS 7.5) | Debian系デフォルト値(Debian GNU/Linux 9) |
---|---|---|---|
0 | ダンプ情報を出力しない | ||
1 | ダンプ情報を出力する | ◯ | ◯ |
vm.oom_kill_allocating_task
OOM Killer が実行される際に、メモリを獲得しようとしたプロセスのみを kill 対象とするかを制御するパラメータ。
値 | 説明 | RedHat系デフォルト値(CentOS 7.5) | Debian系デフォルト値(Debian GNU/Linux 9) |
---|---|---|---|
0 | 起動済みプロセスを kill 対象とする | ◯ | ◯ |
1 | メモリーを獲得しようとしたプロセスを kill 対象とする |
vm.panic_on_oom
OOM Killer が実行される際に、カーネルパニックを起こさせるかを制御するパラメータ。カーネルパニック関連のカーネルパラメータである kernel.panic と組み合わせることで、OOM Killer 発生時に OS 再起動をキックしたりすることも可能です。
値 | 説明 | RedHat系デフォルト値(CentOS 7.5) | Debian系デフォルト値(Debian GNU/Linux 9) |
---|---|---|---|
0 | カーネルパニックしない | ◯ | ◯ |
1 | カーネルパニックする。但し cgroup 制限により物理メモリがまだ残っている場合にはカーネルパニックしない。 | ||
2 | 必ずカーネルパニックする |
プロセス毎に適用できるパラメータ
カーネルパラメータとは別に、プロセス毎に付与されるパラメータがあるので、そちらも列挙していきます。
/proc/${pid}/oom_adj
OOM Killer によって kill される優先度を制御するパラメータ。なお、子プロセスは親プロセスの値を引き継ぐ形となります。-17 から 15 までの整数値を格納することが可能となっており、値が大きいものから kill 対象とされていきます。但し -17 は例外的な値で -17 を適用することで OOM Killer の kill 対象から除外することが出来ます。
値 | 説明 | RedHat系デフォルト値(CentOS 7.5) | Debian系デフォルト値(Debian GNU/Linux 9) |
---|---|---|---|
1 から 15 | |||
0 | 初期値 | ◯ | ◯ |
-16 から -1 | |||
-17 | OOM Killer によって kill されない |
Linux 2.6.36 以降ではこちらのパラメータは非推奨となっており、後述の /proc/${pid}/oom_score_adj が推奨されています。
/proc/${pid}/oom_score_adj
/proc/${pid}/oom_adj と同じで OOM Killer によって kill される優先度を制御するパラメータ。-1000 から 1000 までの整数値を格納することが可能となっており、値が大きいものから kill 対象とされていきます。但し -1000 は例外的な値で -1000 を適用することで OOM Killer の kill 対象から除外することが出来ます。
値 | 説明 | RedHat系デフォルト値(CentOS 7.5) | Debian系デフォルト値(Debian GNU/Linux 9) |
---|---|---|---|
1 から 1000 | |||
0 | 初期値 | ◯ | ◯ |
-999 から -1 | |||
-1000 | OOM Killer によって kill されない |
/proc/${pid}/oom_score
OOM Killer が kill する対象を定めるパラメータ。/proc/${pid}/oom_adj 及び /proc/${pid}/oom_score_adj の値を元に算出され、値が大きいものから kill 対象となっていきます。
細かい計算式は追えていませんが、実行時間やnice値などの値も使って算出されているようです。なお /proc/${pid}/oom_adj に -17 もしくは /proc/${pid}/oom_score_adj に -1000 を適用すると、/proc/${pid}/oom_score は 0 となる式のようです。
すなわち /proc/${pid}/oom_score が 0 の場合には、そのプロセスは OOM Killer の kill 対象から除外されていることになります。
こちらの計算式に興味がある人はソースを見るなり、以下記事を眺めると理解が進むかもしれません。(私は心が折れました…。
まとめ
潤沢なリソースでサービスを運用していると、意外と盲点になるかもしれませんが、OS が機能するように OOM Killer が縁の下の力持ちな役割を担っていることがわかりました。また、今回で自らのカーネル知識の浅さが露呈したので、今後も学習していこうと思います。