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プロセスは作成されなくなり、余計なゾンビプロセスも発生しなくなります。