In the section Common Directives in Dockerfile, we discussed the common directives available for a Dockerfile
. In that section, we discussed FROM
, LABEL
, RUN
, CMD
, and ENTRYPOINT
directives and how to use them to create a simple Dockerfile
.
In this section, we will be discussing more advanced Dockerfile
directives. These directives can be used to create more advanced Docker images. For example, we can use the VOLUME
directive to bind the filesystem of the host machine to a Docker container. This will allow us to persist the data generated and used by the Docker container. Another example is the HEALTHCHECK
directive, which allows us to define health checks to evaluate the health status of Docker containers. We will look into the following directives in this section:
- The
ENV
directive
- The
ARG
directive
- The
WORKDIR
directive
- The
COPY
directive
- The
ADD
directive
- The
USER
directive
- The
VOLUME
directive
- The
EXPOSE
directive
- The
HEALTHCHECK
directive
- The
ONBUILD
directive
The ENV Directive
The ENV directive in Dockerfile
is used to set environment variables. Environment variables are used by applications and processes to get information about the environment in which a process runs. One example would be the PATH
environment variable, which lists the directories to search for executable files.
Environment variables are defined as key-value pairs as per the following format:
ENV <key> <value>
The PATH environment variable is set with the following value:
$PATH:/usr/local/myapp/bin/
Hence, it can be set using the ENV
directive as follows:
ENV PATH $PATH:/usr/local/myapp/bin/
We can set multiple environment variables in the same line separated by spaces. However, in this form, the key
and value
should be separated by the equal to (=
) symbol:
ENV <key>=<value> <key>=<value> ...
In the following example, there are two environment variables configured. The PATH
environment variable is configured with the value of $PATH:/usr/local/myapp/bin/
, and the VERSION
environment variable is configured with the value of 1.0.0
:
ENV PATH=$PATH:/usr/local/myapp/bin/ VERSION=1.0.0
Once an environment variable is set with the ENV
directive in the Dockerfile
, this variable is available in all subsequent Docker image layers. This variable is even available in the Docker containers launched from this Docker image.
In the next section, we will look into the ARG
directive.
The ARG Directive
The ARG
directive is used to define variables that the user can pass at build time. ARG
is the only directive that can precede the FROM
directive in the Dockerfile
.
Users can pass values using --build-arg <varname>=<value>
, as shown here, while building the Docker image:
$ docker image build -t <image>:<tag> --build-arg <varname>=<value> .
The ARG
directive has the following format:
ARG <varname>
There can be multiple ARG
directives in a Dockerfile
, as follows:
ARG USER
ARG VERSION
The ARG
directive can also have an optional default value defined. This default value will be used if no value is passed at build time:
ARG USER=TestUser
ARG VERSION=1.0.0
Unlike the ENV
variables, ARG
variables are not accessible from the running container. They are only available during the build process.
In the next exercise, we will use the knowledge gained so far to use ENV
and ARG
directives in a Dockerfile
.
Exercise 2.03: Using ENV and ARG Directives in a Dockerfile
Your manager has asked you to create a Dockerfile
that will use ubuntu as the parent image, but you should be able to change the ubuntu version at build time. You will also need to specify the publisher's name and application directory as the environment variables of the Docker image. You will use the ENV
and ARG
directives in the Dockerfile
to perform this exercise:
- Create a new directory named
env-arg-exercise
using the mkdir
command: mkdir env-arg-exercise
- Navigate to the newly created
env-arg-exercise
directory using the cd
command:cd env-arg-exercise
- Within the
env-arg-exercise
directory, create a file named Dockerfile
:touch Dockerfile
- Now, open the
Dockerfile
using your favorite text editor:vim Dockerfile
- Add the following content to the
Dockerfile
. Then, save and exit from the Dockerfile
:# ENV and ARG example
ARG TAG=latest
FROM ubuntu:$TAG
LABEL maintainer=sathsara@mydomain.com
ENV PUBLISHER=packt APP_DIR=/usr/local/app/bin
CMD ["env"]
This Dockerfile
first defined an argument named TAG
with the default value of the latest. The next line is the FROM
directive, which will use the ubuntu parent image with the TAG
variable value sent with the build
command (or the default value if no value is sent with the build command). Then, the LABEL
directive sets the value for the maintainer. Next is the ENV
directive, which defines the environment variable of PUBLISHER
with the value packt
, and APP_DIR
with the value of /usr/local/app/bin
. Finally, use the CMD
directive to execute the env
command, which will print all the environment variables.
- Now, build the Docker image:
$ docker image build -t env-arg --build-arg TAG=19.04 .
Note the env-arg --build-arg TAG=19.04
flag used to send the TAG
argument to the build process. The output should be as follows:
Figure 2.5: Building the env-arg Docker image
Note that the 19.04
tag of the ubuntu image was used as the parent image. This is because you sent the --build-arg flag
with the value of TAG=19.04
during the build process.
- Now, execute the
docker container run
command to start a new container from the Docker image that you built in the last step:$ docker container run env-arg
As we can see from the output, the PUBLISHER
environment variable is available with the value of packt
, and the APP_DIR
environment variable is available with the value of /usr/local/app/bin
:
Figure 2.6: Running the env-arg Docker container
In this exercise, we defined environment variables for a Docker image using the ENV
directive. We also experienced how to use ARG
directives to pass values during the Docker image build time. In the next section, we will be covering the WORKDIR
directive, which can be used to define the current working directory of the Docker container.
The WORKDIR Directive
The WORKDIR
directive is used to specify the current working directory of the Docker container. Any subsequent ADD
, CMD
, COPY
, ENTRYPOINT
, and RUN
directives will be executed in this directory. The WORKDIR
directive has the following format:
WORKDIR /path/to/workdir
If the specified directory does not exist, Docker will create this directory and make it the current working directory, which means this directive executes both mkdir
and cd
commands implicitly.
There can be multiple WORKDIR
directives in the Dockerfile
. If a relative path is provided in a subsequent WORKDIR
directive, that will be relative to the working directory set by the previous WORKDIR
directive:
WORKDIR /one
WORKDIR two
WORKDIR three
RUN pwd
In the preceding example, we are using the pwd
command at the end of the Dockerfile
to print the current working directory. The output of the pwd
command will be /one/two/three
.
In the next section, we will discuss the COPY
directive that is used to copy files from the local filesystem to the Docker image filesystem.
The COPY Directive
During the Docker image build process, we may need to copy files from our local filesystem to the Docker image filesystem. These files can be source code files (for example, JavaScript files), configuration files (for example, properties files), or artifacts (for example, JAR files). The COPY
directive can be used to copy files and folders from the local filesystem to the Docker image during the build process. This directive takes two arguments. The first one is the source path from the local filesystem, and the second one is the destination path on the image filesystem:
COPY <source> <destination>
In the following example, we are using the COPY
directive to copy the index.html
file from the local filesystem to the /var/www/html/
directory of the Docker image:
COPY index.html /var/www/html/index.html
Wildcards can also be specified to copy all files that match the given pattern. The following example will copy all files with the .html
extension from the current directory to the /var/www/html/
directory of the Docker image:
COPY *.html /var/www/html/
In addition to copying files, the --chown
flag can be used with the COPY
directive to specify the user and group ownership of the files:
COPY --chown=myuser:mygroup *.html /var/www/html/
In the preceding example, in addition to copying all the HTML files from the current directory to the /var/www/html/
directory, the --chown
flag is used to set file ownership, with the user as myuser
and group as mygroup
:
Note
The --chown
flag is only supported from Docker version 17.09 and above. For Docker versions below 17.09, you need to run the chown
command after the COPY
command to change file ownership.
In the next section, we will look at the ADD
directive.
The ADD Directive
The ADD
directive is also similar to the COPY
directive, and has the following format:
ADD <source> <destination>
However, in addition to the functionality provided by the COPY
directive, the ADD
directive also allows us to use a URL as the <source>
parameter:
ADD http://sample.com/test.txt /tmp/test.txt
In the preceding example, the ADD
directive will download the test.txt
file from http://sample.com
and copy the file to the /tmp
directory of the Docker image filesystem.
Another feature of the ADD
directive is automatically extracting the compressed files. If we add a compressed file (gzip, bzip2, tar, and so on) to the <source>
parameter, the ADD
directive will extract the archive and copy the content to the image filesystem.
Imagine we have a compressed file named html.tar.gz
that contains index.html
and contact.html
files. The following command will extract the html.tar.gz
file and copy the index.html
and contact.html
files to the /var/www/html
directory:
ADD html.tar.gz /var/www/html
Since the COPY
and ADD
directives provide almost the same functionality, it is recommended to always use the COPY
directive unless you need the additional functionality (add from a URL or extract a compressed file) provided by the ADD
directive. This is because the ADD
directive provides additional functionality that can behave unpredictably if used incorrectly (for example, copying files when you want to extract, or extracting files when you want to copy).
In the next exercise, we are going to use the WORKDIR
, COPY
, and ADD
directives to copy files into the Docker image.
Exercise 2.04: Using the WORKDIR, COPY, and ADD Directives in the Dockerfile
In this exercise, you will deploy your custom HTML file to the Apache web server. You will use Ubuntu as the base image and install Apache on top of it. Then, you will copy your custom index.html file to the Docker image and download the Docker logo (from the https://www.docker.com website) to be used with the custom index.html file:
- Create a new directory named
workdir-copy-add-exercise
using the mkdir
command:mkdir workdir-copy-add-exercise
- Navigate to the newly created
workdir-copy-add-exercise
directory:cd workdir-copy-add-exercise
- Within the
workdir-copy-add-exercise
directory, create a file named index.html
. This file will be copied to the Docker image during build time:touch index.html
- Now, open
index.html
using your favorite text editor:vim index.html
- Add the following content to the
index.html
file, save it, and exit from index.html
:<html>
<body>
<h1>Welcome to The Docker Workshop</h1>
<img src="logo.png" height="350" width="500"/>
</body>
</html>
This HTML file will output Welcome to The Docker Workshop
as the header of the page and logo.png
(which we will download during the Docker image build process) as an image. You have defined the size of the logo.png
image as a height of 350
and a width of 500
.
- Within the
workdir-copy-add-exercise
directory, create a file named Dockerfile
: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 the Dockerfile
:# WORKDIR, COPY and ADD example
FROM ubuntu:latest
RUN apt-get update && apt-get install apache2 -y
WORKDIR /var/www/html/
COPY index.html .
ADD https://www.docker.com/sites/default/files/d8/2019-07/ Moby-logo.png ./logo.png
CMD ["ls"]
This Dockerfile
first defines the ubuntu image as the parent image. The next line is the RUN
directive, which will execute apt-get update
to update the package list, and apt-get install apache2 -y
to install the Apache HTTP server. Then, you will set /var/www/html/
as the working directory. Next, copy the index.html
file that we created in step 3 to the Docker image. Then, use the ADD
directive to download the Docker logo from https://www.docker.com/sites/default/files/d8/2019-07/Moby-logo.png to the Docker image. The final step is to use the ls
command to print the content of the /var/www/html/
directory.
- Now, build the Docker image with the tag of
workdir-copy-add
:$ docker image build -t workdir-copy-add .
You will observe that the image is successfully built and tagged as latest
since we did not explicitly tag our image:
Figure 2.7: Building the Docker image using WORKDIR, COPY, and ADD directives
- Execute the
docker container run
command to start a new container from the Docker image that you built in the previous step:$ docker container run workdir-copy-add
As we can see from the output, both the index.html
and logo.png
files are available in the /var/www/html/
directory:
index.html
logo.png
In this exercise, we observed how the WORKDIR
, ADD
, and COPY
directives work with Docker. In the next section, we are going to discuss the USER
directive.
The USER Directive
Docker will use the root user as the default user of a Docker container. We can use the USER
directive to change this default behavior and specify a non-root user as the default user of a Docker container. This is a great way to improve security by running the Docker container as a non-privileged user. The username specified with the USER
directive will be used to run all subsequent RUN
, CMD
, and ENTRYPOINT
directives in the Dockerfile
.
The USER
directive takes the following format:
USER <user>
In addition to the username, we can also specify the optional group name to run the Docker container:
USER <user>:<group>
We need to make sure that the <user>
and <group>
values are valid user and group names. Otherwise, the Docker daemon will throw an error while trying to run the container:
docker: Error response from daemon: unable to find user my_user:
no matching entries in passwd file.
Now, let's try our hands at using the USER
directive in the next exercise.
Exercise 2.05: Using USER Directive in the Dockerfile
Your manager has asked you to create a Docker image to run the Apache web server. He has specifically requested that you use a non-root user while running the Docker container due to security reasons. In this exercise, you will use the USER
directive in the Dockerfile
to set the default user. You will be installing the Apache web server and changing the user to www-data
. Finally, you will execute the whoami
command to verify the current user by printing the username:
Note
The www-data
user is the default user for the Apache web server on Ubuntu.
- Create a new directory named
user-exercise
for this exercise:mkdir user-exercise
- Navigate to the newly created
user-exercise
directory:cd user-exercise
- Within the
user-exercise
directory, create a file named Dockerfile
: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 the Dockerfile
:# USER example
FROM ubuntu
RUN apt-get update && apt-get install apache2 -y
USER www-data
CMD ["whoami"]
This Dockerfile
first defines the Ubuntu image as the parent image. The next line is the RUN
directive, which will execute apt-get update
to update the package list, and apt-get install apache2 -y
to install the Apache HTTP server. Next, you use the USER
directive to change the current user to the www-data
user. Finally, you have the CMD
directive, which executes the whoami
command, which will print the username of the current user.
- Build the Docker image:
$ docker image build -t user .
The output should be as follows:
Figure 2.8: Building the user Docker image
- Now, execute the
docker container
run command to start a new container from the Docker image that we built in the previous step:$ docker container run user
As you can see from the following output, www-data
is the current user associated with the Docker container:
www-data
In this exercise, we implemented the USER
directive in the Dockerfile
to set the www-data
user as the default user of the Docker image.
In the next section, we will discuss the VOLUME
directive.
The VOLUME Directive
In Docker, the data (for example, files, executables) generated and used by Docker containers will be stored within the container filesystem. When we delete the container, all the data will be lost. To overcome this issue, Docker came up with the concept of volumes. Volumes are used to persist the data and share the data between containers. We can use the VOLUME
directive within the Dockerfile
to create Docker volumes. Once a VOLUME
is created in the Docker container, a mapping directory will be created in the underlying host machine. All file changes to the volume mount of the Docker container will be copied to the mapped directory of the host machine.
The VOLUME
directive generally takes a JSON array as the parameter:
VOLUME ["/path/to/volume"]
Or, we can specify a plain string with multiple paths:
VOLUME /path/to/volume1 /path/to/volume2
We can use the docker container inspect <container>
command to view the volumes available in a container. The output JSON of the docker container inspect command will print the volume information similar to the following:
"Mounts": [
{
"Type": "volume",
"Name": "77db32d66407a554bd0dbdf3950671b658b6233c509ea
ed9f5c2a589fea268fe",
"Source": "/var/lib/docker/volumes/77db32d66407a554bd0
dbdf3950671b658b6233c509eaed9f5c2a589fea268fe/_data",
"Destination": "/path/to/volume",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
As per the preceding output, there is a unique name given to the volume by Docker. Also, the source and destination paths of the volume are mentioned in the output.
Additionally, we can execute the docker volume inspect <volume>
command to display detailed information pertaining to a volume:
[
{
"CreatedAt": "2019-12-28T12:52:52+05:30",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/77db32d66407a554
bd0dbdf3950671b658b6233c509eaed9f5c2a589fea268fe/_data",
"Name": "77db32d66407a554bd0dbdf3950671b658b6233c509eae
d9f5c2a589fea268fe",
"Options": null,
"Scope": "local"
}
]
This is also similar to the previous output, with the same unique name and the mount path of the volume.
In the next exercise, we will learn how to use the VOLUME
directive in a Dockerfile
.
Exercise 2.06: Using VOLUME Directive in the Dockerfile
In this exercise, you will be setting a Docker container to run the Apache web server. However, you do not want to lose the Apache log files in case of a Docker container failure. As a solution, you have decided to persist in the log files by mounting the Apache log path to the underlying Docker host:
- Create a new directory named
volume-exercise
:mkdir volume-exercise
- Navigate to the newly created
volume-exercise
directory:cd volume-exercise
- Within the
volume-exercise
directory, create a file named Dockerfile
: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 the Dockerfile
:# VOLUME example
FROM ubuntu
RUN apt-get update && apt-get install apache2 -y
VOLUME ["/var/log/apache2"]
This Dockerfile
started by defining the Ubuntu image as the parent image. Next, you will execute the apt-get update
command to update the package list, and the apt-get install apache2 -y
command to install the Apache web server. Finally, use the VOLUME
directive to set up a mount point to the /var/log/apache2
directory.
- Now, build the Docker image:
$ docker image build -t volume .
The output should be as follows:
Figure 2.9: Building the volume Docker image
- Execute the docker container run command to start a new container from the Docker image that you built in the previous step. Note that you are using the
--interactive
and --tty
flags to open an interactive bash session so that you can execute commands from the bash shell of the Docker container. You have also used the --name
flag to define the container name as volume-container
:$ docker container run --interactive --tty --name volume-container volume /bin/bash
Your bash shell will be opened as follows:
root@bc61d46de960: /#
- From the Docker container command line, change directory to the
/var/log/apache2/
directory:# cd /var/log/apache2/
This will produce the following output:
root@bc61d46de960: /var/log/apache2#
- Now, list the available files in the directory:
# ls -l
The output should be as follows:
Figure 2.10: Listing files of the /var/log/apache2 directory
These are the log files created by Apache while running the process. The same files should be available once you check the host mount of this volume.
- Now, exit the container to check the host filesystem:
# exit
- Inspect
volume-container
to view the mount information:$ docker container inspect volume-container
Under the "Mounts
" key, you can see the information relating to the mount:
Figure 2.11: Inspecting the Docker container
- Inspect the volume with the
docker volume inspect <volume_name>
command. <volume_name>
can be identified by the Name
field of the preceding output:$ docker volume inspect 354d188e0761d82e1e7d9f3d5c6ee644782b7150f51cead8f140556e5d334bd5
You should get the output similar to the following:
Figure 2.12: Inspecting the Docker volume
We can see that the container is mounted to the host path of "/var/lib/docker/volumes/354d188e0761d82e1e7d9f3d5c6ee644782b 7150f51cead8f140556e5d334bd5/_data"
, which is defined as the Mountpoint
field in the preceding output.
- List the files available in the host file path. The host file path can be identified with the
"Mountpoint
" field of the preceding output:$ sudo ls -l /var/lib/docker/volumes/354d188e0761d82e1e7d9f3d5c6ee644782b7150f51cead8f14 0556e5d334bd5/_data
In the following output, you can see that the log files in the /var/log/apache2
directory of the container are mounted to the host:
Figure 2.13: Listing files in the mount point directory
In this exercise, we observed how to mount the log path of the Apache web server to the host filesystem using the VOLUME
directive. In the next section, we will learn about the EXPOSE
directive.
The EXPOSE Directive
The EXPOSE
directive is used to inform Docker that the container is listening on the specified ports at runtime. We can use the EXPOSE
directive to expose ports through either TCP or UDP protocols. The EXPOSE
directive has the following format:
EXPOSE <port>
However, the ports exposed with the EXPOSE
directive will only be accessible from within the other Docker containers. To expose these ports outside the Docker container, we can publish the ports with the -p
flag with the docker container run
command:
docker container run -p <host_port>:<container_port> <image>
As an example, imagine that we have two containers. One is a NodeJS web app container that should be accessed from outside via port 80
. The second one is the MySQL container, which should be accessed from the node app container via port 3306
. In this scenario, we have to expose port 80
of the NodeJS app with the EXPOSE
directive and use the -p
flag with the docker container run
command to expose it externally. However, for the MySQL container, we can only use the EXPOSE
directive without the -p
flag when running the container, as 3306
should only be accessible from the node app container.
So, in summary, the following statements define this directive:
- If we specify both the
EXPOSE
directive and -p
flag, exposed ports will be accessible from other containers as well as externally.
- If we specify
EXPOSE
without the -p
flag, exposed ports will only be accessible from other containers, but not externally.
You will learn about the HEALTHCHECK
directive in the next section.
The HEALTHCHECK Directive
Health checks are used in Docker to check whether the containers are running healthily. For example, we can use health checks to make sure the application is running within the Docker container. Unless there is a health check specified, there is no way for Docker to say whether a container is healthy. This is very important if you are running Docker containers in production environments. The HEALTHCHECK
directive has the following format:
HEALTHCHECK [OPTIONS] CMD command
There can be only one HEALTHCHECK
directive in a Dockerfile
. If there is more than one HEALTHCHECK
directive, only the last one will take effect.
As an example, we can use the following directive to ensure that the container can receive traffic on the http://localhost/
endpoint:
HEALTHCHECK CMD curl -f http://localhost/ || exit 1
The exit code at the end of the preceding command is used to specify the health status of the container. 0
and 1
are valid values for this field. 0 is used to denote a healthy container, and 1
is used to denote an unhealthy container.
In addition to the command, we can specify a few other parameters with the HEALTHCHECK
directive, as follows:
--interval
: This specifies the period between each health check (the default is 30s).
--timeout
: If no success response is received within this period, the health check is considered failed (the default is 30s).
--start-period
: The duration to wait before running the first health check. This is used to give a startup time for the container (the default is 0s).
--retries
: The container will be considered unhealthy if the health check failed consecutively for the given number of retries (the default is 3).
In the following example, we have overridden the default values by providing our custom values with the HEALTHCHECK
directive:
HEALTHCHECK --interval=1m --timeout=2s --start-period=2m --retries=3 \ CMD curl -f http://localhost/ || exit 1
We can check the health status of a container with the docker container list
command. This will list the health status under the STATUS
column:
CONTAINER ID IMAGE COMMAND CREATED
STATUS PORTS NAMES
d4e627acf6ec sample "apache2ctl -D FOREG…" About a minute ago
Up About a minute (healthy) 0.0.0.0:80->80/tcp upbeat_banach
As soon as we start the container, the health status will be health: starting. Following the successful execution of the HEALTHCHECK
command, the status will change to healthy
.
In the next exercise, we are going to use the EXPOSE
and HEALTHCHECK
directives to create a Docker container with the Apache web server and define health checks for it.
Exercise 2.07: Using EXPOSE and HEALTHCHECK Directives in the Dockerfile
Your manager has asked you to dockerize the Apache web server to access the Apache home page from the web browser. Additionally, he has asked you to configure health checks to determine the health status of the Apache web server. In this exercise, you will use the EXPOSE
and HEALTHCHECK
directives to achieve this goal:
- Create a new directory named
expose-healthcheck
:mkdir expose-healthcheck
- Navigate to the newly created
expose-healthcheck
directory:cd expose-healthcheck
- Within the
expose-healthcheck
directory, create a file named Dockerfile
: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 the Dockerfile
:# EXPOSE & HEALTHCHECK example
FROM ubuntu
RUN apt-get update && apt-get install apache2 curl -y
HEALTHCHECK CMD curl -f http://localhost/ || exit 1
EXPOSE 80
ENTRYPOINT ["apache2ctl", "-D", "FOREGROUND"]
This Dockerfile
first defines the ubuntu image as the parent image. Next, we execute the apt-get update
command to update the package list, and the apt-get install apache2 curl -y
command to install the Apache web server and curl tool. Curl
is required to execute the HEALTHCHECK
command. Next, we define the HEALTHCHECK
directive with curl to the http://localhost/
endpoint. Then, we exposed port 80
of the Apache web server so that we can access the home page from our web browser. Finally, we start the Apache web server with the ENTRYPOINT
directive.
- Now, build the Docker image:
$ docker image build -t expose-healthcheck.
You should get the following output:
Figure 2.14: Building the expose-healthcheck Docker image
- Execute the docker container run command to start a new container from the Docker image that you built in the previous step. Note that you are using the
-p
flag to redirect port 80
of the host to port 80
of the container. Additionally, you have used the --name
flag to specify the container name as expose-healthcheck-container
, and the -d
flag to run the container in detached mode (this runs the container in the background):$ docker container run -p 80:80 --name expose-healthcheck-container -d expose-healthcheck
- List the running containers with the
docker container list
command:$ docker container list
In the following output, you can see that the STATUS
of the expose-healthcheck-container
is healthy:
Figure 2.15: List of running containers
- Now, you should be able to view the Apache home page. Go to the
http://127.0.0.1
endpoint from your favorite web browser:Figure 2.16: Apache home page
- Now, clean up the container. First, stop the Docker container by using the
docker container stop
command:$ docker container stop expose-healthcheck-container
- Finally, remove the Docker container with the
docker container rm
command:$ docker container rm expose-healthcheck-container
In this exercise, you utilized the EXPOSE
directive to expose an Apache web server as a Docker container and used the HEALTHCHECK
directive to define a health check to verify the healthy status of the Docker container.
In the next section, we will learn about the ONBUILD
directive.
The ONBUILD Directive
The ONBUILD
directive is used in the Dockerfile
to create a reusable Docker image that will be used as the base for another Docker image. As an example, we can create a Docker image that contains all the prerequisites, such as dependencies and configurations, in order to run an application. Then, we can use this 'prerequisite' image as the parent image to run the application.
While creating the prerequisite image, we can use the ONBUILD
directive, which will include the instructions that should only be executed when this image is used as the parent image in another Dockerfile
. ONBUILD
instructions will not be executed while building the Dockerfile
that contains the ONBUILD
directive, but only when building the child image.
The ONBUILD
directive takes the following format:
ONBUILD <instruction>
As an example, consider that we have the following ONBUILD
instruction in the Dockerfile
of our custom base image:
ONBUILD ENTRYPOINT ["echo","Running ONBUILD directive"]
The "Running ONBUILD directive"
value will not be printed if we create a Docker container from our custom base image. However, the "Running ONBUILD directive"
value will be printed if we use our custom base image as the base for our new child Docker image.
We can use the docker image inspect
command for the parent image to list the OnBuild triggers listed for the image:
$ docker image inspect <parent-image>
The command will return output similar to the following:
...
"OnBuild": [
"CMD [\"echo\",\"Running ONBUILD directive\"]"
]
...
In the next exercise, we will be using the ONBUILD
directive to define a Docker image to deploy the HTML files.
Exercise 2.08: Using ONBUILD Directive in the Dockerfile
You have been asked by your manager to create a Docker image that is capable of running any HTML files provided by the software development team. In this exercise, you will build a parent image with the Apache web server and use the ONBUILD
directive to copy the HTML files. The software development team can use this Docker image as the parent image to deploy and test any HTML files created by them:
- Create a new directory named
onbuild-parent
:mkdir onbuild-parent
- Navigate to the newly created
onbuild-parent
directory:cd onbuild-parent
- Within the
onbuild-parent
directory, create a file named Dockerfile
: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 the Dockerfile
:# ONBUILD example
FROM ubuntu
RUN apt-get update && apt-get install apache2 -y
ONBUILD COPY *.html /var/www/html
EXPOSE 80
ENTRYPOINT ["apache2ctl", "-D", "FOREGROUND"]
This Dockerfile
first defines the ubuntu image as the parent image. It then executes the apt-get update
command to update the package list, and the apt-get install apache2 -y
command to install the Apache web server. The ONBUILD
directive is used to provide a trigger to copy all HTML files to the /var/www/html
directory. The EXPOSE
directive is used to expose port 80
of the container and ENTRYPOINT
to start the Apache web server using the apache2ctl
command.
- Now, build the Docker image:
$ docker image build -t onbuild-parent .
The output should be as follows:
Figure 2.17: Building the onbuild-parent Docker image
- Execute the
docker container run
command to start a new container from the Docker image built in the previous step:$ docker container run -p 80:80 --name onbuild-parent-container -d onbuild-parent
In the preceding command, you have started the Docker container in detached mode while exposing port 80
of the container.
- Now, you should be able to view the Apache home page. Go to the
http://127.0.0.1
endpoint from your favorite web browser. Note that the default Apache home page is visible:Figure 2.18: Apache home page
- Now, clean up the container. Stop the Docker container by using the
docker container stop
command:$ docker container stop onbuild-parent-container
- Remove the Docker container with the
docker container rm
command:$ docker container rm onbuild-parent-container
- Now, create another Docker image using
onbuild-parent-container
as the parent image to deploy a custom HTML home page. First, change the directory back to the previous directory:cd ..
- Create a new directory named
onbuild-child
for this exercise:mkdir onbuild-child
- Navigate to the newly created
onbuild-child
directory:cd onbuild-child
- Within the
onbuild-child
directory, create a file named index.html
. This file will be copied to the Docker image by the ONBUILD
command during build time:touch index.html
- Now, open the
index.html
file using your favorite text editor:vim index.html
- Add the following content to the
index.html
file, save it, and exit from the index.html
file:<html>
<body>
<h1>Learning Docker ONBUILD directive</h1>
</body>
</html>
This is a simple HTML file that will output the Learning Docker ONBUILD
directive as the header of the page.
- Within the
onbuild-child
directory, create a file named Dockerfile
: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 the Dockerfile
:# ONBUILD example
FROM onbuild-parent
This Dockerfile
has only one directive. This will use the FROM
directive to utilize the onbuild-parent
Docker image that you created previously as the parent image.
- Now, build the Docker image:
$ docker image build -t onbuild-child .
Figure 2.19: Building the onbuild-child Docker image
- Execute the
docker container run
command to start a new container from the Docker image that you built in the previous step: $ docker container run -p 80:80 --name onbuild-child-container -d onbuild-child
In this command, you have started the Docker container from the onbuild-child
Docker image while exposing port 80
of the container.
- You should be able to view the Apache home page. Go to the
http://127.0.0.1
endpoint from your favorite web browser:Figure 2.20: Customized home page of the Apache web server
- Now, clean up the container. First, stop the Docker container by using the
docker container stop
command:$ docker container stop onbuild-child-container
- Finally, remove the Docker container with the
docker container rm
command:$ docker container rm onbuild-child-container
In this exercise, we observed how we can use the ONBUILD
directive to create a reusable Docker image that is capable of running any HTML file provided to it. We created the reusable Docker image named onbuild-parent
with the Apache web server, with port 80
exposed. This Dockerfile
contains the ONBUILD
directive to copy the HTML files in the context of the Docker image. Then, we created the second Docker image named onbuild-child
, using onbuild-parent
as the base image, that provided a simple HTML file to be deployed to the Apache web server.
Now, let's test our knowledge that we have acquired in this chapter by dockerizing the given PHP application using the Apache web server in the following activity.
Activity 2.01: Running a PHP Application on a Docker Container
Imagine that you want to deploy a PHP welcome page that will greet visitors based on the date and time using the following logic. Your task is to dockerize the PHP application given here, using the Apache web server installed on an Ubuntu base image:
<?php
$hourOfDay = date('H');
if($hourOfDay < 12) {
$message = "Good Morning";
} elseif($hourOfDay > 11 && $hourOfDay < 18) {
$message = "Good Afternoon";
} elseif($hourOfDay > 17){
$message = "Good Evening";
}
echo $message;
?>
This is a simple PHP file that will greet the user based on the following logic:
Figure 2.21: Logic of PHP application
Execute the following steps to complete this activity:
- Create a folder to store the activity files.
- Create a
welcome.php
file with the code provided previously.
- Create a
Dockerfile
and set up the application with PHP and Apache2 on an Ubuntu base image.
- Build and run the Docker image.
- Once completed, stop and remove the Docker container.
Note
The solution for this activity can be found via this link.