TL;DR
chrome(chromium)を起動するときに--no-zygoteオプションを指定すると、
余計なプロセスが立ち上がらなくなりゾンビプロセスも発生しない。
概要
ヘッドレス(--headless)でChromeを立ち上げた場合に、以下のようにゾンビプロセスが溜まってしまうことがあります。
(以下はAWS Lambda内でchromiumを立ち上げた場合)
sbx_use+ 24 0.0 0.0 0 0 ? Z 13:50 0:00 [chromium] <defunct>
sbx_use+ 25 0.0 0.0 0 0 ? Z 13:50 0:00 [chromium] <defunct>
sbx_use+ 53 0.0 0.0 0 0 ? Z 13:50 0:00 [chromium] <defunct>
sbx_use+ 54 0.0 0.0 0 0 ? Z 13:50 0:00 [chromium] <defunct>
sbx_use+ 82 0.0 0.0 0 0 ? Z 13:51 0:00 [chromium] <defunct>
sbx_use+ 83 0.0 0.0 0 0 ? Z 13:51 0:00 [chromium] <defunct>
sbx_use+ 111 0.0 0.0 0 0 ? Z 13:52 0:00 [chromium] <defunct>
sbx_use+ 112 0.0 0.0 0 0 ? Z 13:52 0:00 [chromium] <defunct>
ゾンビプロセスは貴重なPIDを浪費するだけでなく、元のプロセスが開いていたファイルを開きっぱなしにしてしまうなど、
様々な弊害を生み出す元凶となります。
ゾンビプロセスはなぜ発生するのか
そもそも、正常な過程で実行・終了されたプロセスはゾンビ化することはありません。
正常な過程というのは具体的に以下のようなものを言います。
- 親プロセスが
forkする - 子プロセスが
exitする - 親プロセスが子プロセスを
waitpidする(重要)
waitpidというのはいわば不要になったプロセスの回収作業であり、
これを忘れると子プロセスに関するリソースを開放することができずゾンビ化します。
逆にちゃんとwaitpidしてやればプロセスがゾンビ化することはありません。
子プロセスがwaitpidされることなくfork元のプロセス(親プロセス)が終了してしまった場合、
ゾンビ化した子プロセスは行き場を失ってしまいます。
しかし、UNIXシステムには特別なプロセスであるinitプロセス(PIDが1のプロセス)が存在し、
initプロセスは行き場を失ったゾンビプロセスを子プロセスとして取り込むようになっています。
一般的なinitプロセスは取り込んだゾンビプロセスに対処するため、
waitpidをゾンビプロセスに対して実行するようなロジックが組み込まれています。
しかしながら、initプロセスがおバカな場合、
即ちinitとして利用されることを想定されていないプロセスがPID1で立ち上がってしまっている場合、
取り込んだゾンビプロセスをそのまま放置してしまうためゾンビが溜まり放題になってしまうのです。
これはコンテナ環境では割と起こりうる話だったりします。
というのも、コンテナ内では一番最初に立ち上がったプロセスのPIDが1になるためです。
コンテナで実行されるプロセスのほとんどはinitとして動かされることを想定されていないでしょうから、
必然的にゾンビプロセスも世話されること無く放置されてしまうということです。
なお、あくまで本質的な解決方法ではないのですが、
一番最初に起動するプロセスをコントロールできる場合には、
ちゃんとゾンビプロセスを回収してくれるプロセスを最初に起動することで解決できます。
例えば、コンテナ用の擬似initのひとつであるtiniなどです。
https://github.com/krallin/tini
詳しい使い方はここでは割愛します。
AWS Lambdaなどのいわゆるサーバーレスなプラットフォームを利用する場合は
最初に起動するプロセスを変更できない場合が有り、
この場合はtiniを使った解決方法を適用することはできません。
ゾンビプロセスって後から叩き潰せば良いんじゃ?
ゾンビプロセスが発生したなら後から手動でkillしてやれば良いような気がしてしまいますが、
ゾンビプロセスはそもそも既に死んだ(exitした)後のプロセスであるためいくらkillしようとしても全く意味がありません。
実際にkill -9 [ゾンビのPID]を実行してみると全く効かないことが分かると思います。
ゾンビプロセスというのは溜まってしまうともはやシステムを再起動することでしか消すことができないため、
非常に厄介な存在でありできる限り生み出さないようにする必要があるのです。
(特にPID1のプロセスがinitとしての役割を果たせない場合)
ゾンビプロセスを発生させないために
元はと言えば自分で生んだ子プロセスをちゃんと回収しないchromeが悪いわけなので、
chromeがちゃんと子プロセスを回収する(ないし余計なプロセスを生まないようにする)ように仕向けることを第一に考えます。
そもそも、chromeはわざわざ好き好んでゾンビプロセスを作っているわけではありません。
本来常駐させようと思っていたプロセスが何故か終了してしまったがためにゾンビ化してしまったのです。
この常駐プロセスはZygoteと呼ばれ、プロセスのセットアップ時間の短縮やライブラリ依存関係の維持などに用いられているようです。
https://chromium.googlesource.com/chromium/src/+/master/docs/linux/zygote.md
普段は当然役に立っているのでしょうが、勝手に死なれてゾンビ化されると迷惑なだけなので、
そもそもZygoteプロセス自体を起動しないようにするのが一番良さそうです。
Zygoteを起動しないようにするには--no-zygoteオプションをchromeに渡します。
これによりZygoteプロセスは作成されなくなり、余計なゾンビプロセスも発生しなくなります。