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
FROMdirective - The
LABELdirective - The
RUNdirective - The
CMDdirective - The
ENTRYPOINTdirective
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 maintainer=sathsara@mydomain.com LABEL version=1.0 LABEL environment=dev
Or these labels can be included on a single line separated by spaces:
LABEL maintainer=sathsara@mydomain.com 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": "sathsara@mydomain.com",
"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-imageusing themkdircommand. This directory will be the context for your Docker image.Contextis the directory that contains all the files needed to successfully build an image:$ mkdir custom-docker-image
- Navigate to the newly created
custom-docker-imagedirectory using thecdcommand 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-imagedirectory, create a file namedDockerfileusing thetouchcommand:$ touch Dockerfile
- Now, open the
Dockerfileusing 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 maintainer=sathsara@mydomain.com 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
LABELdirective to provide the email address of the author of theDockerfile. The next line executes theapt-get updatecommand to update the package list of Debian to the latest available version. Finally, you will use theENTRYPOINTandCMDdirectives to define the default executable and parameters of the container.We have provided
echoas the default executable andYou are readingas the default parameter that cannot be overridden with command-line parameters. Also, we have providedThe Docker Workshopas an additional parameter that can be overridden with command-line parameters with adocker container runcommand.
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.