Common Directives in Dockerfiles
As discussed in the previous section, a directive is a command that is used to create a Docker image. In this section, we will be discussing the following five Dockerfile
directives:
- The
FROM
directive - The
LABEL
directive - The
RUN
directive - The
CMD
directive - The
ENTRYPOINT
directive
The FROM Directive
A Dockerfile
usually starts with the FROM
directive. This is used to specify the parent image of our custom Docker image. The parent image is the starting point of our custom Docker image. All the customization that we do will be applied on top of the parent image. The parent image can be an image from Docker Hub, such as Ubuntu, CentOS, Nginx, and MySQL. The FROM
directive takes a valid image name and a tag as arguments. If the tag is not specified, the latest
tag will be used.
A FROM directive has the following format:
FROM <image>:<tag>
In the following FROM
directive, we are using the ubuntu
parent image with the 20.04
tag:
FROM ubuntu:20.04
Additionally, we can use the base image if we need to build a Docker image from scratch. The base image, known as the scratch image, is an empty image mostly used to build other parent images.
In the following FROM
directive, we are using the scratch
image to build our custom Docker image from scratch:
FROM scratch
Now, let's understand what a LABEL
directive is in the next section.
The LABEL Directive
A LABEL
is a key-value pair that can be used to add metadata to a Docker image. These labels can be used to organize the Docker images properly. An example would be to add the name of the author of the Dockerfile
or the version of the Dockerfile
.
A LABEL
directive has the following format:
LABEL <key>=<value>
A Dockerfile
can have multiple labels, adhering to the preceding key-value format:
LABEL [email protected] LABEL version=1.0 LABEL environment=dev
Or these labels can be included on a single line separated by spaces:
LABEL [email protected] version=1.0 environment=dev
Labels on an existing Docker image can be viewed with the docker image inspect
command.
The output should be like the following on running the docker image inspect <image>:<tag>
command:
... ... "Labels": { "environment": "dev", "maintainer": "[email protected]", "version": "1.0" } ... ...
As shown here, the docker image inspect command will output the key-value pairs configured in the Dockerfile
using the LABEL
directive.
In the next section, we will learn how to execute commands during the image build time using the RUN
directive.
The RUN Directive
The RUN
directive is used to execute commands during the image build time. This will create a new layer on top of the existing layer, execute the specified command, and commit the results to the newly created layer. The RUN
directive can be used to install the required packages, update the packages, create users and groups, and so on.
The RUN
directive takes the following format:
RUN <command>
<command>
specifies the shell command you want to execute as part of the image build process. A Dockerfile
can have multiple RUN
directives adhering to the preceding format.
In the following example, we are running two commands on top of the parent image. The apt-get update
is used to update the package repositories, and apt-get install nginx -y
is used to install the Nginx package:
RUN apt-get update RUN apt-get install nginx -y
Alternatively, you can add multiple shell commands to a single RUN
directive by separating them with the &&
symbol. In the following example, we have used the same two commands, but this time in a single RUN
directive, separated by an &&
symbol:
RUN apt-get update && apt-get install nginx -y
Now, let's move on to the next section where we will learn about the CMD
directive.
The CMD Directive
A Docker container is normally expected to run one process. A CMD
directive is used to provide this default initialization command that will be executed when a container is created from the Docker image. A Dockerfile
can execute only one CMD
directive. If there is more than one CMD
directive in the Dockerfile
, Docker will execute only the last one.
The CMD
directive has the following format:
CMD ["executable","param1","param2","param3", ...]
For example, use the following command to echo "Hello World
" as the output of a Docker container:
CMD ["echo","Hello World"]
The preceding CMD
directive will produce the following output when we run the Docker container with the docker container run <image>
command (replace <image>
with the name of the Docker image):
$ docker container run <image> Hello World
However, if we send any command-line arguments with docker container run <image>
, these arguments will take precedence over the CMD
command that we defined. For example, if we execute the following command (replace <image>
with the name of the Docker image), the default "Hello World
" output defined with the CMD
directive will be ignored. Instead, the container will output "Hello Docker !!!
":
$ docker container run <image> echo "Hello Docker !!!"
As we discussed, both the RUN
and CMD
directives can be used to execute a shell command. The main difference between these two directives is that the command provided with the RUN
directive will be executed during the image build process, while the command provided with the CMD
directive will be executed once a container is launched from the built image.
Another notable difference between the RUN
and CMD
directives is that there can be multiple RUN
directives in a Dockerfile
, but there can be only one CMD
directive (if there are multiple CMD
directives, all others except the last one will be ignored).
As an example, we can use the RUN
directive to install a software package during the Docker image build process and the CMD
directive to start the software package once a container is launched from the built image.
In the next section, we will learn about the ENTRYPOINT
directive, which provides the same functionality as the CMD
directive, except for overriding.
The ENTRYPOINT Directive
Similar to the CMD
directive, the ENTRYPOINT
directive is also used to provide this default initialization command that will be executed when a container is created from the Docker image. The difference between the CMD
directive and the ENTRYPOINT
directive is that, unlike the CMD
directive, we cannot override the ENTRYPOINT
command using the command-line parameters sent with the docker container run
command.
Note
The --entrypoint
flag can be sent with the docker container run
command to override the default ENTRYPOINT
of the image.
The ENTRYPOINT
directive has the following format:
ENTRYPOINT ["executable","param1","param2","param3", ...]
Similar to the CMD
directive, the ENTRYPOINT
directive also allows us to provide the default executable and the parameters. We can use the CMD
directive with the ENTRYPOINT
directive to provide additional arguments to the executable.
In the following example, we have used "echo"
as the default command and "Hello"
as the default parameter using the ENTRYPOINT
directive. We have also provided "World"
as the additional parameter using the CMD
directive:
ENTRYPOINT ["echo","Hello"] CMD ["World"]
The output of the echo
command will differ based on how we execute the docker container run
command.
If we launch the Docker image without any command-line parameters, it will output the message as Hello World
:
$ docker container run <image> Hello World
But if we launch the Docker image with additional command-line parameters (for example, Docker
), the output message will be Hello Docker
:
$ docker container run <image> "Docker" Hello Docker
Before discussing the Dockerfile
directives any further, let's start by creating our first Dockerfile
in the next exercise.
Exercise 2.01: Creating Our First Dockerfile
In this exercise, you will create a Docker image that can print the arguments you pass to the Docker image, preceded by the text You are reading
. For example, if you pass hello world
, it will output You are reading hello world
as the output. If no argument is provided, The Docker Workshop
will be used as the standard value:
- Create a new directory named
custom-docker-image
using themkdir
command. This directory will be the context for your Docker image.Context
is the directory that contains all the files needed to successfully build an image:$ mkdir custom-docker-image
- Navigate to the newly created
custom-docker-image
directory using thecd
command as we will be creating all the files required during the build process (including theDockerfile
) within this directory:$ cd custom-docker-image
- Within the
custom-docker-image
directory, create a file namedDockerfile
using thetouch
command:$ touch Dockerfile
- Now, open the
Dockerfile
using your favorite text editor:$ vim Dockerfile
- Add the following content to the
Dockerfile
, save it, and exit from theDockerfile
:# This is my first Docker image FROM ubuntu LABEL [email protected] RUN apt-get update CMD ["The Docker Workshop"] ENTRYPOINT ["echo", "You are reading"]
The Docker image will be based on the Ubuntu parent image. You then use the
LABEL
directive to provide the email address of the author of theDockerfile
. The next line executes theapt-get update
command to update the package list of Debian to the latest available version. Finally, you will use theENTRYPOINT
andCMD
directives to define the default executable and parameters of the container.We have provided
echo
as the default executable andYou are reading
as the default parameter that cannot be overridden with command-line parameters. Also, we have providedThe Docker Workshop
as an additional parameter that can be overridden with command-line parameters with adocker container run
command.
In this exercise, we created our first Dockerfile
using the common directives that we learned in the previous sections. The next step of the process is to build the Docker image from the Dockerfile
. You can only run a Docker container after building the Docker image from the Dockerfile
. In the next section, we are going to look at how to build a Docker image from the Dockerfile
.