概要
Dockerを触っていたらorphan(孤児の意)というプロセスの存在を知ったのでゾンビプロセスとの違いを調べてみた。
ゾンビプロセスとは
ゾンビプロセスとは、処理が終了した子プロセスがプロセステーブルに残り、親プロセスによるwait()システムコールを待っている状態のプロセスである。
Linuxでは、子プロセスが終了しても、親プロセスがwait()(またはwaitpid())を呼び出して終了ステータスを受け取るまで、プロセスの情報はプロセステーブルに保持される。この状態を「ゾンビ状態(defunct)」と呼ぶ。
特徴:
- CPUやメモリなどのシステムリソースは使用しない
- PID(プロセスID)だけが保持される
- ゾンビプロセスが大量に増えると、使用可能なPIDが枯渇し、新しいプロセスを起動できなくなる
ゾンビプロセスの確認方法:
# ps auxでstatがZのもの、またはコマンド末尾がdefunctのもの
ps aux | grep defunct
# もしくは
ps -ef | grep defunct
ゾンビプロセスのkill:
ゾンビプロセス自体は終了済みのため、killコマンドは効かない。親プロセスがwait()を呼び出すか、親プロセス自体をkillすることで解消される。親プロセスがkillされると、孤児プロセスがinitに引き取られてwaitが行われ、プロセステーブルから削除される。
# 親プロセスのPIDを確認(PPIDの欄)
ps -ef | grep defunct
# 親プロセスをkill
kill -9 <親プロセスのPID>
孤児プロセス
孤児プロセスとは、親プロセスがwait()を呼ばずに終了してしまったことで、親を失ったプロセスのことである。
Linuxでは、親プロセスが先に終了した場合、孤児プロセスはinitプロセス(PID: 1)またはサブリーパー(Dockerならtiniなど)に引き取られる。initプロセスが里親となり、定期的にwait()を呼んでゾンビ状態を解消する。
特徴:
- 動作は継続する(ゾンビとは異なりリソースを使用する)
- PIDが1のinitに再ペアレントされる
- Dockerコンテナ内では、PID 1が
initでない場合(shやbashが起動している場合など)、適切にwaitされずゾンビが蓄積しやすい
孤児プロセスの確認方法:
# PPIDが1のプロセスのうちrootでないものを表示
ps -elf | head -1; ps -elf | awk '{if ($5 == 1 && $3 != "root") {print $0}}' | head
孤児プロセスのkill:
kill <PID>
ゾンビプロセスと孤児プロセスの対比
| 項目 | ゾンビプロセス | 孤児プロセス |
|---|---|---|
| 状態 | 終了済み(defunctとして残存) | 実行中 |
| リソース消費 | なし(PIDのみ保持) | あり |
| 親プロセス | 存在するがwait()待ち | 存在しない(initが引き取る) |
| 問題のリスク | PID枯渇 | リソース消費続行 |
| 解消方法 | 親がwait()するか、親をkill | killコマンドで終了 |
Dockerにおける注意点
Dockerコンテナ内のPID 1プロセスが通常のアプリ(node、python等)の場合、signal処理やwait()をデフォルトでは行わないため、ゾンビプロセスが蓄積しやすい。docker runに--initフラグを付けるか、tiniなどのinitプロセスマネージャーを使用することが推奨される。
# docker-compose.ymlでの設定
services:
app:
image: myapp
init: true # tiniをPID 1として使用