1

I have a Dockerfile that falls into the exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd category in the matrix found in Understand how CMD and ENTRYPOINT interact. The behavior is not what I'm expecting. I was expecting /bin/sh -c exec_cmd p1_cmd to evaluate first then get passed into exec_entry p1_entry. What I am observing is that /bin/sh -c exec_cmd p1_cmd literally gets passed into exec_entry p1_entry which I think is funny.

To provide more context, I am specifically deriving a new Dockerfile from an existing Dockerfile where the parent has:

ENTRYPOINT ["/bin/registrator"]

I want to pass in specific command-line parameters from my Dockerfile:

FROM gliderlabs/registrator:v7
CMD echo "-ip=$EXTERNAL_IP consul://$CONSUL_HOST"

When I run my Docker image in a container:

$ docker run --rm --name=test-registrator --volume=/var/run/docker.sock:/tmp/docker.sock -e "EXTERNAL_IP=<some-ip>" -e "CONSUL_HOST=<some-consul-hostname>:8500" my/registrator

I get the following error:

2016/12/28 19:20:46 Starting registrator v7 ...
Extra unparsed arguments:
  -c echo "-ip=$EXTERNAL_IP consul://$CONSUL_HOST"
Options should come before the registry URI argument.

Usage of /bin/registrator:
  /bin/registrator [options] <registry URI>

-cleanup=false: Remove dangling services
-deregister="always": Deregister exited services "always" or "on-success"
-internal=false: Use internal ports instead of published ones
-ip="": IP for ports mapped to the host
-resync=0: Frequency with which services are resynchronized
-retry-attempts=0: Max retry attempts to establish a connection with the backend. Use -1 for infinite retries
-retry-interval=2000: Interval (in millisecond) between retry-attempts.
-tags="": Append tags for all registered services
-ttl=0: TTL for services (default is no expiry)
-ttl-refresh=0: Frequency with which service TTLs are refreshed

This means that -c echo "-ip=$EXTERNAL_IP consul://$CONSUL_HOST" is literally getting passed into /bin/registrator as the parameter.

Am I doing something wrong or is this a limitation of the use case where /bin/sh -c exec_cmd p1_cmd does not get evaulated first before getting passed into the ENTRYPOINT? If the latter is true, then can you also explain the usefulness of this use case?

mikehwang
  • 490
  • 3
  • 10

2 Answers2

2

Yes. This is exactly how it is supposed to work.

The value of CMD is just passed as a parameter to the ENTRYPOINT.

Main difference between CMD and ENTRYPOINT is that CMD just provides default command argument to the ENTRYPOINT program and it's usually overridden by the arguments to run command. On the other hand you have to redefine ENTRYPOINT explicitly with --entrypoint option if you want different command to be executed.

Also, notice there is a difference how things are executed depending on the way ENTRYPOINT and CMD are defined in the Dockerfile. When they are defined as an array in the form of ['arg1', 'arg2'] this array is passed as is to the ENTRYPOINT command, with the first element of ENTRYPOINT being the program being executed. In the other case when they are defined as a simple string like arg1 arg2 this string is first passed is prepended by "/bin/sh -c" note that Docker does not execute /bin/sh and returns the result of evaluation back, that string itself is passed to the ENTRYPOINT program.

So in your case you have to use array method of passing the arguments:

CMD  [-ip, $EXTERNAL_IP,  consul://$CONSUL_HOST]
Vlad
  • 9,180
  • 5
  • 48
  • 67
  • Then I don't understand what is the point for this use case `exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd`. This would require the `exec_entry` application to handle the command which I think is strange. – mikehwang Dec 28 '16 at 20:18
  • Ahh, missed that part. It is actually degenerate case when CMD is defined in string form, but your ENTRYPOINT is not a shell. I don't think that it has any use. The only use this syntax makes sense is when your ENTRYPOINT is a shell (bash, sh, csh) and you have command line like this "ls *.c" this way the globs will work. Otherwise ls will just get '*.c' as an argument and will look for a file named '*.c' not the set of files that end with '.c' – Vlad Dec 28 '16 at 21:47
  • Thanks for your input! I want to point out that (although it'd be nice) `CMD [-ip, $EXTERNAL_IP, consul://$CONSUL_HOST]` does not work because the environment variables don't resolve. See [this article](http://goinbigdata.com/docker-run-vs-cmd-vs-entrypoint/) under *Shell form* then *Exec form*. – mikehwang Dec 29 '16 at 15:30
  • Yes you right. It's shell's Job to resolve globs and environment variables. In your case I'd just redefine ENTRYPOINT in your own image to point to /bin/sh and add a small shell script with /bin/registrator -ip=$EXTERNAL_IP consul://$CONSUL_HOST this way you'll get what you want. – Vlad Dec 29 '16 at 16:22
0

Are you struggling to understand the difference between CMD and ENTRYPOINT in Dockerfile? Check out this article, 'Understanding the Difference Between CMD and ENTRYPOINT in Dockerfile:A Comprehensive Guide,' at https://code-craft.fr/understanding-the-difference-between-cmd-and-entrypoint-in-dockerfile-a-comprehensive-guide/. It provides a comprehensive guide on the differences and best practices for effective use. This may help you gain a better understanding of these crucial components in Dockerfile.

Khaled Boussoffara
  • 1,567
  • 2
  • 25
  • 53