Reputation: 674
Dockerfile - best practices says that
CMD should almost always be used in the form of CMD ["executable", "param1", "param2"…]
in which case shell form should be used?
Upvotes: 4
Views: 2954
Reputation: 7988
Shell form will invoke a command shell and do the usual command processing that the shell typically handles (like substitution of environment variables such as $HOME
). The exec form doesn't do that.
That is closely related to the SHELL
directive.
You can have multiple SHELL
commands in the Dockerfile, but only one CMD
. CMD
is used to specify what the container should run when it starts. The SHELL
directive will overwrite the default shell that is used by the shell-form of various commands (RUN
, CMD
, ENTRYPOINT
).
Using this Dockerfile illustrates this better than I could explain it:
FROM python:3.6
RUN echo $PATH
SHELL ["/bin/bash" ,"-c"]
RUN echo $PATH
RUN ["echo", "$PATH"]
COPY run.sh /run.sh
ENTRYPOINT ["/run.sh"]
Will result in this when running docker build
:
$ docker build .
Sending build context to Docker daemon 5.12kB
Step 1/7 : FROM python:3.6
---> 5bf410ee7bb2
Step 2/7 : RUN echo $PATH
---> Running in 3a08d7c4450c
/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
Removing intermediate container 3a08d7c4450c
---> 85b4da5d8e5d
Step 3/7 : SHELL ["/bin/bash" ,"-c"]
---> Running in da1b90ac14f2
Removing intermediate container da1b90ac14f2
---> ed747f0862a6
Step 4/7 : RUN echo $PATH
---> Running in 5c6a86e133ff
/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
Removing intermediate container 5c6a86e133ff
---> 8ec42f23d390
Step 5/7 : RUN ["echo", "$PATH"]
---> Running in cc0650a6d8e8
$PATH
Removing intermediate container cc0650a6d8e8
---> 8b11432adb3a
Step 6/7 : COPY run.sh /run.sh
---> a168c58738e7
Step 7/7 : ENTRYPOINT ["/run.sh"]
---> Running in f9e28048d139
Removing intermediate container f9e28048d139
---> d20920ea562c
Successfully built d20920ea562c
Notice that when it ran the shell mode commands (using both the default shell and bash), $PATH was expanded, but not when run using exec mode.
Upvotes: 4
Reputation: 159830
For CMD
, you need shell form only if you need functionality that’s only provided in a shell — if your CMD
includes environment variable references $ARG
, multiple commands a && b
, pipelines | tee ...
, or redirections 2>&1
. If you have an especially complex CMD
like this it might be a better practice to move it to a shell script (that you can independently test outside of Docker) and have your default CMD
just be to invoke the script.
RUN
technically has the same considerations. It’s very common to run multiple commands in the same RUN
instruction, though, and this requires shell form. Almost every RUN
line I’ve ever seen uses the shell form.
ENTRYPOINT
should always be the JSON-array form. Remember that the entrypoint gets passed the command as arguments; if you use the shell form it effectively causes these arguments to be ignored. Again, if you need to do complex things in your ENTRYPOINT
(including exec "$@"
to run the command), split it into a separate shell script and invoke it using JSON-array syntax.
Upvotes: 3
Reputation: 3900
Pretty much never; if you need a shell command, you can just call a script with parenthesis format (CMD ["bash", "myscript.sh"]
). The problem with shell form is that it breaks shutdown: https://hynek.me/articles/docker-signals/
Upvotes: 2