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"]
EOFBuild the image.
docker build -t cmd_exec .Run the image.
docker run -it --rm cmd_execCheck 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/dashCheck 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 ./DockerfileNow try it with shell form
cat <<"EOF" > Dockerfile
FROM ubuntu
CMD /bin/bash
EOFBuild the image.
docker build -t cmd_shell .Run the image.
docker run -it --rm cmd_shellCheck 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 aWe 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/dashCheck that the shell is now dash and see what its PID is.
ps aInterestingly, 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 ./DockerfileFinally, 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
EOFBuild the image.
docker build -t cmd_shell_exec .Run the image.
docker run -it --rm cmd_shell_execCheck 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 ./DockerfileENTRYPOINT'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"]
EOFBuild the image.
docker build -t entrypoint_exec .Run the image.
docker run -it --rm entrypoint_execCheck 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 $2You 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 pongCheck the positional parameters to see that the new arguments were appended to the ones specifiend in the Dockerfile's ENTRYPOINT.
echo $1 $2 $3 $4foo bar ping pong
Exit out of the container, and then delete the image and the Dockerfile.
docker rmi entrypoint_exec
rm ./DockerfileBuild 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
EOFBuild the image.
docker build -t entrypoint_shell .Run the image.
docker run -it --rm entrypoint_shellCheck what PID bash is running as.
ps aHere 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 $2You 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 pongCheck the positional parameters to see that the new arguments were appended to the ones specifiend in the Dockerfile's ENTRYPOINT.
echo $1 $2 $3 $4And 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 ./DockerfileBuild 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
EOFBuild the image.
docker build -t entrypoint_shell_exec .Run the image.
docker run -it --rm entrypoint_shell_execCheck what PID bash is running as.
ps aAnd 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 ./DockerfileWhen 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"]
EOFBuild the image.
docker build -t entrypoint_cmd_exec .Run the image.
docker run -it --rm entrypoint_cmd_execSee if there's any values passed into positional parameters via CMD's exec form.
echo $1 $2You 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 pongSee what values were passed into the first two positional parameters.
echo $1 $2ping 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 ./DockerfileBuild 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
EOFBuild the image.
docker build -t entrypoint_shell_exec .Run the image.
docker run -it --rm entrypoint_shell_execSee if there's any values passed into positional parameters via CMD's exec form.
echo $1 $2Well, 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 pongSee 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