Как прописывается процесс, который будет работать внутри Docker-контейнера

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

Ответ

Процесс, который будет запущен внутри контейнера, определяется в Dockerfile с помощью инструкций ENTRYPOINT и CMD. Я всегда комбинирую их для создания гибких и безопасных образов.

Базовые инструкции и их взаимодействие:

  • ENTRYPOINT определяет исполняемую команду, которую нельзя переопределить при запуске контейнера (если только не использовать --entrypoint).
  • CMD задает аргументы по умолчанию для ENTRYPOINT. Эти аргументы можно переопределить при запуске контейнера.

Предпочтительный паттерн (exec form):

FROM alpine:3.18

# Устанавливаем приложение
RUN apk add --no-cache nginx && 
    mkdir -p /run/nginx

# Копируем конфигурацию
COPY nginx.conf /etc/nginx/nginx.conf

# Фиксируем исполняемый файл
ENTRYPOINT ["nginx"]

# Задаем аргументы по умолчанию
CMD ["-g", "daemon off;"]

При таком подходе контейнер по умолчанию запустит nginx -g "daemon off;", но можно легко изменить аргументы:

docker run my-nginx-image -g "daemon off;" -c /custom/nginx.conf

Почему exec form (["cmd", "arg1"]) лучше shell form (cmd arg1):

  1. Корректная обработка сигналов: Контейнер получит сигналы (SIGTERM) напрямую в PID 1, что позволит приложению корректно завершиться.
  2. Отсутствие оболочки: Экономит память и устраняет потенциальные проблемы с переменными окружения.

Для скриптов или сложной логики инициализации я использую скрипт-обертку в качестве ENTRYPOINT:

COPY docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["app-start"]

Содержимое docker-entrypoint.sh:

#!/bin/sh
set -e

# Выполняем предварительную настройку (настройка переменных, проверка зависимостей)
echo "Initializing container..."

# Передаем управление основному процессу
exec "$@"

Важно: Для долгоживущих процессов (веб-серверы, базы данных) необходимо убедиться, что они работают в foreground-режиме, иначе Docker завершит контейнер сразу после запуска.