I had difficulty in understanding the difference between CMD and ENTRYPOINT in Dockerfile before I did the below experiment. Hope this will help someone like me who cannot understand the difference from the documentation or stackoverflow.
Consider the below docker file
[[email protected] 01.BusyboxWithCMD]$ cat Dockerfile FROM busybox:latest CMD ["cat", "/etc/passwd"]
Let's build an image and call it busy_cmd_image
[[email protected] 01.BusyboxWithCMD]$ docker build -t busy_cmd_image . Sending build context to Docker daemon 2.048 kB Step 1 : FROM busybox:latest ---> 2b8fd9751c4c Step 2 : CMD cat /etc/passwd ---> Using cache ---> e4df0d0c0d67 Successfully built e4df0d0c0d67
Now, we'll run the image and name the container busy_cmd_container
[[email protected] 01.BusyboxWithCMD]$ docker run --name busy_cmd_container busy_cmd_image root:x:0:0:root:/root:/bin/sh daemon:x:1:1:daemon:/usr/sbin:/bin/false bin:x:2:2:bin:/bin:/bin/false sys:x:3:3:sys:/dev:/bin/false sync:x:4:100:sync:/bin:/bin/sync mail:x:8:8:mail:/var/spool/mail:/bin/false www-data:x:33:33:www-data:/var/www:/bin/false operator:x:37:37:Operator:/var:/bin/false nobody:x:99:99:nobody:/home:/bin/false
There is another docker file which has ENTRYPOINT.
[[email protected] 02.BusyboxWithEntryPoint]$ cat Dockerfile FROM busybox:latest ENTRYPOINT ["cat", "/etc/passwd"]
Let's build the image and name it busy_entry_image.
[[email protected] 02.BusyboxWithEntryPoint]$ docker build -t busy_entry_image . Sending build context to Docker daemon 2.048 kB Step 1 : FROM busybox:latest ---> 2b8fd9751c4c Step 2 : ENTRYPOINT cat /etc/passwd ---> Using cache ---> 4016bc22c516 Successfully built 4016bc22c516
Now, when we run this image we'll get the same output like before.
[[email protected] 02.BusyboxWithEntryPoint]$ docker run --name busy_entry_container busy_entry_image root:x:0:0:root:/root:/bin/sh daemon:x:1:1:daemon:/usr/sbin:/bin/false bin:x:2:2:bin:/bin:/bin/false sys:x:3:3:sys:/dev:/bin/false sync:x:4:100:sync:/bin:/bin/sync mail:x:8:8:mail:/var/spool/mail:/bin/false www-data:x:33:33:www-data:/var/www:/bin/false operator:x:37:37:Operator:/var:/bin/false nobody:x:99:99:nobody:/home:/bin/false
Let's come to the point now.
Does it mean CMD and ENTRYPOINT are just different notations to perform the same activity?
The answer is NO.
We'll first see why are they behaving the same way.
For that, let's inspect their containers to understand them better.
I am not a docker expert to interpret all the fields in the output. But let's have a look at those fields which are significant for us.
I have marked those in red.
[[email protected] 01.BusyboxWithCMD]$ docker inspect busy_cmd_container [ { "Id": "2e71127e337a437d02af82bf998d0507ab1bb037ddd85448eed504262bad0f22", "Created": "2016-10-21T03:36:54.315846464Z", "Path": "cat", "Args": [ "/etc/passwd" ], "State": { "Status": "exited", "Running": false, "Paused": false, "Restarting": false, "OOMKilled": false, "Dead": false, "Pid": 0, "ExitCode": 0, "Error": "", "StartedAt": "2016-10-21T03:36:54.45640868Z", "FinishedAt": "2016-10-21T03:36:54.459113472Z" }, "Image": "sha256:9aaf05fedcf6f4510f76281a33fcc6f1f16e63d406fa6ebe2f3b9218590d23b9", ... ]
Path
is set as "cat" and Args
has just one value that is "/etc/passwd".
Let's also inspect the busy_entry_container,
[[email protected] 02.BusyboxWithEntryPoint]$ docker inspect busy_entry_container [ { "Id": "99ae755c4c4422021bd28e8acd19fc061a97a1b3654e5d50f2ad5521e866bf3d", "Created": "2016-10-21T12:38:04.567062004Z", "Path": "cat", "Args": [ "/etc/passwd" ], "State": { "Status": "exited", "Running": false, "Paused": false, "Restarting": false, "OOMKilled": false, "Dead": false, "Pid": 0, "ExitCode": 0, "Error": "", "StartedAt": "2016-10-21T12:38:04.690532676Z", "FinishedAt": "2016-10-21T12:38:04.693614887Z" }, "Image": "sha256:22aa602beefdaee9ee54ee218d099817cfba48fbea38fb943c85fb141332fb63", ... ]
In both the cases Path
is "cat" and Args
is "/etc/passwd".
This is reason for both the above images to give the same output when no command line parameter is passed.
We found why both the images are giving the same output. Now, let's run images by passing the command line arguments and see how it behaves.
[[email protected] 01.BusyboxWithCMD]$ docker run --name busy_cmd_container_2 busy_cmd_image /etc/hosts docker: Error response from daemon: oci runtime error: exec: "/etc/hosts": permission denied.
When we inspect the container busy_cmd_container_2
[ { "Id": "95a99fb59cb293683719824c1d712d32658b314435743f028fe5c3335923a45f", "Created": "2016-10-21T12:26:25.400017698Z", "Path": "/etc/hosts", "Args": [], "State": { "Status": "created", "Running": false, "Paused": false, "Restarting": false, "OOMKilled": false, "Dead": false, "Pid": 0, "ExitCode": 126, "Error": "oci runtime error: exec: \"/etc/hosts\": permission denied", "StartedAt": "0001-01-01T00:00:00Z", "FinishedAt": "0001-01-01T00:00:00Z" }, "Image": "sha256:9aaf05fedcf6f4510f76281a33fcc6f1f16e63d406fa6ebe2f3b9218590d23b9", ... ]
This shows that arguments passed in the command line overrides the default command(CMD) given in Dockerfile.
In this case docker thinks that /etc/hosts is a command and tries to execute it. As it is not a executable file, we are seeing permission denied error.
Let's try the same experiment on busy_entry_image image. We'll run busy_entry_image image and pass /etc/hosts a command line argument and give a name busy_entry_container_2 to inspect it.
[[email protected] 02.BusyboxWithEntryPoint]$ docker run --name busy_entry_container_2 busy_entry_image /etc/hosts root:x:0:0:root:/root:/bin/sh daemon:x:1:1:daemon:/usr/sbin:/bin/false bin:x:2:2:bin:/bin:/bin/false sys:x:3:3:sys:/dev:/bin/false sync:x:4:100:sync:/bin:/bin/sync mail:x:8:8:mail:/var/spool/mail:/bin/false www-data:x:33:33:www-data:/var/www:/bin/false operator:x:37:37:Operator:/var:/bin/false nobody:x:99:99:nobody:/home:/bin/false 127.0.0.1 localhost ::1 localhost ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters 172.17.0.2 b4133689279d
Cool!!! We can see contents of both /etc/passwd and /etc/hosts file getting printed.
Let's inspect this container,
[[email protected] 02.BusyboxWithEntryPoint]$ docker inspect busy_entry_container_2 [ { "Id": "7d0be615dac4624fda3e690485719a32921042a1f32ad738856cf0425eeb1f0b", "Created": "2016-10-21T12:41:21.194580336Z", "Path": "cat", "Args": [ "/etc/passwd", "/etc/hosts" ], "State": { "Status": "exited", "Running": false, "Paused": false, "Restarting": false, "OOMKilled": false, "Dead": false, "Pid": 0, "ExitCode": 0, "Error": "", "StartedAt": "2016-10-21T12:41:21.327221171Z", "FinishedAt": "2016-10-21T12:41:21.339957393Z" }, "Image": "sha256:22aa602beefdaee9ee54ee218d099817cfba48fbea38fb943c85fb141332fb63", ... ]
In this case, the argument passed in command line is getting appended to the Args
list but the Path
is unchanged.
This is the difference between CMD and ENTRYPOINT in a Dockerfile.
CMD
is meant to supply the default arguments while running the container.
ENTRYPOINT
is meant to specify the executable that will be invoked when the container is run.
CMD
and ENTRYPOINT
can be used in conjuction.
Look at the Dockerfile which has both CMD and ENTRYPOINT.
[[email protected] 03.BusyboxWithEntryAndCMD]$ cat Dockerfile FROM busybox:latest CMD ["/etc/passwd", "/etc/hosts"] ENTRYPOINT ["cat"]
Let's build an image out of it can call it as busy_cmd_entry_image
[[email protected] 03.BusyboxWithEntryAndCMD]$ docker build -t busy_cmd_entry_image . Sending build context to Docker daemon 2.048 kB Step 1 : FROM busybox:latest ---> 2b8fd9751c4c Step 2 : CMD /etc/passwd /etc/hosts ---> Running in 4d583424e639 ---> f7a96ac1bf4c Removing intermediate container 4d583424e639 Step 3 : ENTRYPOINT cat ---> Running in 057321f9e2a0 ---> 8b6517ac039e Removing intermediate container 057321f9e2a0 Successfully built 8b6517ac039e
We'll run the image without any argument
[[email protected] 03.BusyboxWithEntryAndCMD]$ docker run --name busy_cmd_entry_container busy_cmd_entry_image root:x:0:0:root:/root:/bin/sh daemon:x:1:1:daemon:/usr/sbin:/bin/false bin:x:2:2:bin:/bin:/bin/false sys:x:3:3:sys:/dev:/bin/false sync:x:4:100:sync:/bin:/bin/sync mail:x:8:8:mail:/var/spool/mail:/bin/false www-data:x:33:33:www-data:/var/www:/bin/false operator:x:37:37:Operator:/var:/bin/false nobody:x:99:99:nobody:/home:/bin/false 127.0.0.1 localhost ::1 localhost ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters 172.17.0.2 7e549359bd1c
Let's run it passing a command line argument
[[email protected] 03.BusyboxWithEntryAndCMD]$ docker run --name busy_cmd_entry_container_2 busy_cmd_entry_image /etc/resolv.conf nameserver 10.0.2.3
The onliner of this article is,
ENTRYPOINT
is meant to provide the executable and CMD
is to pass the default arguments to the executable.
Happy hacking!!!
References:
Comments
comments powered by Disqus