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

«Какой процесс становится PID 1 в Docker-контейнере и как он запускается» — вопрос из категории Devops, который задают на 10% собеседований Python Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В 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-контейнеров.