Docker CMD and ENTRYPOINT - blitterated/docker-dev-env GitHub Wiki
Quick experiments to learn how the Dockerfile's CMD and ENTRYPOINT work alone and together.
CMD is overridable, and is intended to provide a default executable and/or default arguments for a container.
From the Docker documentation for CMD:
The CMD instruction has three forms:
- CMD ["executable","param1","param2"] (exec form, this is the preferred form)
- CMD ["param1","param2"] (as default parameters to ENTRYPOINT)
- CMD command param1 param2 (shell form)
The first and second forms are Exec Form and are both parsed as JSON. The third form is Shell Form. Its command gets passed to /bin/sh -c
by default.
I'll cover the first and third forms, CMD exec and CMD shell, here. The second form, ENTRYPOINT default params, I'll cover in its own section below, "ENTRYPOINT and CMD together".
First, build a default Ubuntu image that, by default, runs bash with the exec form.
cat <<"EOF" > Dockerfile
FROM ubuntu
CMD ["/bin/bash"]
EOF
Build the image.
docker build -t cmd_exec .
Run the image.
docker run -it --rm cmd_exec
Check that the shell is bash
.
ps p $$
Notice in the output that bash
is PID 1. This will be different for shell form.
PID TTY STAT TIME COMMAND
1 pts/0 Ss 0:00 /bin/bash
Exit out of the container.
Start a new one but specify dash
instead.
docker run -it --rm cmd_exec /usr/bin/dash
Check that the shell is now dash
.
ps p $$
It's still PID 1.
PID TTY STAT TIME COMMAND
1 pts/0 Ss 0:00 /usr/bin/dash
Exit out of the container, and then delete the image and the Dockerfile.
docker rmi cmd_exec
rm ./Dockerfile
Now try it with shell form
cat <<"EOF" > Dockerfile
FROM ubuntu
CMD /bin/bash
EOF
Build the image.
docker build -t cmd_shell .
Run the image.
docker run -it --rm cmd_shell
Check that the shell is bash
.
ps p $$
Notice in the output that bash
is NOT PID 1.
PID TTY STAT TIME COMMAND
9 pts/0 S 0:00 /bin/bash
Take a look at all the running processes.
ps a
We can see in the out put that shell form CMD wraps it's executable in /bin/sh -c
.
PID TTY STAT TIME COMMAND
1 pts/0 Ss 0:00 /bin/sh -c /bin/bash
9 pts/0 S 0:00 /bin/bash
16 pts/0 R+ 0:00 ps a
Exit out of the container.
Start a new one but specify dash
instead.
docker run -it --rm cmd_shell /usr/bin/dash
Check that the shell is now dash
and see what its PID is.
ps a
Interestingly, it's PID 1 when CMD is overridden
PID TTY STAT TIME COMMAND
1 pts/0 Ss 0:00 /usr/bin/dash
8 pts/0 R+ 0:00 ps a
Exit out of the container, and then delete the image and the Dockerfile.
docker rmi cmd_shell
rm ./Dockerfile
Finally, try it with shell form but using exec
. This isn't the combination of ENTRYPOINT and CMD. Rather it's specifying exec
as the actual executable to run. It just so happens that the first argument to exec
is another executable. Here exec
will lift the second executable into process that's running /bin/sh -c
. It just so happens that that process is PID 1. It's an interesting little hack on Docker's CMD shell form.
cat <<"EOF" > Dockerfile
FROM ubuntu
CMD exec /bin/bash
EOF
Build the image.
docker build -t cmd_shell_exec .
Run the image.
docker run -it --rm cmd_shell_exec
Check that the shell is bash
.
ps p $$
Notice in the output that bash
is PID 1. With exec
we force bash
to take over the process running sh
used by CMD shell form to wrap executables.
PID TTY STAT TIME COMMAND
1 pts/0 Ss 0:00 /bin/bash
Exit out of the container, and then delete the image and the Dockerfile.
docker rmi cmd_shell_exec
rm ./Dockerfile
ENTRYPOINT's executable, unlike CMD, is not overridable. Its intended use is for creating containers that will be run as executables.
Anything extra passed into docker run
will be interpreted as extra arguments to the executable, and will be appended to any arguments given in the ENTRYPOINT forms.
From the Docker documentation for ENTRYPOINT:
ENTRYPOINT has two forms:
The exec form, which is the preferred form:
ENTRYPOINT ["executable", "param1", "param2"]
The shell form:
ENTRYPOINT command param1 param2
Build an Ubuntu image that runs bash
with the exec form. It specifies the -s
option to bash
that will allow arguments to be loaded into positional variables.
cat <<"EOF" > Dockerfile
FROM ubuntu
ENTRYPOINT ["/bin/bash", "-s", "foo", "bar"]
EOF
Build the image.
docker build -t entrypoint_exec .
Run the image.
docker run -it --rm entrypoint_exec
Check that bash
is PID 1.
ps a
PID TTY STAT TIME COMMAND
1 pts/0 Ss 0:00 /bin/bash -s
9 pts/0 R+ 0:00 ps a
See if there's any values passed into positional parameters with bash -s
.
echo $1 $2
You should see foo bar
as a result
Exit out of the container, and start a new one with extra arguments.
docker run -it --rm entrypoint_exec ping pong
Check the positional parameters to see that the new arguments were appended to the ones specifiend in the Dockerfile's ENTRYPOINT.
echo $1 $2 $3 $4
foo bar ping pong
Exit out of the container, and then delete the image and the Dockerfile.
docker rmi entrypoint_exec
rm ./Dockerfile
Build an Ubuntu image that runs bash
with the shell form. It also specifies the -s
option to bash
that allows arguments to be loaded into positional variables.
cat <<"EOF" > Dockerfile
FROM ubuntu
ENTRYPOINT /bin/bash -s foo bar
EOF
Build the image.
docker build -t entrypoint_shell .
Run the image.
docker run -it --rm entrypoint_shell
Check what PID bash
is running as.
ps a
Here we can see that bash is running as a child of /bin/sh -c
just like with CMD's shell format.
PID TTY STAT TIME COMMAND
1 pts/0 Ss 0:00 /bin/sh -c /bin/bash -s foo bar
8 pts/0 S 0:00 /bin/bash -s foo bar
11 pts/0 R+ 0:00 ps a
We'll work around this in the "ENTRYPOINT Shell Form with exec
usage" section below.
See if there's any values passed into positional parameters with bash -s
.
echo $1 $2
You should see foo bar
as a result.
Exit out of the container, and start a new one with extra arguments.
docker run -it --rm entrypoint_shell ping pong
Check the positional parameters to see that the new arguments were appended to the ones specifiend in the Dockerfile's ENTRYPOINT.
echo $1 $2 $3 $4
And here we don't get the additional parameters appended.
foo bar
Exit out of the container, and then delete the image and the Dockerfile.
docker rmi entrypoint_shell
rm ./Dockerfile
Build an Ubuntu image that runs bash
with the shell form. It also uses exec
to have bash
take over /bin/sh -c
's process (PID).
It also specifies the -s
option to bash
that allows arguments to be loaded into positional variables.
cat <<"EOF" > Dockerfile
FROM ubuntu
ENTRYPOINT exec /bin/bash -s foo bar
EOF
Build the image.
docker build -t entrypoint_shell_exec .
Run the image.
docker run -it --rm entrypoint_shell_exec
Check what PID bash
is running as.
ps a
And we can see that its PID is 1. We can also see that -s foo bar
were all passed in as arguments, so no need to test that again.
PID TTY STAT TIME COMMAND
1 pts/0 Ss 0:00 /bin/bash -s foo bar
9 pts/0 R+ 0:00 ps a
Exit out of the container, and then delete the image and the Dockerfile.
docker rmi entrypoint_shell_exec
rm ./Dockerfile
When using ENTRYPOINT and CMD together, CMD acts as a way to supply default arguments to the executable and initial options/arguments supplied by ENTRYPOINT.
This only appears to work with Exec Form.
Build an Ubuntu image that runs bash
with the ENTRYPOINT exec form. Add arguments with the CMD exec form.
cat <<"EOF" > Dockerfile
FROM ubuntu
ENTRYPOINT ["/bin/bash", "-s"]
CMD ["foo", "bar"]
EOF
Build the image.
docker build -t entrypoint_cmd_exec .
Run the image.
docker run -it --rm entrypoint_cmd_exec
See if there's any values passed into positional parameters via CMD's exec form.
echo $1 $2
You should see foo bar
as a result.
Exit out of the container, and start a new one but with different arguments.
docker run -it --rm entrypoint_cmd_exec ping pong
See what values were passed into the first two positional parameters.
echo $1 $2
ping pong
So we see that the arguments supplied to docker run
overrode the default ones supplied by the Dockerfile's CMD entry.
Exit out of the container, and then delete the image and the Dockerfile.
docker rmi entrypoint_cmd_exec
rm ./Dockerfile
Build an Ubuntu image that runs bash
with the ENTRYPOINT shell form. Add arguments with the CMD shell form.
cat <<"EOF" > Dockerfile
FROM ubuntu
ENTRYPOINT /bin/bash -s
CMD foo bar
EOF
Build the image.
docker build -t entrypoint_shell_exec .
Run the image.
docker run -it --rm entrypoint_shell_exec
See if there's any values passed into positional parameters via CMD's exec form.
echo $1 $2
Well, there's nothing passed in.
Exit out of the container, and start a new one but with supply arguments to docker run
.
docker run -it --rm entrypoint_shell_exec ping pong
See what values were passed into the first two positional parameters.
Still nothing.
Exit out of the container, and then delete the image and the Dockerfile.
docker rmi entrypoint_shell_exec
rm ./Dockerfile