Wednesday, September 13, 2017

DOCKER ENTRYPIONT VS CMD




This is a good article http://www.johnzaccone.io/entrypoint-vs-cmd-back-to-basics/ explaining the difference between ENTRYPOINT and CMD. I am going to design some tests to illustrate the differences further more. The things to remember are:

1)      In the “docker run” command, things after the image name replaces CMD
2)      ENTRYPOINT has two forms:
ENTRYPOINT [“executable”, “param1”,”param2”] (exec form, preferred)
ENTRYPOINT command param1 param2 (shell form)

With shell form, the process with PID 1 is the shell. When you send a killing signal to a container, PID 1 process receives the signal. If PID 1 is shell, you need to take care of handling killing signals.

My docker version is:
ubuntu@exp1:~$ docker version
Client:
 Version:      17.03.0-ce
 API version:  1.26
 Go version:   go1.7.5
 Git commit:   3a232c8
 Built:        Tue Feb 28 08:01:32 2017
 OS/Arch:      linux/amd64

Dockerfile_1

FROM openjdk:8
ENTRYPOINT ["java"]
CMD ["-version"]

Build and run Dockerfile_1
sudo docker build -t test1 -f Dockerfile_1 .

sudo docker run -it --name test1 --rm test1
This runs “java version”

sudo docker run -it --name test1  --rm test1 aaa
(--rm deletes the container after it has finished running)
This runs “java aaa”. Things behind the image name replaces CMD in the Dockerfile. Since there is no class aaa, this will throw out an error: Error: Could not find or load main class aaa

sudo docker run -it --name test1  --rm --entrypoint "echo" test1 aaa
This runs “echo aaa”.  --entrypoint runtime argument replace ENTRYPOINT in the Dockerfile, note the arguments to --entrypoint is placed at the end.

Dockerfile_2

FROM openjdk:8
ENTRYPOINT ["java aaa"]
CMD ["-version"]

Build and run Dockerfile_2
sudo docker build -t test2 -f Dockerfile_2 .

sudo docker run -it --name test2  --rm test2
This runs into an error: Error response from daemon: oci runtime error: container_linux.go:247: starting container process caused "exec: \"java aaa\": executable file not found in $PATH". This is because “java aaa” is treated as one executable.

Dockerfile_3

FROM openjdk:8
ENTRYPOINT ["/bin/sh", "-c", "echo apple && java"]
CMD ["-version"]

Build and run Dockerfile_3
sudo docker run -it --name test3  --rm test3
This runs "echo apple && java" not "echo apple && java -version", it is impossible to append additional arguments to “/bin/sh -c”.

Dockerfile_4

FROM openjdk:8
CMD ["java", "-version"]

Build and run Dockerfile_4
sudo docker build -t test4 -f Dockerfile_4 .

sudo docker run -it --name test4 --rm test4
This run "java -version".

sudo docker run -it --name test4 --rm test4  aaa
This run “aaa”, and throws an error: docker: Error response from daemon: oci runtime error: container_linux.go:247: starting container process caused "exec: \"aaa\": executable file not found in $PATH".
This is because things after the image name replaces CMD, so 'java -version' is not run, "aaa" is run instead.

sudo docker run -it --name test4  --rm test4 /bin/sh
"/bin/sh" replaces CMD, which enables you to get inside the container.

 

Dockerfile_5

FROM openjdk:8
ENTRYPOINT ["java"]

Build and run Dockerfile_5
sudo docker build -t test5 -f Dockerfile_5 .

sudo docker run --rm --name test5 test5 aaa
This runs “java aaa”. Since there is no class aaa, this will throw out an error: Error: Could not find or load main class aaa.
This behavior is exactly the same as Dockerfile_1, even though there is no CMD defined in the Dockefile.

Dockerfile_6

FROM openjdk:8
RUN touch /tmp.txt
ENTRYPOINT tail -f /tmp.txt

Build and run Dockerfile_6
sudo docker build -t test6 -f Dockerfile_6 .

sudo docker run  -d --name test6 test6
(-d runs container in the background)

$ sudo docker exec test6 ps auxf
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.8  0.0   4288   724 ?        Ss   09:34   0:00 /bin/sh -c tail -f /tmp.txt
root         6  0.0  0.0   5980   756 ?        S    09:34   0:00 tail -f /tmp.txt

docker exec needs the container to be alive, “tail -f” makes sure the container is alive.
Note the PID 1 process is shell.
$ sudo docker inspect test6
"Path": "/bin/sh",
"Args": [
           "-c",
           "tail -f /tmp.txt"
 ]

Dockerfile_7

FROM openjdk:8
RUN touch /tmp.txt
ENTRYPOINT ["tail", "-f", "/tmp.txt"]

Note, you can’t write ENTRYPOINT ["tail", "-f /tmp.txt"], upon running the image, you will receive an error: tail: invalid option -- ' '

Build and run Dockerfile_7
sudo docker build -t test7 -f Dockerfile_7 .

sudo docker run  -d --name test7 test7
$ sudo docker exec test7 ps auxf
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  1.2  0.0   5980   708 ?        Ss   09:43   0:00 tail -f /tmp.txt

Note the PID 1 process is tail.



In my tests, running both test6 and test7 in the foreground (without -d), ctrl+c to kill them doesn’t work. I had expected to see it work on test7, because in test7, PID 1 is tail, but it didn’t. This may have something to do with https://www.weave.works/blog/my-container-wont-stop-on-ctrl-c-and-other-minor-tragedies/.