← Назад к вопросам

Различие exec и shell

1.7 Middle🔥 151 комментариев
#Docker и контейнеризация#Linux и администрирование

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Различие между exec и shell формами в Docker и Kubernetes

В контексте контейнеризации (Docker, Kubernetes) и автоматизации, термины exec и shell относятся к двум различным способам выполнения команд внутри контейнера или системы. Их ключевое отличие заключается в модели процесса и обработке переменных окружения.

Форма shell

Форма shell выполняет команду через оболочку (shell) системы, по умолчанию /bin/sh в Linux. Это поведение по умолчанию в Dockerfile, если команда указана как обычная строка.

  • Синтаксис в Dockerfile: CMD echo "Hello" или RUN apt-get update
  • Как это работает: Docker неявно оборачивает команду в /bin/sh -c "ваша_команда".
  • Особенности:
    *   **Поддержка shell-операторов:** Можно использовать подстановки переменных (`$VAR`), пайпы (`|`), перенаправления вывода (`>`, `>>`), логические операторы (`&&`, `||`).
    *   **Процесс-оболочка:** В качестве PID 1 (главного процесса контейнера) запускается сам shell, а ваша команда становится его дочерним процессом. Это может мешать корректной обработке сигналов (например, SIGTERM).
    *   **Расширение переменных:** Переменные окружения обрабатываются shell'ом.

Пример Dockerfile (shell-форма):

FROM alpine:latest
ENV NAME=World
# Shell-форма (неявная)
CMD echo "Hello, $NAME!"

При запуске контейнера будет выведено: Hello, World!.

Форма exec (исполнительная)

Форма exec выполняет команду напрямую, минуя оболочку. Она используется, когда команда и её аргументы передаются как массив строк (JSON-массив).

  • Синтаксис в Dockerfile: CMD ["executable", "arg1", "arg2"]
  • Как это работает: Docker (или runtime) запускает указанный исполняемый файл (executable) напрямую, без посредника в виде shell.
  • Особенности:
    *   **Прямой вызов:** Нет поддержки shell-операторов. Каждый элемент массива — это отдельный аргумент.
    *   **Правильный PID 1:** Ваше приложение становится процессом с PID 1, что обеспечивает прямую передачу системных сигналов (например, `docker stop` отправляет SIGTERM напрямую вашему процессу). Это **рекомендуемая практика** для основного процесса контейнера.
    *   **Без расширения переменных:** Shell-переменные не будут обработаны автоматически. Для использования переменных окружения нужно обрабатывать их внутри приложения.

Пример Dockerfile (exec-форма):

FROM alpine:latest
ENV NAME=World
# Exec-форма (явная, JSON-массив)
CMD ["/bin/echo", "Hello, $NAME"]

При запуске контейнера будет выведено дословно: Hello, $NAME. Чтобы использовать переменную, нужно, чтобы само приложение (например, echo) поддерживало это, либо использовать shell внутри exec-формы.

Сравнение в таблице

КритерийShell-формаExec-форма
СинтаксисCMD command argCMD ["exec", "arg1", "arg2"]
ОболочкаИспользуется (/bin/sh -c)Не используется
PID 1Процесс оболочки (shell)Ваше приложение
Обработка сигналовМожет быть некорректной (shell может игнорировать SIGTERM)Корректная (сигналы идут прямо в приложение)
Переменные окруженияРасширяются оболочкойНе расширяются (передаются как есть)
Shell-операторыПоддерживаются (&&, |, >)Не поддерживаются
РекомендацияДля отладки, сложных команд с пайпамиДля основного процесса контейнера (best practice)

Практические примеры и важные нюансы

  1. Комбинация форм (shell внутри exec): Если вам нужны shell-возможности (например, подстановка переменной) и корректный PID 1, можно явно вызвать shell в exec-форме.
    CMD ["/bin/sh", "-c", "echo Hello, $NAME && my-app"]
    
    Здесь PID 1 будет `/bin/sh`, но он корректно обработает сигналы, так как запущен с флагом `-c` и передаст их дочернему процессу `my-app`.

  1. Использование в Kubernetes: В манифестах Pod (spec.containers[].command и args) всегда используется exec-форма. command соответствует ENTRYPOINT в exec-форме, а args — аргументам CMD.

    apiVersion: v1
    kind: Pod
    spec:
      containers:
      - name: myapp
        image: myapp:latest
        command: ["/bin/myapp"]  # Exec-форма (аналог ENTRYPOINT)
        args: ["--port", "8080"] # Аргументы (аналог CMD)
    
  2. Отладка: Для разовых команд отладки в запущенном контейнере почти всегда используется shell-форма через docker exec или kubectl exec, так как она интерактивна и привычна.

    kubectl exec -it my-pod -- /bin/bash
    # или с shell-оператором
    docker exec my-container sh -c "cat /etc/hosts | grep local"
    

Вывод: Основное различие — в наличии процесса-оболочки как посредника. Exec-форма — это стандарт де-факто для запуска production-приложений в контейнерах из-за корректной работы с сигналами и чистой среды выполнения. Shell-форма остается полезным инструментом для написания сложных команд в Dockerfile на этапе сборки (RUN) или для отладки, но для финальной команды (CMD или ENTRYPOINT) предпочтение следует отдавать exec-варианту.