2022/06/17(金)OOM Killerでサーバが死んだ

ある日突然、Webサーバ(Apache)が死亡したときのメモと、実施した対策のまとめ。

OOM Killerとは

メモリが足りなくなったときに、負荷が高そうなプロセスを一撃必殺してくれるLinuxの機能です。

ログ

長いので別ファイルに

この中で、rssが実消費メモリ(KB)なのですが、トータルしても209MBしかありません。ps auxの値(KB)と異なり、OOM Killerプロセス一覧数値はすべてページ単位(4kB単位)なので、836MB。このマシンの実メモリ1GBなのでほとんど無くなっています。

スワップもswapents(Anonymous swap entries)の合計が960589、エントリー1つあたり4KBですので、3.7GB。つまりスワップは完全に食いつぶしてる。

色々調べると、OOM発動時はこの辺を見ると良いらしい。

[7934037.757829] Node 0 DMA: 43*4kB (UME) 14*8kB (UME) 12*16kB (UE) 13*32kB (UME) 2*64kB (UE) 7*128kB (UME) 4*256kB (UME) 3*512kB (UME) 0*1024kB 0*2048kB 0*4096kB = 4476kB
[7934037.762116] Node 0 DMA32: 258*4kB (UME) 252*8kB (UME) 576*16kB (UME) 325*32kB (UME) 105*64kB (UME) 20*128kB (UME) 8*256kB (UE) 3*512kB (UME) 5*1024kB (UME) 0*2048kB 1*4096kB (M) = 44744kB

どれくらいのサイズの連続したメモリをいくつ確保できるかという表示ですが、一番大きいもので「DMA側が512kBを3つ」「DMA32側が4096kBを1つ」ということで、メモリの断片化してますし、そもそも両方合わせて48MBぐらいしか空きメモリがない。

OOM Killerが発生して当然の状況と言えます。

なぜApacheが死んだのか

OOM Killerが発動したのは、sys.fcgiというFastCGI(Apacheの子)プロセスなのですが、なぜかApache自体が死亡しておりました。daemon.logを見ますと、こんな感じ。

systemd[1]: apache2.service: A process of this unit has been killed by the OOM killer.
systemd[1]: apache2.service: Failed with result 'oom-kill'.
systemd[1]: apache2.service: Consumed 1h 21min 18.835s CPU time.

Apacheの子プロセスがOOM Killerされたので、systemdが善きに計らってApache自体を終了させた模様。……やめてくれー(苦笑)

子プロセスが死んでもApacheを生かす

メモリ食いつぶしcgiを作って実験してみたのですが、通常のcgiなら問題なくFastCGIのときだけこの問題が起きるようです。systemdの設定を変更して、Apacheを巻き添えにしないよう変更します。

# Debianの場合, rootでの作業
cd /etc/systemd/system
mkdir apache2.service.d
vi apache2.service.d/oom.conf
systemctl daemon-reload

# oom.confの中身
[Service]
OOMPolicy=continue

/etc/system/systemd/apache2.service.d/oom.conf がきちんと読み込まれてるか、設定を確認してください。

systemctl cat apache2

この状態でメモリ食いつぶしfcgiを実行してみましたが、無事(?)にfcgiだけプロセスkillされました。

その他の対策

  • Swap領域を3.7GBから10GBに増強。
  • sys.fcgiで不要になったメモリを開放することが難しかったので、メモリ使用量(/proc/[pid]/statusのVmHWM)が一定量を超えたら、処理終了後に自動でプロセスを終了するようにした。
  • mod_fcgidの設定を変更して、一定時間使われないプロセスはすべて終了するようにした。
    FcgidMinProcessesPerClass	0
    FcgidProcessLifeTime		600
    FcgidIdleScanInterval		120
    

スワップは実RAMの倍(この場合2GB)という定説を昔読んだ気がしますが、「未開放メモリ→スワップアウト」というケースを考えると、多いに越したことはないかもしれません。

本当はApache側でメモリ制限を実施できたら楽なのですが、mod_fcgidの子プロセスには RLimitMem とか効かないんですよね……。

参考サイト