PID 1 в контейнере: почему обычный процесс внезапно становится ответственным за orphan processes
В обычной системе PID 1 - это init/systemd. В контейнере им может оказаться что угодно:
docker run --rm alpine ps
Например:
PID COMMAND
1 /app/server
7 worker
12 helper
И здесь у PID 1 есть особая обязанность, о которой часто забывают.
Если процесс становится orphan, ядро должно перепривязать его к другому процессу. Внутри PID namespace таким процессом становится namespace init, то есть PID 1.
Упрощённо:
PID 1
└── worker
└── child
worker завершается раньше child:
PID 1
└── child
child становится orphan и теперь его parent - PID 1.
Это можно увидеть через:
ps -eo pid,ppid,stat,comm
Ключевой момент: PID 1 в Linux получает специальное поведение не просто из-за номера. Процессы, работающие как namespace init, становятся точкой усыновления orphan-процессов.
Но есть ещё более важная часть.
PID 1 должен корректно обрабатывать SIGCHLD и делать wait()/waitpid() для завершившихся дочерних процессов. Иначе завершённые процессы могут оставаться zombie:
PID 1
├── worker
├── worker <defunct>
├── worker <defunct>
└── worker <defunct>
Проверить это внутри контейнера:
ps -eo pid,ppid,stat,comm
или:
grep -a '^State:' /proc/*/status 2>/dev/null
Именно поэтому приложение, которое прекрасно работает как обычный процесс на хосте, может вести себя хуже в контейнере, если его запускают напрямую как PID 1.
Например:
docker run myapp
Если myapp - не полноценный init и не умеет корректно reaping’ить дочерние процессы, ответственность за них всё равно никуда не исчезает.
Для контейнеров поэтому используют специальные init-решения или запускают приложение через init-процесс. В Docker есть встроенный вариант:
docker run --init myapp
Тогда между runtime и приложением появляется небольшой init, который занимается в том числе orphan/zombie processes.
И есть важная граница: PID 1 контейнера отвечает только за процессы своего PID namespace. Он не становится каким-то глобальным init всего хоста.
То есть контейнер получает собственное дерево процессов:
HOST PID namespace
systemd
└── container process
└── ...
CONTAINER PID namespace
PID 1
├── app
├── worker
└── orphan → PID 1
Поэтому PID 1 в контейнере - это не просто «процесс с красивым номером». Это специальная точка жизненного цикла процессов внутри конкретного PID namespace.