Docker image authors have multiple concerns for their images:
There are many details which can affect the answers to these questions. We’ve created this document to help image authors create images for which the answer is ‘yes’ across the board.
There are two ways to create a docker image:
Most image authors will find that using a Dockerfile is a much easier way to repeatably create an image. A Dockerfile is made up of instructions, several of which will be discussed in this guide.
You can find the complete Dockerfile instruction reference here.
The MAINTAINER instruction sets the Author field of the image. This is useful for providing an email contact for your users if they have questions for you.
There are many details of how the Dockerfile CMD and ENTRYPOINT instructions work, and expressing exactly what you want is key to ensuring users of your image have the right experience. These two instructions are similar in that they both specify commands that run in an image, but there’s an important difference: CMD simply sets a command to run in the image if no arguments are passed to docker run, while ENTRYPOINT is meant to make your image behave like a binary. The rules are essentially:
Be careful with using ENTRYPOINT; it will make it more difficult to get a shell inside your image. While it may not be an issue if your image is designed to be used as a single command, it can frustrate or confuse users that expect to be able to use the idiom:
# docker run -i -t/bin/bash
Then entrypoint of an image can be changed by the --entrypoint option to docker run. If you choose to set an entrypoint, consider educating your users on how to get a shell in your image with:
# docker run -i --entrypoint /bin/bash -t
There are also two different forms of both CMD and ENTRYPOINT: array and string forms:
The -c option affects how the shell interprets arguments. We recommend reading up on this option’s behavior or using array syntax.
Many images use wrapper scripts to do some setup before starting a process for the software being run. It is important that if your image uses such a script, that script should use exec so that the script’s process is replaced by your software. If you do not use exec, then signals sent by docker will go to your wrapper script instead of your software’s process. This is not what you want - as illustrated by the following example:
Say that you have a wrapper script that starts a process for a server of some kind. You start your container (using docker run -i), which runs the wrapper script, which in turn starts your process. Now say that you want to kill your container with CTRL+C. If your wrapper script used exec to start the server process, docker will send SIGINT to the server process, and everything will work as you expect. If you didn’t use exec in your wrapper script, docker will send SIGINT to the process for the wrapper script - and your process will keep running like nothing happened.
The EXPOSE instruction makes a port in the container available to the host system and other containers. While it is possible to specify that a port should be exposed with a docker run invocation, using the EXPOSE instruction in a Dockerfile makes it easier for both humans and software to use your image by explicitly declaring the ports your software needs to run:
For more information about ports and docker, please check out the docker documentation.
The VOLUME instruction and the -v option to docker run tell docker to store files in a directory on the host instead of in the container’s file system. Volumes give you a couple of key benefits:
Volumes obey different rules from normal files in containers:
It is possible to share the volumes created by one container with another by using the --volumes-fromparameter to docker run. For example, say we make a container named ‘ContainerA’ that has a volume:
# docker run -i -v /var/volume1-name 'ContainerA' -t fedora /bin/bash
We can share the volumes from this container with another container:
# docker run -i --volumes-from ContainerA -t fedora /bin/bash
In ContainerB, we will see /var/volume1 from ContainerA. For more information about sharing volumes, please check out the docker documentation
We recommend that you use volumes in the following use-cases:
By default docker containers run as root. A docker container running as root has full control of the host system. As docker matures, more secure default options may become available. For now, requiring root is dangerous for others and may not be available in all environments. Your image should use the USER instruction to specify a non-root user for containers to run as. If your software does not create its own user, you can create a user and group in the Dockerfile as follows:
RUN groupadd -r swuser -g 433 && \ useradd -u 431 -r -g swuser -d-s /sbin/nologin -c "Docker image user" swuser && \ chown -R swuser:swuser
The default user in a Dockerfile is the user of the parent image. For example, if your image is derived from an image that uses a non-root user swuser, then RUN commands in your Dockerfile will run as swuser.
If you need to run as root, you should change the user to root at the beginning of your Dockerfile then change back to the correct user with another USER instruction:
USER root RUN yum install -yUSER swuser