В чем разница между формами exec и shell в Dockerfile?

«В чем разница между формами exec и shell в Dockerfile?» — вопрос из категории Linux, который задают на 23% собеседований Devops Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В Dockerfile инструкции RUN, CMD и ENTRYPOINT могут быть записаны в двух формах: shell и exec. Это влияет на то, как запускается процесс внутри контейнера.

Shell-форма (по умолчанию):

  • Команда выполняется через оболочку, обычно /bin/sh -c.
  • Поддерживает shell-подстановки: переменные окружения, пайпы (|), перенаправления (>), логические операторы (&&).
    RUN echo $HOME && ls -la | grep log
    CMD echo "Container started with PID: $$"

Exec-форма (форма JSON-массива):

  • Команда выполняется напрямую, минуя оболочку. Первый элемент массива — исполняемый файл, остальные — аргументы.
  • Более эффективна, так как не создает лишнего процесса оболочки (PID 1 в контейнере — это сама команда).
  • Переменные окружения не раскрываются автоматически.
    RUN ["/bin/bash", "-c", "echo $HOME"] # Здесь bash раскроет переменную
    CMD ["nginx", "-g", "daemon off;"]
    ENTRYPOINT ["/app/start.sh"]
Ключевые различия и рекомендации: Аспект Shell-форма Exec-форма
Процесс PID 1 Оболочка (/bin/sh), которая может некорректно обрабатывать сигналы (SIGTERM). Сама команда, что обеспечивает корректное завершение по сигналам.
Переменные окружения Раскрываются оболочкой. Не раскрываются, если не использовать оболочку явно.
Использование Для простых команд, где нужны shell-возможности. Рекомендуется для CMD и ENTRYPOINT, чтобы контейнер корректно получал сигналы остановки.

Практический пример: Для запуска веб-сервера всегда использую exec-форму, чтобы docker stop отправлял сигнал напрямую nginx/gunicorn, а не оболочке.