Book Image

The Docker Workshop

By : Vincent Sesto, Onur Yılmaz, Sathsara Sarathchandra, Aric Renzo, Engy Fouda
5 (3)
Book Image

The Docker Workshop

5 (3)
By: Vincent Sesto, Onur Yılmaz, Sathsara Sarathchandra, Aric Renzo, Engy Fouda

Overview of this book

No doubt Docker Containers are the future of highly-scalable software systems and have cost and runtime efficient supporting infrastructure. But learning it might look complex as it comes with many technicalities. This is where The Docker Workshop will help you. Through this workshop, you’ll quickly learn how to work with containers and Docker with the help of practical activities.? The workshop starts with Docker containers, enabling you to understand how it works. You’ll run third party Docker images and also create your own images using Dockerfiles and multi-stage Dockerfiles. Next, you’ll create environments for Docker images, and expedite your deployment and testing process with Continuous Integration. Moving ahead, you’ll tap into interesting topics and learn how to implement production-ready environments using Docker Swarm. You’ll also apply best practices to secure Docker images and to ensure that production environments are running at maximum capacity. Towards the end, you’ll gather skills to successfully move Docker from development to testing, and then into production. While doing so, you’ll learn how to troubleshoot issues, clear up resource bottlenecks and optimize the performance of services. By the end of this workshop, you’ll be able to utilize Docker containers in real-world use cases.
Table of Contents (17 chapters)
Preface

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:

  1. The FROM directive
  2. The LABEL directive
  3. The RUN directive
  4. The CMD directive
  5. 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:

  1. Create a new directory named custom-docker-image using the mkdir 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
  2. Navigate to the newly created custom-docker-image directory using the cd command as we will be creating all the files required during the build process (including the Dockerfile) within this directory:
    $ cd custom-docker-image
  3. Within the custom-docker-image directory, create a file named Dockerfile using the touch command:
    $ touch Dockerfile
  4. Now, open the Dockerfile using your favorite text editor:
    $ vim Dockerfile
  5. Add the following content to the Dockerfile, save it, and exit from the Dockerfile:
    # 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 the Dockerfile. The next line executes the apt-get update command to update the package list of Debian to the latest available version. Finally, you will use the ENTRYPOINT and CMD directives to define the default executable and parameters of the container.

    We have provided echo as the default executable and You are reading as the default parameter that cannot be overridden with command-line parameters. Also, we have provided The Docker Workshop as an additional parameter that can be overridden with command-line parameters with a docker 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.