Ответ
В 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, а не оболочке.