Not understanding what is happening when I try to execute two commands at runtime via CMD directive in `Dockerfile. I assumed that this should work:
CMD ["/etc/init.d/nullmailer", "start", ";", "/usr/sbin/php5-fpm"]
But it's not working. Container has not started. So I had to do it like this:
CMD ["sh", "-c", "/etc/init.d/nullmailer start ; /usr/sbin/php5-fpm"]
I don't understand. Why is that? Why first line is not the right way? Can somebody explain me these "CMD shell format vs JSON format, etc" stuff. In simple words.
Just to note - the same was with command:
directive in docker-compose.yml
, as expected.
I believe the difference might be because the second command does shell processing while the first does not. Per the official documentation, there are the
exec
andshell
forms. Your first command is anexec
form. Theexec
form does not expand environment variables while theshell
form does. It is possible that by using theexec
form the command is failing due to its dependence on shell processing. You can check this by runningdocker logs CONTAINERID
Your second command, the shell form, is equivalent to -
CMD /etc/init.d/nullmailer start ; /usr/sbin/php5-fpm
Excerpts from the documentation -
Don't make it hard on yourself. Just create a bash file "start.sh":
in your Dockerfile do:
The json syntax of
CMD
(andRUN
andENTRYPOINT
) pass the arguments to the kernel directly as an exec syscall. There is no separating of the command from the arguments by spaces, escaping of quotes, IO redirection, variable substitution, piping between commands, running multiple commands, etc, in the exec syscall. The syscall only takes the executable to run and list of arguments to pass to that executable, and it runs it.Characters like
$
to expand variables,;
to separate commands,(space) to separate arguments,
&&
and||
to chain commands,>
for output redirection,|
to pipe between commands, etc, are all features of the shell and need something like/bin/sh
or/bin/bash
to interpret and implement them.If you switch to the string syntax of
CMD
, docker will run your command with a shell:Otherwise, your second syntax does the exact same thing:
Note that I do not recommend running multiple commands this way inside of a container since there is no error handling if your first command fails, especially if it runs in the background. You also leave a shell running as pid 1 inside the container which will break signal handling, resulting in a 10 second delay and ungraceful kill of your container by docker. The signal handling can be mitigated by using the shell
exec
command:However, handling processes silently failing in the background requires you switch to some kind of multi-process manager like supervisord, or preferably breakup your application into multiple containers and deploy them with something like docker-compose.
I guess first command fails because in DOCKER CMD form, only the first parameter is executed, the rest is fed into this command.
The second form works because all commands seperated with ";" are fed into sh command, which executes them.
For example, imagine you have two python commands to run
python init_reset.py
andpython app.py
. Then using CMD, you can combine the two commands with the single commandIn Docker compose, this can be done as the following example:
The
exec
will switch the context of the main executable to apache2-forground.I don't think you should put semi comma after "start"
instead of using
try
as docker uses "sh -c", above command will be executed as below