Jump to content
View in the app

A better way to browse. Learn more.

T.M.I IThub

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.

Введение

Корректный запуск процессов внутри контейнера — одна из ключевых тем при разработке Docker-образов. Формально всё описано в документации Docker, однако на практике регулярно возникают неоднозначные ситуации:

  • контейнер не останавливается корректно;

  • сигналы не доходят до приложения;

  • появляются zombie-процессы;

  • PID 1 ведёт себя неожиданно.

В этой статье разберём:

  1. Разницу между ENTRYPOINT и CMD.

  2. Отличие exec и shell форм.

  3. Почему критически важно, какой процесс имеет PID 1.

  4. Как правильно писать docker-entrypoint.sh.

  5. Когда и зачем использовать tini.

Материал ориентирован на практическое применение и реальные сценарии.


1. ENTRYPOINT и CMD: фундаментальная разница

В Dockerfile существуют две директивы для запуска процессов:

  • ENTRYPOINT

  • CMD

Обе участвуют в формировании итоговой команды запуска контейнера, но выполняют разные роли.

Логическая модель

Можно представить их так:

ENTRYPOINT + CMD = финальная команда контейнера

Рекомендуемая практика

  • ENTRYPOINT — фиксированная команда (исполняемый файл или скрипт).

  • CMD — аргументы по умолчанию, которые можно переопределить.


Exec-форма и Shell-форма

Docker поддерживает два синтаксиса.

1️⃣ Exec-форма (рекомендуется)

ENTRYPOINT ["/bin/ping"]
CMD ["it-lux.ru"]

Особенности:

  • Не используется shell.

  • Нет подстановки переменных.

  • Процесс запускается напрямую.

  • Корректная обработка сигналов.

После сборки:

docker run ping

Внутри контейнера выполнится:

/bin/ping it-lux.ru

Переопределение аргументов:

docker run ping google.com

Теперь выполнится:

/bin/ping google.com

Это правильная архитектура: один образ — разные параметры запуска.


2️⃣ Shell-форма (менее предпочтительна)

ENTRYPOINT ping it-lux.ru

Фактически Docker запустит:

/bin/sh -c "ping it-lux.ru"

Минусы:

  • Появляется промежуточный shell.

  • Сигналы могут не дойти до целевого процесса.

  • PID 1 становится shell.

Shell-форма допустима, но требует понимания последствий.


2. Проблема PID 1

В Linux процесс с PID 1 — особый.

Особенности:

  • Он не имеет обработчиков сигналов по умолчанию.

  • Он ответственен за "усыновление" осиротевших процессов.

  • Он должен корректно обрабатывать SIGTERM.

Docker при остановке контейнера выполняет:

docker stop → отправляет SIGTERM → PID 1

Если PID 1:

  • не обрабатывает сигнал,

  • не передаёт его дочерним процессам,

то контейнер завершится некорректно (force kill через SIGKILL спустя timeout).


3. Ошибка с docker-entrypoint.sh

Типичный пример:

FROM centos:7

COPY docker-entrypoint.sh /usr/bin
ENTRYPOINT ["/usr/bin/docker-entrypoint.sh"]

Содержимое:

#!/bin/bash
ping ya.ru

Что происходит?

PID 1 — это:

/bin/bash /usr/bin/docker-entrypoint.sh

А ping — дочерний процесс.

При docker stop:

  • SIGTERM получает bash

  • bash может не передать сигнал дальше

  • ping зависает

  • появляются zombie-процессы

Это некорректная архитектура контейнера.


4. Правильное решение — exec

В bash существует встроенная команда exec.

Она:

  • заменяет текущий процесс

  • передаёт ему PID

  • не создаёт дополнительный уровень

Правильный вариант:

#!/bin/bash
exec ping ya.ru

Теперь:

PID 1 → ping

Контейнер завершится корректно.


5. Использование CMD внутри entrypoint

Более гибкий вариант:

ENTRYPOINT ["/usr/bin/docker-entrypoint.sh"]
CMD ["ya.ru"]

Скрипт:

#!/bin/bash

# подготовительные действия

set -- ping "$@"
exec "$@"

Разбор:

  • $@ — все аргументы контейнера.

  • set -- — формирует новую команду.

  • exec "$@" — запускает её как PID 1.

Запуск:

docker run ping google.com

Результат:

PID 1 → ping google.com

Это production-подход.


6. Когда одного exec недостаточно

Теперь усложним сценарий.

Допустим, запускается:

  • Jenkins

  • Apache

  • Zabbix server

Такие системы активно создают дочерние процессы.

Примеры:

  • Jenkins

  • Zabbix

  • Apache HTTP Server

Если дочерние процессы:

  • завершаются некорректно,

  • остаются "осиротевшими",

то PID 1 должен их "подчищать".

Но большинство приложений:

  • не реализуют init-поведение,

  • не умеют корректно reaping zombie-процессов.


7. Решение — tini

Здесь используется tini.

  • Минималистичный init для контейнеров.

  • Корректно проксирует сигналы.

  • Убирает zombie-процессы.

  • Работает как PID 1.

Название — это "init" наоборот.

Как подключить tini

Пример Dockerfile:

FROM debian:stable

RUN apt-get update && apt-get install -y tini

ENTRYPOINT ["/usr/bin/tini", "--"]
CMD ["your-app"]

Теперь:

PID 1 → tini
PID 7 → your-app

Что делает tini:

  1. Получает SIGTERM.

  2. Передаёт сигнал дочернему процессу.

  3. Reap'ит zombie-процессы.

  4. Корректно завершает контейнер.

Это production best practice.


8. Почему bash ≠ tini

Bash как PID 1

tini как PID 1

Не проксирует сигналы корректно

Проксирует

Не предназначен как init

Предназначен

Может терять SIGTERM

Корректно передаёт

Не чистит zombie

Чистит

Это принципиально разные роли.


9. Итоговые рекомендации (Best Practices)

  1. Используйте exec-форму всегда, когда возможно.

  2. В docker-entrypoint.sh обязательно применяйте exec.

  3. Разделяйте:

    • ENTRYPOINT — исполняемый файл

    • CMD — аргументы по умолчанию

  4. Если приложение создаёт дочерние процессы — используйте tini.

  5. Проверяйте, кто имеет PID 1:

docker exec -it container ps aux

Заключение

На простых примерах всё работает и без этих нюансов. Однако при усложнении логики контейнера:

  • появляются проблемы с остановкой,

  • теряются сигналы,

  • возникают zombie-процессы,

  • контейнер завершает работу некорректно.

Docker упрощает деплой, но не отменяет фундаментальные принципы работы процессов в Linux.

Понимание:

  • роли PID 1,

  • различий exec и shell,

  • корректного построения entrypoint,

  • необходимости tini

позволяет создавать production-ready Docker-образы, которые ведут себя предсказуемо и корректно в любой среде.

User Feedback

Create an account or sign in to leave a review

There are no reviews to display.

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.