Dockerfiles, CMD, ENTRYPOINT and input syntax

6,129 views
Skip to first unread message

James Turnbull

unread,
Jan 6, 2014, 2:55:01 PM1/6/14
to docker-user, docker-dev
Hi all

There are currently two syntaxes for CMD and ENTRYPOINT (ignoring the
interactions between the two instructions like passing options from CMD
to ENTRYPOINT).

1. The exec format (JSON-style):

CMD ["executable", "flag" ]

2. The shell format

CMD /bin/true flag

Having both behaviors is confusing and unintuitive. IMHO We should
settle on a single behavior preferably by having a lexer that recognizes
and can parse appropriate shell commands and their flags from a variety
of shells in either instruction. Other alternatives include settling on
the JSON exec format. Or open to other ideas.

As part of implementing this lexer we should deprecate existing
behaviors carefully as this is a large breaking change. I'd suggest:

1. Major release with new syntax generates deprecation warning if old
syntax is used.
2. Next major release removes old syntax and throws error message if used.

I've opened a ticket for comment/discussion:

https://github.com/dotcloud/docker/issues/3466

Regards

James



--
* The Docker Book (http://dockerbook.com)
* The LogStash Book (http://logstashbook.com)
* Pro Puppet (http://tinyurl.com/ppuppet2 )
* Pro Linux System Administration (http://tinyurl.com/linuxadmin)
* Pro Nagios 2.0 (http://tinyurl.com/pronagios)
* Hardening Linux (http://tinyurl.com/hardeninglinux)

Philippe Lafoucrière

unread,
Jan 7, 2014, 3:32:42 AM1/7/14
to docke...@googlegroups.com, docker-user, ja...@lovedthanlost.net
More generally, I find the ```CMD``` and  ``ENTRYPOINT``` confusing for beginners...
What's the point of having 2 instructions, for the same thing?
Even worse, the 2 are sometimes mixed (according to latest doc):

    CMD ["param1","param2"] (as default parameters to ENTRYPOINT)

Why don't keep only ```CMD``? One can argue that entrypoint is "readonly", but I can easily commit the same container with a different entrypoint.

I would strongly suggest to drop ENTRYPOINT and keep the single CMD. When no command is specified, CMD is being used as default.

gwfran

unread,
Jan 7, 2014, 8:50:53 PM1/7/14
to docke...@googlegroups.com, docker-user, ja...@lovedthanlost.net
My (limited) understanding is that ENTRYPOINT is really just being used to execute a command upon launch of the container.  Since CMD can only be executed once per dockerfile, people are using this as a workaround.

I'm not sure why you can't use multiple CMD (or ENTRYPOINT, for that matter) where the launch of the container would just loop through the list of commands, execute them, and then return the state of the last CMD.  However, it is a limitation of the current implementation.  Since we have the ability to view logs from the launched container, the output of multiple commands should be viewable if one of the commands were to fail with or without terminating the container.

   

Tianon

unread,
Jan 7, 2014, 10:11:31 PM1/7/14
to docke...@googlegroups.com, docker-user, ja...@lovedthanlost.net
I find ENTRYPOINT to be legitimately useful in my image building.  CMD is just a convenience method for supplying some defaults.

See https://github.com/tianon/dockerfiles/blob/master/mongodb-mms for a good use of ENTRYPOINT that also uses CMD.

Philippe Lafoucrière

unread,
Jan 8, 2014, 3:37:30 AM1/8/14
to Tianon, docke...@googlegroups.com, docker-user, ja...@lovedthanlost.net
Yes, you're both right.
But having (from the doc):

CMD ["executable","param1","param2"] (like an exec, preferred form)

is therefore a bit confusing. It allows scenarios like:

# I force the command to run in the container to be /entrypoint.sh
# ["python", "agent.py"] being default parameters to /entrypoint.sh
# Params can be overridden when running
ENTRYPOINT ["/entrypoint.sh"]
CMD ["python", "agent.py"]

# The default command of this container is ["/entrypoint.sh"], with
params ["python", "agent.py"]
# both command and params can be overriden
CMD [ "/entrypoint.sh", "python", "agent.py"]

I know it's working, I'm just saying I can't understand these 3 lines
without reading carefully twice the doc.
It's probably just me :)

Tianon Gravi

unread,
Jan 8, 2014, 3:39:48 AM1/8/14
to Philippe Lafoucrière, docker-dev, docker-user, ja...@lovedthanlost.net
Well, the magic really comes in when you want to run a different command, like "bash".

I use that image and "docker run <image> bash" (instead of how I'd normally do "docker run <image>" with no command argument), and I get a shell in the container, but entrypoint.sh has also run correctly without me having to think about it, so my settings.py file includes the necessary substitutions already.  Now if I want to start the agent inside, I can just run "python agent.py" from the shell that I've opened.

- Tianon

Philippe Lafoucrière

unread,
Jan 8, 2014, 4:16:27 AM1/8/14
to Tianon Gravi, docker-dev, docker-user, ja...@lovedthanlost.net
Yes, I understand that. I'm not bitching about the feature, I'm
talking about the keywords being used.
Something like DEFAULT_RUN_PARAMS would have been more explicit to me.
Just my 2c :)

--
Philippe Lafoucrière - CEO
http://www.tech-angels.com
main : +33 (0) 970 444 643
mobile : +33 (0) 6 72 63 75 40
fax : +33 (0) 9 72 12 78 75

ro...@roovoweb.com

unread,
Jan 8, 2014, 4:50:07 AM1/8/14
to docke...@googlegroups.com, Tianon, docker-user, ja...@lovedthanlost.net
On Wednesday, January 8, 2014 8:37:30 AM UTC, Philippe Lafoucrière wrote:
> I know it's working, I'm just saying I can't understand these 3 lines
> without reading carefully twice the doc.
> It's probably just me :)

It's not just you - I've been messing around with docker for a wee while now and only yesterday had a first stab at playing around with/understanding CMD and ENTRYPOINT and I must say it wasn't immediately obvious what was going on and how I could/should use them, so I do think that there must be a better way.

As far as I can tell (I hope I've got it right) it seems that you use CMD on it's own if you want to be able to overwrite the whole command when doing a docker run.  You use ENTRYPOINT on it's own if you want to always run the specified command when running the container and anything added when doing a docker run is treated as additional arguments to that command.  You use both if you want to specify some default arguments to ENTRYPOINT that can be overwritten when using docker run.

Could all this not be handled just using the ENTRYPOINT directive but adding some form of optional parameter syntax, e.g.

ENTRYPOINT [ "/entrypoint.sh", { defaults: ["python", "agent.py"] } ]

Then we wouldn't need the CMD directive at all. If you need to override the actual command then use the -entrypoint option to docker run instead of using CMD.  If -entrypoint is given an empty string it would have the effect of totally ignoring the ENTRYPOINT (so the full command could be specified on the end of the docker run command.  If entrypoint is given a non-empty string then it replaces the contaiters entry point.  Or I guess the -entrypoint options could be replaced with an -ignnore-entrypoint that dwas just a flag.

On Wednesday, January 8, 2014 1:50:53 AM UTC, gwfran wrote:
> I'm not sure why you can't use multiple CMD (or ENTRYPOINT, for that matter)
> where the launch of the container would just loop through the list of
> commands, execute them, and then return the state of the last CMD.

The complications I can see with this would be what to do if one of the commands fails, do you exit immediately and return it's error code or do you move on to the next command (either behaviour could have undesirable effects depending what you actually want to happen - if you handle this yourself in your single script - it's something you can decide and docker's life is kept simple).  Also if you want to stop the container using docker stop and you could at the time be running any one of multiple commands how does docker know which process to send a SIGTERM to?

roovo

Solomon Hykes

unread,
Jan 8, 2014, 5:49:19 AM1/8/14
to Philippe Lafoucrière, ja...@lovedthanlost.net, Tianon Gravi, docker-dev, docker-user
I agree the keywords are not clear. The reason is quite simple: CMD came first as a simple default command, we only thought of ENTRYPOINT later. We realized people wanted to produce containers which behaved like binaries: where users could pass arbitrary arguments, get a usage message etc. But to pass arguments users needed to know the name of the binary to call, which at best meant typing annoying redundant commands, and at worst having to parse the output of "docker inspect" to find the binary from CMD.

So we added ENTRYPOINT! (I believe it was an early contribution by Mike Crosby). But we had to support the existing CMD in parallel and define an acceptable way for them to interact.

Philippe Lafoucrière

unread,
Jan 8, 2014, 8:24:31 AM1/8/14
to Solomon Hykes, James Turnbull, Tianon Gravi, docker-dev, docker-user
On Wed, Jan 8, 2014 at 11:49 AM, Solomon Hykes <solomo...@docker.com> wrote:
> So we added ENTRYPOINT! (I believe it was an early contribution by Mike
> Crosby). But we had to support the existing CMD in parallel and define an
> acceptable way for them to interact.

Make sense! Thanks for the explanation.
Anyway, a syntax update would be appreciated...
I would keep CMD as it, and remove the ability to define params for ENTRYPOINT.
At the same time, ENTRYPOINT could be something like:

ENTRYPOINT ["cmd", "param1","param2"], ["default_param_1", ""default_param_2"]

or

# default params for entrypoint
DEFAULTS ["default_param_1", ""default_param_2"]
ENTRYPOINT ["cmd", "param1","param2"]

roovo

unread,
Jan 8, 2014, 10:08:03 AM1/8/14
to docke...@googlegroups.com, Solomon Hykes, James Turnbull, Tianon Gravi, docker-user
wouldn't

ENTRYPOINT ["cmd", "param1","param2"], { defaults: ["default_param_1", ""default_param_2"] }

be more intent revealing whilst not introducing another directive? - the best of both the above

Brian Morearty

unread,
Jan 8, 2014, 10:29:09 AM1/8/14
to Philippe Lafoucrière, Solomon Hykes, James Turnbull, Tianon Gravi, docker-dev, docker-user

My 2 cents on the topic of JSON syntax vs. shell syntax. I’ve found some features of the shell syntax are useful in Dockerfiles. Here are a couple of examples:

  • When launching a container that has an updated release of a Rails app, it’s useful to migrate the database to the latest schema. (And create the database, if it’s the first release.) Here’s how I’ve done it. Not pretty, but I don’t know a better way that allows the commands to stay inside the Dockerfile rather than an external shell script:
CMD bundle exec rake db:create && \
    bundle exec rake db:migrate && \
    bundle exec rails server

I suppose this can still be done in JSON syntax by explicitly calling the shell, so it’s still possible but harder to get right. Something like:

CMD ['sh', '-c', 'bundle exec rake db:create && \
                  bundle exec rake db:migrate && \
                  bundle exec rails s']

(I had to look at the sh man page to remind myself that -c was the right cmd-line arg, which goes to show you that it’s harder to get right.)

  • With JSON syntax, it can be confusing to try to remember where to break words in the array. A real-life example:
CMD ["su", "postgres", "-c", "/usr/lib/postgresql/$PG_VERSION/bin/postgres -D /var/lib/postgresql/$PG_VERSION/main/ -c config_file=/etc/postgresql/$PG_VERSION/main/postgresql.conf"]

It’s true that even in shell syntax you’d have to remember to put quotes around the command that su executes:

CMD su postgres -c "/usr/lib/postgresql/$PG_VERSION/bin/postgres -D /var/lib/postgresql/$PG_VERSION/main/ -c config_file=/etc/postgresql/$PG_VERSION/main/postgresql.conf"

but that’s something you’re more used to seeing in scripts, so it doesn’t seem quite so mind-bending.

So in the end, I think it’s nice to have both syntaxes available.

I believe the more confusing point, which I think is in greater need of a change, is the other one people have been discussing: the CMD and ENTRYPOINT instructions. Tianon’s example was pretty cool—having an entrypoint that does some configuration and then runs the CMD. I could be convinced to move my rake db:create && rake db:migrate example to a shell script that runs those two commands as an entrypoint and then runs the cmd. But I do agree with the point someone made, that it’s not immediately obvious what it’s doing. The power is wonderful but if the Dockerfile syntax can be changed to something that makes it more clear what’s going on, that would be nice.

Brian Morearty
Hands on with Docker
http://handsonwith.com/




--
You received this message because you are subscribed to the Google Groups "docker-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to docker-dev+...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Brian Morearty

unread,
Jan 8, 2014, 12:12:10 PM1/8/14
to Philippe Lafoucrière, Solomon Hykes, James Turnbull, Tianon Gravi, docker-dev, docker-user

James, can you please elaborate on this part of your original question?

a lexer that recognizes and can parse appropriate shell commands and their flags from a variety of shells in either instruction

What did you mean? Perhaps an example would help.

I have one more thought on this part:

Other alternatives include settling on the JSON exec format.

For me, the JSON exec format is annoying to type. It’s hard to make your fingers get it right with all the brackets, quotes, and commas—even when your brain knows what to do.

So if the desire is to have a single syntax and you prefer not to call out to the shell implicitly, I would propose this: the new syntax should be unbracketed and unquoted, but should be tokenized and exec’d as if it were the JSON syntax. For example:

# This gets exec'd, not run in the shell:
CMD executable flag

As a bonus user-friendly feature, it could watch for unquoted shell tokens (&&,||,|,&,<,>,2>, etc.) and give a warning:

CMD command1 && command2

$ docker build .
WARNING: for shell syntax use:
  sh -c "command1 && command2"
or use quotes to hide this warning:
  command1 "&&" command2

And, finally, about quotes: if you do end up keeping the JSON syntax, please add support for single-quoted strings. It’s unintuitive that it looks like JSON but only supports double-quoted strings. See this post for an example of the trouble it can cause.

Brian Morearty
Hands on with Docker trainer

James Turnbull

unread,
Jan 8, 2014, 1:10:08 PM1/8/14
to docker-dev, docker-user
Brian Morearty wrote:
> James, can you please elaborate on this part of your original question?
>
> a lexer that recognizes and can parse appropriate shell commands and
> their flags from a variety of shells in either instruction
>
> What did you mean? Perhaps an example would help.

Pretty much what you eluded to later in the email: something tokenized
and exec'ed that handles special characters, single/double quoted, etc.

Brian Morearty

unread,
Jan 9, 2014, 4:18:35 PM1/9/14
to James Turnbull, docker-dev, docker-user

Come to think of it, Upstart’s approach for “exec” is a bit nicer than what I suggested:

Note that if this command-line contains any shell meta-characters, it will be passed through a shell prior to being executed. This ensures that shell redirection and variable expansion occur as expected.



On Wed, Jan 8, 2014 at 10:22 AM, Brian Morearty <br...@morearty.org> wrote:
Thanks.

I should clarify that when I said "give a warning" I really meant "give an error." :-)

When docker build shows warnings, they are hard to see because they're typically in the middle of a bunch of other output.

Brian



Brian Morearty

unread,
Jan 8, 2014, 1:22:10 PM1/8/14
to James Turnbull, docker-dev, docker-user
Thanks.

I should clarify that when I said "give a warning" I really meant "give an error." :-)

When docker build shows warnings, they are hard to see because they're typically in the middle of a bunch of other output.

Brian

On Wed, Jan 8, 2014 at 10:10 AM, James Turnbull <ja...@lovedthanlost.net> wrote:
Reply all
Reply to author
Forward
0 new messages