Какой процесс становится PID 1 в Docker-контейнере и как он запускается

Ответ

В Docker-контейнере процесс с идентификатором PID 1 — это самый первый (корневой) процесс, который запускается при старте контейнера. Он определяется инструкциями ENTRYPOINT или CMD в Dockerfile.

Ключевая роль PID 1

Процесс с PID 1 имеет особое значение в Linux-системах:

  1. Родительский процесс: Он становится родителем для всех дочерних процессов, запущенных в контейнере.
  2. Обработка сигналов: Он отвечает за получение и обработку системных сигналов, таких как SIGTERM, который отправляется командой docker stop. Корректная обработка сигнала позволяет приложению завершиться штатно.
  3. Завершение контейнера: Когда процесс с 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. Волнение ебать, но это работает.