What is a dockerfile? When working with Docker you can download (pull) a pre-built Docker image from a registry such as Docker Hub, or you can build your own images by building from Dockerfiles. This article will look at what Dockerfiles are and how to write them so that you can build your own Docker images.
Creating a Docker image from a docker file is a two step process. First of all we need to create the dockerfile itself, then we need to run the docker build command with the dockerfile to create the docker image. We’ll start by going through what a dockerfile is and how it is created.
What is a Dockerfile?
A dockerfile is a list of instructions in a test file that are used to build the image. These instructions include commands such as setting environment variables, copying files into the image and running commands. A full list of dockerfile instructions can be seen below.
Dockerfile Instructions
FROM
: Initializes a new build stage and sets the Base ImageRUN
: Will execute any commands in a new layerCMD
: Provides a default for an executing container. There can only be one CMD instruction in aLABEL
: Adds metadata to an imageEXPOSE
: Informs Docker that the container listens on the specified network ports at runtimeENV
: Sets the environment variable<key>
to the value<value>
ADD
: Copies new files, directories or remote file URLs from<src>
and adds them to the filesystem of the image at the path<dest>
.COPY
: Copies new files or directories from<src>
and adds them to the filesystem of the container at the path<dest>
.ENTRYPOINT
: Allows for configuring a container that will run as an executableVOLUME
: Creates a mount point with the specified name and marks it as holding externally mounted volumes from native host or other containersUSER
: Sets the user name (or UID) and optionally the user group (or GID) to use when running the image and for anyRUN
,CMD
, andENTRYPOINT
instructions that follow it in the DockerfileWORKDIR
: Sets the working directory for anyRUN
,CMD
,ENTRYPOINT
,COPY
, andADD
instructions that follow it in the DockerfileARG
: Defines a variable that users can pass at build-time to the builder with thedocker build
command, using the--build-arg <varname>=<value>
flagONBUILD
: Adds a trigger instruction to the image that will be executed at a later time, when the image is used as the base for another buildHEALTHCHECK
: Tells Docker how to test a container to check that it is still workingSHELL
: Allows the default shell used for the shell form of commands to be overridden
A Sample Dockerfile
So, how are these instructions used? Let’s have a look at a simple dockerfile example. This is a DockerFile that will create a new image, using Centos as its base image, then installing Python, Selenium and Google Chrome. I’ve included comments in the file to explain what each line does.
# Uses Centos as the base image for the new image FROM centos # Creates a new working directory called Scripts then sets this as the working directory RUN mkdir /scripts WORKDIR /scripts # Copies chomedriver, googlechrome.rpm and w3test.py into the image's current working directory COPY chromedriver . COPY googlechrome.rpm . COPY w3test.py . # Runs some commands to install Python 3 and Selenium, and Google Chrome from the RPM file copied to the image RUN yum install epel-release -y RUN yum install python3 -y RUN pip3 install selenium RUN yum localinstall googlechrome.rpm -y # Sets the entrypoint - this command will run every time a container is created from this image. In this case, a python script will run, and return its output. ENTRYPOINT python3 w3test.py
The above is a complete Dockerfile – running the docker build command against it will create a working image. But before we look at how to build the image, I wanted to give some further examples of dockerfile instructions. In the above example, I’ve used the FROM, WORKDIR, COPY, RUN and ENTRYPOINT instructions. In the next sections, I’ll go into a bit more detail on each dockerfile instruction with examples of how they are used.
The FROM Instruction
This is always the first instruction in a dockerfile. The FROM instruction starts a new build phase, and sets the base image to be used by the instructions that follow it. In my dockerfile example above, this is:
FROM centos
This sets ‘centos’ as the base image. Another example would be:
FROM ubuntu:latest
This time I’ve also used the ‘latest’ tag to ensure I am using the latest available Ubuntu image. Note that you can have multiple FROM instructions in the same dockerfile, which allows you to split the build into stages or to create multiple images from the same dockerfile.
The RUN Instruction
The RUN instruction is used to run commands in a new layer above the base image. In the example above, I have used the RUN instruction to create a new directory, and then install some software into the image.
RUN yum install python3 -y
The CMD Instruction
The main purpose of the CMD instruction is to provide a default command for a container created from the image. For example, we could have:
CMD whoami
This would run the echo command when a container is created from the image.
Running a container from the image (mytestimage) would look like this:
$ docker run mytestimage root
The container has ran the ‘whoami’ shell command when the container was executed. Note that the CMD instruction is just a default – it can be overridden when creating a container. For example, if we added the ‘hostname’ command to the docker run command we would get:
$ docker run mytestimage hostname 9c1234c0a5d0
This time, instead of getting the output from the ‘whoami’ command, we have the output from the ‘hostname’ command as the default CMD has been overridden.
The ENTRYPOINT Instruction
The ENTRYPOINT instruction lets you configure the container created from the image run as an executable – it’s used when you want the command specified to run every time a container is created from the image. In my dockerfile above I have used the ENTRYPOINT instruction to ensure my python script runs everytime a container is created:
ENTRYPOINT python3 w3test.py
Whilst it’s possible to override the entrypoint, it’s a little more difficult that overriding CMD instruction – you have to specify the entrypoint as follows:
$ docker run --entrypoint hostname mytestimage
CMD can be used when you want to offer users flexibility in what commands they want to run against the container easily, whereas ENTRYPOINT should be used when you want to container to behave as though it was an executable, as I have in my dockerfile. I’ve packaged the components needed to run the script (python, pip, selenium) and the script itself (w3test.py) into the image, and set the entrypoint as the script file.
CMD and ENTRYPOINT can be a little confusing as they appear to be similar. For further reading on these instructions be sure to check out the dockerfile reference documentation, and also this useful page.
The LABEL Instruction
This instruction is used for adding metadata to the image. A simple example would be to add a version number, which could be done like this:
LABEL version="2.0"
Labels added to an image can be viewed when running the docker image inspect command.
The EXPOSE Instruction
The EXPOSE instruction tells docker which network ports the container will be listening on when it is run. An example of it’s use would be:
EXPOSE 443/tcp
Note that the instruction doesn’t actually publish the port. Instead it acts as documentation to show which ports should be published when a container is created from the image. To actually publish the ports the -P flag should be used with the docker run command to map the exposed ports, or specific port mappings can be specified using the -p flag:
docker run -P mytestimage
docker run -p 443:443/tcp mytestimage
The ENV Instruction
The ENV instruction is used to set an environment variable in the image. This variable may then be used by an application running in the image. The ENV instruction is formatted as a key and a value. For example:
ENV version 1.0
In the example above, ‘version’ is the key, and ‘1.0’ is the value. One typical example of how the ENV instruction is used is to have it update the PATH environment variable of the container, to include the path to an application installed in the container. For example:
ENV PATH /usr/local/nginx/bin:$PATH
The ADD and COPY Instructions
The ADD and COPY instructions are fairly similar. They are both ways of getting files from you host system to be included in the docker container image you are building. I’ve used COPY in my example file above to copy a script into the container image:
COPY w3test.py .
The ADD instruction can also be used to do this. Though ADD also offers some additional functionality. For example, you can use it to auto extract a tar file into the image:
ADD rootfs.tar.xz /
.
The VOLUME Instruction
VOLUME is used to create a mount point in a container that connects to the docker host. It can be used to share data between the container and the host, as a means of retaining data, logs, config files and so on.
VOLUME /vol1
When you run a container from the image, a volume should be created, which will be mounted at /vol1 in the container. You can list the docker volumes with:
$ docker volume list
Find out more about how Docker volumes work and what they are used for here.
The USER Instruction
The one is straight forward. The USER instruction sets the username (or UID) of the user to use when running the image, and for any RUN, CMD or ENTRYPOINT commands that follow the USER command in the dockerfile. If the service in the container can run with non-root privileges then you could use USER to switch to using a non-privileged user.
USER myusername
The WORKDIR Instruction
This instruction is used to change the working directory within the image, for example:
WORKDIR /scripts
The ARG Instruction
The ARG
instruction lets you specify a variable that can be passed at build-time with the docker build
command using the --build-arg <varname>=<value>
flag. You can set default values, which can be overridden when the build is executed. For example:
FROM nginx ARG buildversion=1
The HEALTHCHECK Instruction
The Healthcheck instruction configures how you tell Docker how to test that a container is still working. A typical example is checking a webserver is still serving pages:
HEALTHCHECK --interval=5m --timeout=3s \ CMD curl -f http://localhost/ || exit 1
The SHELL Instruction
The shell instruction can be used to override the default shell used in the container. On Linux this is [“/bin/sh”, “-c”]. This instruction can be particularly useful on Windows Docker hosts to change the shell to PowerShell rather than CMD.
Building a Docker Image from a DockerFile
The docker build command is used to create a docker image from a dockerfile. At it’s simplest, in the directory containing the docker file, we can run:
docker build -t myimage:latest .
We have passed the docker build command two parameters :
- -t is the Docker image tag. You can give a name to your image and a tag – I have used myimage:latest
- The second parameter is ‘.’ This is simply the location for the dockerfile. I am running it from my current directory.
As the command is executed you will see the build steps being performed:
docker build -t myimage:latest .
Sending build context to Docker daemon 2.048kB
Step 1/3 : FROM centos
latest: Pulling from library/centos
8a29a15cefae: Pull complete
Digest: sha256:fe8d824220415eed5477b63addf40fb06c3b049
Once done you should see a ‘complete!’ message:
Complete!
Removing intermediate container 5a95f416f75e
---> cba6914f8545
Successfully built cba6914f8545
Successfully tagged myimage:latest
We can check the image is now available for use by running the docker image list command:
$ docker image list REPOSITORY TAG IMAGE ID CREATED SIZE myimage latest cba6914f8545 2 minutes ago 304MB centos latest 470671670cac 4 months ago 237MB
We now have a useable image! The docker image inspect command can be used to get more information about the image we have created, whilst the docker image history command will show the layers in the image, and the commands that were ran:
$ docker image history myimage:latest
IMAGE CREATED CREATED BY SIZE COMMENT
cba6914f8545 5 minutes ago /bin/sh -c yum install python3 -y 34.3MB
f17493390298 5 minutes ago /bin/sh -c yum install epel-release -y 33MB
470671670cac 4 months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
4 months ago /bin/sh -c #(nop) LABEL org.label-schema.sc… 0B
4 months ago /bin/sh -c #(nop) ADD file:aa54047c80ba30064… 237MB
Useful Links
Learning Docker?
If you are starting out, then I highly recommend this book. Thirsty for more?
Then it’s time to take your Docker skills to the next level with this book (It’s my favorite). Also, check out my page on Docker Certification.