Docker Dockerfile
Jakob Jenkov |
A Docker Dockerfile contains a set of instructions for how to build a Docker image. The Docker build command executes the Dockerfile and builds a Docker image from it. This Dockerfile tutorial explains how to write a Dockerfile and build it into a Docker image.
Dockerfile Benefits
A Dockerfile captures the recipe for building a Docker image in writing. A Docker image typically consists of:
- A base Docker image on top of which to build your own Docker image.
- A set of tools and applications to be installed in the Docker image.
- A set of files to be copied into the Docker image (e.g configuration files).
- Possibly a network (TCP / UDP) port (or more) to be opened for traffic in the firewall.
- etc.
First of all, capturing all this in writing in a Dockerfile means that You don't have to remember how to setup a new installation of your application, with OS requirements, applications to install, files to copy, network ports to open up for etc. It's all described in the Dockerfile.
Additionally, letting Docker build your Docker image from a Dockerfile means that you don't have to do this manual, tedious and error prone job. Docker takes of it for you, so you can do something else in the meantime. This is both easier, faster, and less error prone.
Third, it's really easy to share a Dockerfile with someone else, and have them be able to build the Docker image.
Fourth, a Dockerfile can easily be stored in a version control system like Git, so you can track changes to the Dockerfile (the server and application configuration) over time. Version control systems typically also makes it easier for multiple people to collaborate - e.g. on a Dockerfile, as well as share the Dockerfile later on.
Dockerfile Structure
A Dockerfile consists of a set of instructions. Each instruction consists of a command followed by arguments to that command, similar to command line executables. Here is a simple example of a Dockerfile:
# The base image FROM ubuntu:latest # More instructions here that install software and copy files into the image. COPY /myapp/target/myapp.jar /myapp/myapp.jar # The command executed when running a Docker container based on this image. CMD echo Starting Docker Container
Docker Base Image
A Docker image consists of layers. Each layer adds something to the final Docker image. Each layer is actually a separate Docker image. Thus, your Docker image consists of one or more underlying Docker images, on top of which you add your own layers.
When you specify your own Docker image via a Dockerfile you typically start with a Docker base image. This is another Docker image on top of which you want your own Docker image to be built. The Docker base image you are using may itself consist of multiple layers, and can itself be based on another base image etc., until you get down to the most basic Docker image you can create - a raw Linux container image with no special settings applied.
You specify the base image of a Docker image in your Dockerfile using the FROM command, as
explained in the following section.
MAINTAINER
The Dockerfile MAINTAINER command is simply used to tell who is maintaining this Dockerfile.
Here is an example:
MAINTAINER Joe Blocks <joe@blocks.com>
The MAINTAINER instruction is not often used though, since that kind of information is also often
available in GIT repositories and elsewhere.
FROM
The Dockerfile FROM command specifies the base image of your Docker images. If you want to start
with a bare Linux image, you can use this FROM command:
# The base image FROM ubuntu:latest
CMD
The CMD command specifies the command line command to execute when a Docker container is
started up which is based on the Docker image built from this Dockerfile. Here are a few Dockerfile CMD
examples:
CMD echo Docker container started.
This example just prints the text Docker container started when the Docker container is started.
The next CMD example runs a Java application:
CMD java -cp /myapp/myapp.jar com.jenkov.myapp.MainClass arg1 arg2 arg3
COPY
The Dockerfile COPY command copies one or more files from the Docker host (the computer building
the Docker image from the Dockerfile) into the Docker image. The COPY command can copy both
a file or a directory from the Docker host to the Docker image. Here is a Dockerfile COPY example:
COPY /myapp/target/myapp.jar /myapp/myapp.jar
This example copies a single file from the Docker host at /myapp/target/myapp.jar to the
Docker image at /myapp/myapp.jar . The first argument is the Docker host path (where to copy from)
and the second argument is the Docker image path (where to copy to).
You can also copy a directory from the Docker host to the Docker image. Here is an example:
COPY /myapp/config/prod /myapp/config
This example copies the directory /myapp/config/prod from the Docker host to the
/myapp/config directory in the Docker image.
You can als copy multiple files into a single directory in the Docker image using the COPY command.
Here is an example:
COPY /myapp/config/prod/conf1.cfg /myapp/config/prod/conf2.cfg /myapp/config/
This example copies the two files /myapp/config/prod/conf1.cfg and
/myapp/conig/prod/conf2.cfg into the Docker image directory /myapp/config/ .
Notice how the destination directory has to end with a / (slash) for this to work.
ADD
The Dockerfile ADD instruction works in the same way as the COPY instruction with a few
minor differences:
- The
ADDinstruction can copy and extract TAR files from the Docker host to the Docker image. - The
ADDinstruction can download files via HTTP and copy them into the Docker image.
Here are a few Dockerfile ADD examples:
ADD myapp.tar /myapp/
This example will extract the given TAR file from the Docker host into the /myapp/ directory inside
the Docker image.
Here is another example:
ADD http://jenkov.com/myapp.jar /myapp/
ENV
The Dockerfile ENV command can set an environment variable inside the Docker image. This environment
variable is available for the application that is started inside the Docker image with the CMD command.
Here is an example:
ENV MY_VAR 123
This example sets the environment variable MY_VAR to the value 123 .
RUN
The Dockerfile RUN command can execute command line executables within the Docker image. The
RUN command is executed during build time of the Docker image, so RUN commands
are only executed once. The RUN command can be used to install applications within the
Docker image, or extract files, or other command line activities which are necessary to run once to
prepare the Docker image for execution.
RUN apt-get install some-needed-app
ARG
The Dockerfile ARG instruction lets you define an argument which can be passed to Docker when you
build the Docker image from the Dockerfile. Here is an example:
ARG tcpPort
When you run the Docker command to build the Dockerfile containing the above ARG instruction, you
can pass an argument to the tcpPort argument like this:
docker build --build-arg tcpPort=8080 .
Notice the --build-arg followed by the tcpPort=8080 . This part sets the tcpPort
argument value to 8080.
You can define multiple build arguments using multiple ARG instructions. Here is an example:
ARG tcpPort ARG useTls
When building the Docker image you must provide values for all of the build arguments. You do so by repeating
the --build-arg sections for each argument you want to set. Here is an example:
docker build --build-arg tcpPort=8080 --build-arg useTls=true .
You can set a default value for an ARG so that it becomes optional to provide a value for it
when building the Docker image. If the argument is not given a value, it will be given its default value.
Here is an example:
ARG tcpPort=8080 ARG useTls=true
If neither the tcpPort nor the useTls argument is set when building the Docker image
for the Dockerfile containing the above ARG instructions, their argument values will be set to
8080 and true .
Arguments declared by ARG are typically referenced elsewhere in your Dockerfile.
You reference an ARG argument like this:
ARG tcpPort=8080
ARG useTls=true
CMD start-my-server.sh -port ${tcpPort} -tls ${useTls}
Notice the two references ${tcpPort} and ${useTls}. These refer to the declared
ARG arguments named tcpPort and useTls .
docker build --build-arg tcpPort=8080
WORKDIR
The WORKDIR instruction specifies what the working directory should be inside the Docker image.
The working directory will be in effect for all commands following the WORKDIR instruction.
Here is an example:
WORKDIR /java/jdk/bin
EXPOSE
The Dockerfile EXPOSE instruction opens up network ports in the Docker container to the outside
world. For instance, if your Docker container runs a web server, that web server will probably need port 80
open for any client to be able to connect to it. Here is an example of opening a network port using the
EXPOSE command:
EXPOSE 8080
You can also set which protocol is allowed to communicate on the opened port. For instance, UDP or TCP. Here is an example of setting the allowed protocol also:
EXPOSE 8080/tcp 9999/udp
If no protocol is set (after the / ) , then the protocol is assumed to be TCP.
VOLUME
The Dockerfile VOLUME instruction creates a directory inside the Docker image which you can
later mount a volume (directory) to from the Docker host. In other words, you can create a directory inside
the docker image, e.g. called /data which can later be mounted to a directory, e.g. called
/container-data/container1 in the Docker host. The mounting is done when the container is started up.
Here is an example of defining a volume (mountable directory) in a Dockerfile using the VOLUME instruction:
VOLUME /data
ENTRYPOINT
The Dockerfile ENTRYPOINT instruction provides an entrypoint for Docker containers started
from this Docker image. An entrypoint is an application or command that is executed when the Docker container
is started up. In that way, ENTRYPOINT works similarly to CMD, with the difference being
that using ENTRYPOINT the Docker container is shut down when the application executed by
ENTRYPOINT finishes. Thus, ENTRYPOINT kind of makes your Docker image an executable
command itself, which can be started up and which shut down automatically when finished. Here is a
Dockerfile ENTRYPOINT example:
ENTRYPOINT java -cp /apps/myapp/myapp.jar com.jenkov.myapp.Main
This example will execute the Java application main class com.jenkov.myapp.Main when the Docker
container is started up, and when the application shuts down, so does the Docker container.
HEALTHCHECK
The Dockerfile HEALTHCHECK instruction can execute a health check command line command
at regular intervals, to monitor the health of the application running inside the Docker container. If the command
line command returns a value of 0 when exiting, Docker considers the application and container to be healthy.
If the command line command returns a value of 1, Docker consider the application and container to be unhealthy.
Here is a Dockerfile HEALTHCHECK example:
HEALTHCHECK java -cp /apps/myapp/healthcheck.jar com.jenkov.myapp.HealthCheck https://localhost/healthcheck
This example configures the Java application com.jenkov.myapp.HealthCheck as the healthcheck command.
You can use whatever healthcheck command that makes sense to you here.
Health Check Interval
By default Docker executes the HEALTHCHECK command every 30 seconds. However, you can set a custom
health check interval if you prefer a different interval length than the default 30 seconds. You specify the
health check interval using the --interval argument to the HEALTHCHECK instruction.
Here is an example that sets the HEALTHCHECK interval to 60 seconds instead:
HEALTHCHECK --interval=60s java -cp /apps/myapp/healthcheck.jar com.jenkov.myapp.HealthCheck https://localhost/healthcheck
Health Check Start Period
By default Docker will start checking the health of the Docker container immediately. However, sometimes an application
might take some time to start up, so it may not make sense to health check it until after a certain time period
has elapsed. This gives the application the chance to startup before Docker starts health checking it.
You can set the health check start period using the --start-period argument to the
HEALTHCHECK instruction. Here is an example setting the health check start period to 5 minutes, giving
the container and application 300 seconds (5 minutes) to start up before Docker starts checking its health:
HEALTHCHECK --start-period=300s java -cp /apps/myapp/healthcheck.jar com.jenkov.myapp.HealthCheck https://localhost/healthcheck
Health Check Timeout
It is possible for a health check to time out. If the HEALTCHECK command takes more than a given
time limit to finish, Docker will consider the health check timed out. You can set the timeout limit using the
--timeout argument to the HEALTHCHECK command. Here is an example of setting the
health check timeout time limit to 5 seconds:
HEALTHCHECK --timeout=5s java -cp /apps/myapp/healthcheck.jar com.jenkov.myapp.HealthCheck https://localhost/healthcheck
Note: If the health check times out, Docker considers the container to be unhealthy too.
Health Check Retries
If the HEALTHCHECK fails, either because the HEALTCHECK command returns 1, or if
it times out, Docker will retry the HEALTHCHECK command 3 times to see if the Docker container
returns to a healthy state, before considering the Docker container unhealthy. You can override the default
3 retries using the --retries argument to the HEALTHCHECK instruction. Here is an
example of setting the number of health check retries to 5:
HEALTHCHECK --retries=5 java -cp /apps/myapp/healthcheck.jar com.jenkov.myapp.HealthCheck https://localhost/healthcheck
| Tweet | |
Jakob Jenkov | |











