Technology Trends and Talk

Main menu

Monthly Archives: December 2015

When looking at the instructions that are available for use in a Dockerfile there are a few that may initially appear to be redundant (or, at least, have significant overlap). We’ve already covered ADD and COPY and now we’re going to look at ENTRYPOINT and CMD.

Both ENTRYPOINT and CMD allow you to specify the startup command for an image, but there are subtle differences between them. There are many times where you’ll want to choose one or the other, but they can also be used together. We’ll explore all these scenarios in the sections below.

ENTRYPOINT or CMD

Ultimately, both ENTRYPOINT and CMD give you a way to identify which executable should be run when a container is started from your image. In fact, if you want your image to be runnable (without additional docker run command line arguments) youmust specify an ENTRYPOINT or CMD.

Trying to run an image which doesn’t have an ENTRYPOINT or CMD declared will result in an error

Many of the Linux distro base images that you find on the Docker Hub will use a shell like /bin/sh or /bin/bash as the the CMD executable. This means that anyone who runs those images will get dropped into an interactive shell by default (assuming, of course, that they used the -i and -t flags with the docker run command).

This makes sense for a general-purpose base image, but you will probably want to pick a more specific CMD or ENTRYPOINT for your own images.

Overrides

The ENTRYPOINT or CMD that you specify in your Dockerfile identify the default executable for your image. However, the user has the option to override either of these values at run time.

For example, let’s say that we have the following Dockerfile

FROM ubuntu:trusty
CMD ping localhost

If we build this image (with tag “demo”) and run it we would see the following output:

You can see that the ping executable was run automatically when the container was started. However, we can override the default CMD by specifying an argument after the image name when starting the container:

$ docker run demo hostname
6c1573c0d4c0

In this case, hostname was run in place of ping

The default ENTRYPOINT can be similarly overridden but it requires the use of the --entrypoint flag:

$ docker run --entrypoint hostname demo
075a2fa95ab7

Given how much easier it is to override the CMD, the recommendation is use CMD in your Dockerfile when you want the user of your image to have the flexibility to run whichever executable they choose when starting the container. For example, maybe you have a general Ruby image that will start-up an interactive irb session by default (CMD irb) but you also want to give the user the option to run an arbitrary Ruby script (docker run ruby ruby -e 'puts "Hello"')

In contrast, ENTRYPOINT should be used in scenarios where you want the container to behave exclusively as if it were the executable it’s wrapping. That is, when you don’t want or expect the user to override the executable you’ve specified.

There are many situations where it may be convenient to use Docker as portable packaging for a specific executable. Imagine you have a utility implemented as a Python script you need to distribute but don’t want to burden the end-user with installation of the correct interpreter version and dependencies. You could package everything in a Docker image with an ENTRYPOINT referencing your script. Now the user can simplydocker run your image and it will behave as if they are running your script directly.

Of course you can achieve this same thing with CMD, but the use of ENTRYPOINT sends a strong message that this container is only intended to run this one command.

The utility of ENTRYPOINT will become clearer when we show how you can combine ENTRYPOINT and CMD together, but we’ll get to that later.

Shell vs. Exec

Both the ENTRYPOINT and CMD instructions support two different forms: the shell formand the exec form. In the example above, we used the shell form which looks like this:

CMD executable param1 param2

When using the shell form, the specified binary is executed with an invocation of the shell using /bin/sh -c. You can see this clearly if you run a container and then look at the docker ps output:

Here we’ve run the “demo” image again and you can see that the command which was executed was /bin/sh -c 'ping localhost'.

This appears to work just fine, but there are some subtle issues that can occur when using the shell form of either the ENTRYPOINT or CMD instruction. If we peek inside our running container and look at the running processes we will see something like this:

Note how the process running as PID 1 is not our ping command, but is the /bin/shexecutable. This can be problematic if we need to send any sort of POSIX signals to the container since /bin/sh won’t forward signals to child processes (for a detailed write-up, see Gracefully Stopping Docker Containers).

Beyond the PID 1 issue, you may also run into problems with the shell form if you’re building a minimal image which doesn’t even include a shell binary. When Docker is constructing the command to be run it doesn’t check to see if the shell is available inside the container — if you don’t have /bin/sh in your image, the container will simply fail to start.

A better option is to use the exec form of the ENTRYPOINT/CMD instructions which looks like this:

CMD ["executable","param1","param2"]

Note that the content appearing after the CMD instruction in this case is formatted as a JSON array.

When the exec form of the CMD instruction is used the command will be executed without a shell.

Let’s change our Dockerfile from the example above to see this in action:

FROM ubuntu:trusty
CMD ["/bin/ping","localhost"]

Rebuild the image and look at the command that is generated for the running container:

Now /bin/ping is being run directly without the intervening shell process (and, as a result, will end up as PID 1 inside the container).

Whether you’re using ENTRYPOINT or CMD (or both) the recommendation is to always use the exec form so that’s it’s obvious which command is running as PID 1 inside your container.

ENTRYPOINT and CMD

Up to this point, we’ve discussed how to use ENTRYPOINT or CMD to specify your image’s default executable. However, there are some cases where it makes sense to use ENTRYPOINT and CMD together.

Combining ENTRYPOINT and CMD allows you to specify the default executable for your image while also providing default arguments to that executable which may be overridden by the user. Let’s look at an example:

Note that the command which was executed is a combination of the ENTRYPOINT and CMD values that were specified in the Dockerfile. When both an ENTRYPOINT and CMD are specified, the CMD string(s) will be appended to the ENTRYPOINT in order to generate the container’s command string. Remember that the CMD value can be easily overridden by supplying one or more arguments to `docker run` after the name of the image. In this case we could direct our ping to a different host by doing something like this:

Running the image starts to feel like running any other executable — you specify the name of the command you want to run followed by the arguments you want to pass to that command.

Note how the -c 3 argument that was included as part of the ENTRYPOINT essentially becomes a “hard-coded” argument for the ping command (the -c flag is used to limit the ping count to the specified number). It’s included in each invocation of the image and can’t be overridden in the same way as the CMD parameter.

Always Exec

When using ENTRYPOINT and CMD together it’s important that you always use the exec form of both instructions. Trying to use the shell form, or mixing-and-matching the shelland exec forms will almost never give you the result you want.

The table below shows the command string that results from combining the various forms of the ENTRYPOINT and CMD instructions.

The only one of these that results in a valid command string is when the ENTRYPOINT and CMD are both specified using the exec form.

Conclusion

If you want your image to actually do anything when it is run, you should definitely configure some sort of ENTRYPOINT or CMD in you Dockerfile. However, remember that they aren’t mutually exclusive. In many cases you can improve the user experience of your image by using them in combination.

No matter how you use these instructions you should always default to using the exec form.