Несколько команд в директиве Docker CMD

Не понимаю, что происходит, когда я пытаюсь выполнить две команды во время выполнения с помощью директивы CMD в `Dockerfile. Я предположил, что это должно сработать:

CMD ["/etc/init.d/nullmailer", "start", ";", "/usr/sbin/php5-fpm"]

Но это не работает. Контейнер не запущен. Так что мне пришлось сделать это вот так:

CMD ["sh", "-c", "/etc/init.d/nullmailer start ; /usr/sbin/php5-fpm"]

Я не понимаю. Это почему? Почему первая строка - это неправильный путь? Может кто-нибудь объяснить мне эти "CMD shell format vs JSON format и т.д.". Простыми словами.

Просто отметим - то же самое было и с command: директива в docker-compose.yml, как и ожидалось.

Я полагаю, что разница может быть в том, что вторая команда выполняет обработку оболочки, в то время как первая этого не делает. В соответствии с официальная документация, есть следующие exec и shell формы. Ваша первая команда - это exec форма. То exec форма не расширяет переменные среды, в то время как shell форма делает это. Вполне возможно, что с помощью exec форма команды завершается с ошибкой из-за ее зависимости от обработки оболочки. Вы можете проверить это, запустив docker logs CONTAINERID

Ваша вторая команда, форма оболочки, эквивалентна -

CMD /etc/init.d/nullmailer start ; /usr/sbin/php5-fpm

Выдержки из документации -

Примечание: В отличие от формы оболочки, форма exec не вызывает командную оболочку. Это означает, что обычная обработка оболочки не происходит. Например, CMD [ "echo", "$HOME" ] не будет выполнять подстановку переменных на $HOME. Если вам нужна обработка оболочки, то либо используйте форму оболочки, либо запустите оболочку напрямую, например: CMD [ "sh", "-c", "echo", "$HOME" ].

Не усложняй себе жизнь. Просто создайте файл bash "start.sh";:

#!/bin/bash/usr/bin/command2 param1/usr/bin/command1

в вашем файле Dockerfile выполните:

ADD start.sh /RUN chmod +x /start.shCMD ["/start.sh"]

Синтаксис json для CMDRUN и ENTRYPOINT) передайте аргументы ядру непосредственно как системный вызов exec. В системном вызове exec нет разделения команды от аргументов пробелами, экранирования кавычек, перенаправления ввода-вывода, замены переменных, передачи между командами, выполнения нескольких команд и т.д. Системный вызов принимает только исполняемый файл для запуска и список аргументов для передачи этому исполняемому файлу и запускает его.

Персонажи, подобные $ для расширения переменных, ; для разделения команд, (пробел) для разделения аргументов, && и || чтобы связать команды в цепочку, > для перенаправления вывода, | для передачи данных между командами и т.д. Все это функции оболочки и нужно что-то вроде /bin/sh или /bin/bash интерпретировать и внедрять их.


Если вы переключитесь на строковый синтаксис CMD, docker выполнит вашу команду с помощью оболочки:

CMD /etc/init.d/nullmailer start ; /usr/sbin/php5-fpm

В противном случае ваш второй синтаксис делает то же самое:

CMD ["sh", "-c", "/etc/init.d/nullmailer start ; /usr/sbin/php5-fpm"]

Обратите внимание, что я не рекомендую запускать несколько команд таким образом внутри контейнера, поскольку при сбое вашей первой команды обработка ошибок не выполняется, особенно если она выполняется в фоновом режиме. Вы также оставляете оболочку, работающую как pid 1 внутри контейнера, что приведет к нарушению обработки сигналов, что приведет к 10-секундной задержке и неблагодарному уничтожению вашего контейнера docker. Обработка сигналов может быть уменьшена с помощью оболочки exec команда:

CMD /etc/init.d/nullmailer start ; exec /usr/sbin/php5-fpm

Однако для обработки процессов, которые беззвучно выходят из строя в фоновом режиме, требуется переключиться на какой-нибудь менеджер нескольких процессов, такой как supervisord, или, предпочтительно, разбить ваше приложение на несколько контейнеров и развернуть их с помощью чего-то вроде docker-compose.

Я предполагаю, что первая команда завершается ошибкой, потому что в форме DOCKER CMD выполняется только первый параметр, остальное передается в эту команду.

Вторая форма работает, потому что все команды, разделенные символом ";", передаются в команду sh, которая их выполняет.

Например, представьте, что у вас есть две команды python для запуска python init_reset.py и python app.py. Затем с помощью CMD вы можете объединить две команды с помощью одной команды

CMD python init_reset.py ; python app.py

В Docker compose это можно сделать в следующем примере:

command: ["sh", "-c", "    apt update && apt install -y libldap-common;    cp /ca.crt /usr/local/share/ca-certificates/;    update-ca-certificates;    exec apache2-foreground  "]

То exec переключит контекст основного исполняемого файла на apache2-forground.

Я не думаю, что вам следует ставить запятую после слова "начать".

вместо того, чтобы использовать

CMD ["/etc/init.d/nullmailer", "start", ";", "/usr/sbin/php5-fpm"]

пробовать

CMD ["/etc/init.d/nullmailer", "start", "/usr/sbin/php5-fpm"]

поскольку docker использует "sh -c", приведенная выше команда будет выполнена, как показано ниже

/etc/init.d/nullmailer start/etc/init.d/nullmailer /usr/sbin/php5-fpm