Ответ
В Docker-контейнере процесс с идентификатором PID 1 — это самый первый (корневой) процесс, который запускается при старте контейнера. Он определяется инструкциями ENTRYPOINT или CMD в Dockerfile.
Ключевая роль PID 1
Процесс с PID 1 имеет особое значение в Linux-системах:
- Родительский процесс: Он становится родителем для всех дочерних процессов, запущенных в контейнере.
- Обработка сигналов: Он отвечает за получение и обработку системных сигналов, таких как
SIGTERM, который отправляется командойdocker stop. Корректная обработка сигнала позволяет приложению завершиться штатно. - Завершение контейнера: Когда процесс с
PID 1завершается, контейнер останавливается.
Проблема и Best Practices
Частая ошибка — запуск приложения через shell (shell form), что делает сам shell процессом с PID 1, а не ваше приложение. Shell не всегда корректно передает сигналы дочернему процессу, что может привести к "зомби-процессам" и некорректному завершению работы.
1. Используйте exec форму
Это лучший способ, чтобы ваше приложение стало PID 1.
# НЕПРАВИЛЬНО (shell form): /bin/sh становится PID 1
CMD service myapp start
# ПРАВИЛЬНО (exec form): 'myapp' становится PID 1
CMD ["myapp", "--foreground"]
2. Используйте легковесный init-процесс
Для более сложных случаев, когда приложение само не умеет корректно управлять дочерними процессами, рекомендуется использовать init-систему, например tini. Она становится PID 1, корректно обрабатывает сигналы и собирает "зомби-процессы".
Docker имеет встроенную поддержку tini через флаг --init:
docker run --init my-image
Или можно встроить tini прямо в Dockerfile:
FROM alpine:3.15
# Устанавливаем tini
RUN apk add --no-cache tini
# Копируем приложение
COPY ./myapp /usr/local/bin/myapp
# Указываем tini как точку входа. Он запустит наше приложение.
ENTRYPOINT ["/sbin/tini", "--"]
# Указываем команду для запуска
CMD ["myapp"]
Правильное управление процессом PID 1 — ключ к созданию стабильных и предсказуемых Docker-контейнеров.
Ответ 18+ 🔞
Ага, слушай, вот эта вся история с PID 1 в докере — это, блядь, целая наука, а не просто цифра! Представь себе: ты запускаешь контейнер, а там, как в старом анекдоте, главный — тот, кто первый встал, того и тапки. Вот этот PID 1 — он и есть тот самый первый, коренной, мамкин начальник всего, что внутри происходит.
И вот в чём, сука, прикол: в обычном Линуксе этот PID 1 — это священный init или systemd, который за всеми прибирается, сигналы ловит и зомби-процессы хоронит. А в контейнере? В контейнере этим богом становится твоё приложение, которое ты в ENTRYPOINT или CMD прописал. И если оно, мудак, не умеет сигналы обрабатывать — пиши пропало. Контейнер будет висеть, как будто его в рот ебло, когда ты docker stop делаешь.
Где собака, сука, зарыта?
Основная засада — это когда ты пишешь команду как попало. Смотри:
# Вот так НЕ НАДО, ёпта! Тут PID 1 станет /bin/sh, а твоё приложение — его дитё.
# И этот шелл — он тупой как пробка, сигналы твоему ребёнку не передаст.
CMD service myapp start
# А вот так — О, ДА! Твоё приложение сразу станет главным паханом (PID 1).
# И сигнал на остановку придёт прямо ему в ебальник.
CMD ["myapp", "--foreground"]
Видишь разницу? В первом случае ты запускаешь через оболочку, а она, хитрая жопа, сигналы проглатывает. Во втором — напрямую, exec формой. Это как говорить с начальником лично, а не через его секретаршу-стерву.
А если приложение — полный пидарас и само не справляется?
Бывает же, что приложение тупое, своих детей-процессов не контролирует, зомби по углам копятся... Удивление пиздец! На этот случай есть лайфхак — легковесный init-процесс. Самый популярный — tini. Это такой маленький надзиратель, который становится PID 1 вместо твоего приложения, а приложение запускает как своего подопечного. И этот надзиратель — мастер на все руки: сигналы ловит, зомби утилизирует, порядок наводит.
Включить его проще простого, прямо при запуске:
docker run --init my-image
Или, если хочешь на века, прямо в Dockerfile встроить:
FROM alpine:3.15
# Ставим нашего надсмотрщика, tini
RUN apk add --no-cache tini
# Копируем наше кривое, но любимое приложение
COPY ./myapp /usr/local/bin/myapp
# Говорим: "tini, ты теперь главный! А запустишь ты вот это..."
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["myapp"]
Короче, если коротко: с PID 1 не шути. Либо запускай приложение напрямую через exec форму, либо подсовывай ему в помощь tini. И тогда твой контейнер будет останавливаться по-человечески, а не висеть, как манда с ушами, игнорируя все твои docker stop. Волнение ебать, но это работает.