Introduction

A Dockerfile contains intructions on how to build a Docker image. The instructions is in the form of Directives:

# This is a comment
DIRECTIVE argument

The 5 most common Directives are:

  • FROM - The FROM directive is the foundation of a Dockerfile. It’s used to specify an image on which the custom Docker image will be build.

  • LABEL - A LABEL is used to add metadata about an image. It can viewed with docker image inspect.

  • RUN - The RUN directive will run commands only during the image building process. You can have multiple RUN Directives.

  • CMD - The CMD Directive will execute a command once the container is launched. If there’re multiple CMD, only the last one will run. It can be overriden on the command line.

  • ENTRYPOINT - Similar to the CMD directive, but it can’t be overridden by just appending a new command on the command line. The --entrypoint flag is required to so.

Creating our first Dockerfile

Creating a Dockerfile:

FROM alpine

LABEL maintainer=kavish@docker.com

RUN apk update

CMD ["Docker Workshop Exercises"]

ENTRYPOINT ["echo", "You are reading"]

The syntax to build an Image is: docker image build .. The dot means the current directory.

Building the image with -t to specify a tag:

root@ubuntu-local:~/Docker_Workshop/2.02# docker build -t welcome:1.0 .
Sending build context to Docker daemon  2.048kB
Step 1/5 : FROM alpine
 ---> 49f356fa4513
Step 2/5 : LABEL maintainer=kavish@docker.com
 ---> Running in a9dcad4976d2
Removing intermediate container a9dcad4976d2
 ---> 15830818288e
Step 3/5 : RUN apk update
 ---> Running in ab78292729a4
fetch https://dl-cdn.alpinelinux.org/alpine/v3.13/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.13/community/x86_64/APKINDEX.tar.gz
v3.13.5-127-g92dd8b922b [https://dl-cdn.alpinelinux.org/alpine/v3.13/main]
v3.13.5-127-g92dd8b922b [https://dl-cdn.alpinelinux.org/alpine/v3.13/community]
OK: 13891 distinct packages available
Removing intermediate container ab78292729a4
 ---> bbf74a33f3fc
Step 4/5 : CMD ["Docker Workshop Exercises"]
 ---> Running in 7c310e4191a9
Removing intermediate container 7c310e4191a9
 ---> fcd3a0bb479b
Step 5/5 : ENTRYPOINT ["echo", "You are reading"]
 ---> Running in de16d1d7450f
Removing intermediate container de16d1d7450f
 ---> 17cccbb6d2e3
Successfully built 17cccbb6d2e3
Successfully tagged welcome:1.0

Listing available Images:

root@ubuntu-local:~/Docker_Workshop/2.02# docker image ls
REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
welcome       1.0       17cccbb6d2e3   3 minutes ago   7.6MB
postgres      12        e782ad565d74   6 weeks ago     314MB
alpine        latest    49f356fa4513   7 weeks ago     5.61MB
nginx         latest    6084105296a9   2 months ago    133MB
hello-world   latest    d1165f221234   2 months ago    13.3kB

Running the Container:

root@ubuntu-local:~/Docker_Workshop/2.02# docker run welcome:1.0
You are reading Docker Workshop Exercises

The “You are reading” is provided with the ENTRYPOINT directive, and Docker Workshop Exercises comes from the CMD directive.

Overriding CMD and ENTRYPOINT

If I supply an argument, only CMD will be overridden:

root@ubuntu-local:~/Docker_Workshop/2.02# docker run welcome:1.0 "my blog"
You are reading my blog

To override the ENTRYPOINT, use --entrypoint. The syntax is:

docker run --entrypoint [EXECUTABLE] [IMAGE] [OPTIONAL ARGUMENTS]

Let’s say, I want to run cat /etc/alpine-release:

root@ubuntu-local:~# docker run --entrypoint "cat" welcome:1.0 "/etc/alpine-release"
3.13.4

The argument should only be passed after the [IMAGE].

ARG and ENV

ARG Directive

The ARG directive let you define variables that be altered during build time. It’s the only directive that can precede the FROM directive. ARGS are only available during the build process. It takes the following format:

ARG [varname]=[value]

ARG USER=admin

ARG TERM=xterm-256color

ARG VERSION=1.0.1

You can have multiple ARG directives.

ENV Directive

The ENV directive is used to set environment variables, which will be available during run time. It takes the format of:

ENV [KEY]=[VALUE]

ENV [KEY]=[VALUE] [KEY]=[VALUE]

ENV PATH=$PATH:/usr/local/custom_app/bin/

ENV PATH=$PATH:$HOME/go/bin/ GOLOGS=$HOME/logs/goerrors/

Once a variable is set in a Dockerfile, it will available in all layers, and every container launched from that particular image.

Exercise 2.01: Using ENV and ARG

You manager 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 name of your department, and application directory as the environment variables:

ARG VERSION=latest

FROM ubuntu:$VERSION

LABEL maintainer=kavosh@docker.com

ENV DEPARTMENT=Tech APP_DIR=/usr/local/custom_app/bin

CMD ["env"]

Building the image with -t env-arg --build-arg VERSION=19.04:

root@ubuntu-local:~/Docker_Workshop/2.03# docker image build -t env-arg --build-arg VERSION=19.04 .
Sending build context to Docker daemon  2.048kB
Step 1/5 : ARG VERSION=19.04
Step 2/5 : FROM ubuntu:$VERSION
19.04: Pulling from library/ubuntu
4dc9c2fff018: Pull complete
0a4ccbb24215: Pull complete
c0f243bc6706: Pull complete
5ff1eaecba77: Pull complete
Digest: sha256:2adeae829bf27a3399a0e7db8ae38d5adb89bcaf1bbef378240bc0e6724e8344
Status: Downloaded newer image for ubuntu:19.04
 ---> c88ac1f841b7
Step 3/5 : LABEL maintainer=kavish@docker.com
 ---> Running in c183877951ab
Removing intermediate container c183877951ab
 ---> 4118c23ace16
Step 4/5 : ENV DEPARTMENT=Tech APP_DIR=/usr/local/custom_app/bin
 ---> Running in 7a2768c609f9
Removing intermediate container 7a2768c609f9
 ---> 31097b3ac5cf
Step 5/5 : CMD ["env"]
 ---> Running in d3730c298fb1
Removing intermediate container d3730c298fb1
 ---> 7ea47c6e9dea
Successfully built 7ea47c6e9dea
Successfully tagged env-arg:latest

The image name is env-arg. It’s using Ubuntu 19.04 as the parent image instead of the latest version. Starting a container from the new image:

root@ubuntu-local:~/Docker_Workshop/2.03# docker run env-arg
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=0938055665cd
DEPARTMENT=Tech
APP_DIR=/usr/local/custom_app/bin
HOME=/root

All environment variables are defined. Verifying the Distro:

root@ubuntu-local:~/Docker_Workshop/2.03# docker run  --entrypoint cat env-arg /etc/*release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=19.04
DISTRIB_CODENAME=disco
DISTRIB_DESCRIPTION="Ubuntu 19.04"
NAME="Ubuntu"
VERSION="19.04 (Disco Dingo)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 19.04"
VERSION_ID="19.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=disco
UBUNTU_CODENAME=disco

WORDIR, COPY and ADD Directive

WORKDIR

The WORKDIR directive is to specify the current of a running container. Any ADD, CMD, COPY, ENTRYPOINT, and RUN will executed in this directory. The format is:

WORKDIR /home/superuser

It the directory doesn’t exist, it will be created. If there’re multiple paths like so:

root@ubuntu-local:~/Docker_Workshop/2.04# cat Dockerfile
FROM alpine:latest

WORKDIR one
WORKDIR two
WORKDIR three

RUN pwd

They will be all combined as a single path(see step 5):

root@ubuntu-local:~/Docker_Workshop/2.04# docker build -t testdir .
Sending build context to Docker daemon  2.048kB
Step 1/5 : FROM alpine:latest
 ---> 49f356fa4513
Step 2/5 : WORKDIR one
 ---> Running in 403565c01987
Removing intermediate container 403565c01987
 ---> cee595f24d4c
Step 3/5 : WORKDIR two
 ---> Running in 989f6105d117
Removing intermediate container 989f6105d117
 ---> 36a424030ef5
Step 4/5 : WORKDIR three
 ---> Running in 9d11043e9c33
Removing intermediate container 9d11043e9c33
 ---> b49753df603e
Step 5/5 : RUN pwd
 ---> Running in eaa5f2b01321
/one/two/three
Removing intermediate container eaa5f2b01321
 ---> 57769c57b114
Successfully built 57769c57b114
Successfully tagged testdir:latest

If one of the WORKDIR is an absolute path:

root@ubuntu-local:~/Docker_Workshop/2.04# cat Dockerfile
FROM alpine:latest

WORKDIR one
WORKDIR /two
WORKDIR three

RUN pwd

Then the ones above it will be neglected(see step 5):

root@ubuntu-local:~/Docker_Workshop/2.04# docker build -t testdir1 .
Sending build context to Docker daemon  2.048kB
Step 1/5 : FROM alpine:latest
 ---> 49f356fa4513
Step 2/5 : WORKDIR one
 ---> Using cache
 ---> cee595f24d4c
Step 3/5 : WORKDIR /two
 ---> Running in 97e2dfc45638
Removing intermediate container 97e2dfc45638
 ---> 224ebc1cf891
Step 4/5 : WORKDIR three
 ---> Running in c4fcf9723e3a
Removing intermediate container c4fcf9723e3a
 ---> ebdc0d3ac347
Step 5/5 : RUN pwd
 ---> Running in 3176137a0d7d
/two/three
Removing intermediate container 3176137a0d7d
 ---> 99fd3233f785
Successfully built 99fd3233f785
Successfully tagged testdir1:latest

COPY

The COPY directive will copy files from your host to the docker image during the build process. Wildcards can also be specified. The format is:

COPY [src] [dst]

COPY index.html /var/www/html/index.html

COPY *.html /var/www/html/index.html

In addition to that, the --chown flag can be used to specify ownership of the files:

COPY --chown=kavish:admin index.html /var/www/html

ADD

The ADD directive is similar to COPY. Instead of a source, you can pass a URL. It also has the ability to extract compressed files. The format is:

ADD [src] [dst]

ADD https://github.com/kavishgr/scripts/script01.txt /home/admin/script.txt

ADD file.tar.gz /home/kavishgr/scripts/

Exercise 2.04 Using WORKDIR, COPY, and ADD directives

Deploy a custom HTML file to an Apacher web server. You will use Ubuntu as the base image, and install Apache on top of it. Then, you’ll copy your custom index.hml file to the Docker image and download the Docker logo for Apple M1 to be used in the custom index.html file.

Creating an index.html:

root@ubuntu-local:~/Docker_Workshop/2.04# cat index.html

<html>
 <body>
    <h1> Welcome to the Docker Workshop</h1>
    <img src="logo.png" height="350" width="500"/>
 </body>
</html>

The image will be downloaded during the image build process. Creating the dockerfile:

root@ubuntu-local:~/Docker_Workshop/2.04# cat Dockerfile
FROM ubuntu:latest

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && apt-get install apache2 -y

WORKDIR /var/www/html

COPY --chown=www-data:www-data index.html .

ADD https://www.docker.com/sites/default/files/d8/2020-12/img_docker_applem1_b.png ./logo.png

CMD ["ls"]

To run apt without interactive dialogue, I had to set ENV DEBIAN_FRONTEND=noninteractive. The output(apache2 installation redacted):

root@ubuntu-local:~/Docker_Workshop/2.04# docker image build -t workdir-copy-add .
Sending build context to Docker daemon  3.072kB
Step 1/7 : FROM ubuntu:latest
 ---> 7e0aa2d69a15
Step 2/7 : ENV DEBIAN_FRONTEND=noninteractive
 ---> Running in 2eb3145ac3b6
Removing intermediate container 2eb3145ac3b6
 ---> b1f4f232da91
Step 3/7 : RUN apt-get update && apt-get install apache2 -y
 ---> Running in 685c600f952a

---REDACTED---

Running hooks in /etc/ca-certificates/update.d...
done.
Removing intermediate container 685c600f952a
 ---> 2a196c6acf9b
Step 4/7 : WORKDIR /var/www/html
 ---> Running in a04d0ba515d0
Removing intermediate container a04d0ba515d0
 ---> 1f2273c1bb3e
Step 5/7 : COPY --chown=www-data:www-data index.html .
 ---> 075f88ab8ed7
Step 6/7 : ADD https://www.docker.com/sites/default/files/d8/2020-12/img_docker_applem1_b.png ./logo.png
Downloading    111kB/111kB

 ---> 4b85724a07b9
Step 7/7 : CMD ["ls"]
 ---> Running in bf9de8aed452
Removing intermediate container bf9de8aed452
 ---> 96a6a52c9e83
Successfully built 96a6a52c9e83
Successfully tagged workdir-copy-add:latest
root@ubuntu-local:~/Docker_Workshop/2.04#

Running the container:

root@ubuntu-local:~/Docker_Workshop/2.04# docker run workdir-copy-add
index.html
logo.png

The USER Directive

By default, Docker will use the root user as the default user inside containers. The USER directive to a specify a non-root user as the default user inside the container. It is a great way to improve security. The format is:

USER [user]

USER kavish

USER [user]:[group]

The username and group should be valid, or else docker will throw an error.

Exercise 2.05 Using USER directive

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.

The Dockerfile:

root@ubuntu-local:~/Docker_Workshop/2.05# cat Dockerfile
FROM ubuntu

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update > /dev/null && apt-get install apache2 -y > /dev/null

USER www-data

CMD ["whoami"]

Building the image:

root@ubuntu-local:~/Docker_Workshop/2.05# docker image build -t user .
Sending build context to Docker daemon  2.048kB
Step 1/5 : FROM ubuntu
 ---> 7e0aa2d69a15
Step 2/5 : ENV DEBIAN_FRONTEND=noninteractive
 ---> Using cache
 ---> b1f4f232da91
Step 3/5 : RUN apt-get update > /dev/null && apt-get install apache2 -y > /dev/null
 ---> Running in 5c0f6e008b77
debconf: delaying package configuration, since apt-utils is not installed
Removing intermediate container 5c0f6e008b77
 ---> 10383145a367
Step 4/5 : USER www-data
 ---> Running in e9d5ecde701a
Removing intermediate container e9d5ecde701a
 ---> 21970b7d3176
Step 5/5 : CMD ["whoami"]
 ---> Running in e46864a48c31
Removing intermediate container e46864a48c31
 ---> cb5dc37efc86
Successfully built cb5dc37efc86
Successfully tagged user:latest

Running a container:

root@ubuntu-local:~/Docker_Workshop/2.05# docker run user
www-data

The VOLUME Directive

The VOLUME directive is used to make data persistent, by storing the container’s data on your docker host. The format is:

VOLUME [Path inside the container]

VOLUME /var/www/html/

VOLUME /var/www/html/ /var/log/apache2

VOLUME ["/var/www/html/"] # or a json array

All files changed or created on the volume will copied inside a randomly named directory on the host machine inside /var/lib/docker/volumes/.

Exercise 2.06 Using VOLUME Directive

Setup an Apache webserver, but keep the Apache logs in case of a container failure. Persist the log files by mounting the Apache log path to the underlying Docker host.

The content of my Dockerfile:

FROM ubuntu

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update > /dev/null && apt-get install apache2 -y > /dev/null

VOLUME ["/var/log/apache2"]

Building the image:

root@ubuntu-local:~/Docker_Workshop/2.06# docker build -t volume_test .
Sending build context to Docker daemon  2.048kB
Step 1/4 : FROM ubuntu
 ---> 7e0aa2d69a15
Step 2/4 : ENV DEBIAN_FRONTEND=noninteractive
 ---> Using cache
 ---> b1f4f232da91
Step 3/4 : RUN apt-get update > /dev/null && apt-get install apache2 -y > /dev/null
 ---> Using cache
 ---> 10383145a367
Step 4/4 : VOLUME ["/var/log/apache2"]
 ---> Running in c3227272cad8
Removing intermediate container c3227272cad8
 ---> 42813938c1a4
Successfully built 42813938c1a4
Successfully tagged volume_test:latest

Spawning a container with an interactive shell:

root@ubuntu-local:~/Docker_Workshop/2.06# docker run -it --name volume-container volume_test /bin/bash
root@7501506cba26:/#
root@7501506cba26:/#
root@7501506cba26:/# ls -l /var/log/apache2/
total 0
-rw-r----- 1 root adm 0 May 27 07:49 access.log
-rw-r----- 1 root adm 0 May 27 07:49 error.log
-rw-r----- 1 root adm 0 May 27 07:49 other_vhosts_access.log

The volume should have the same contents as above.

Inspecting Volumes

Get the mount information by running docker container inspect [container]:

root@ubuntu-local:~/Docker_Workshop/2.06# docker container inspect volume-container

---REDACTED---

"Mounts": [
            {
                "Type": "volume",
                "Name": "3d6151babad8bce9176eebc711b82bc96e82eedc36942cff7f5e791729add1f5",
                "Source": "/var/lib/docker/volumes/3d6151babad8bce9176eebc711b82bc96e82eedc36942cff7f5e791729add1f5/_data",
                "Destination": "/var/log/apache2",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],

The volume location on your docker host is /var/lib/docker/volumes/3d6151babad8bce9176eebc711b82bc96e82eedc36942cff7f5e791729add1f5/_data.

To inspect the volume itself, run docker volume inspect [volume-name]:

root@ubuntu-local:~/Docker_Workshop/2.06# docker volume inspect 3d6151babad8bce9176eebc711b82bc96e82eedc36942cff7f5e791729add1f5
[
    {
        "CreatedAt": "2021-05-29T07:06:59Z",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/3d6151babad8bce9176eebc711b82bc96e82eedc36942cff7f5e791729add1f5/_data",
        "Name": "3d6151babad8bce9176eebc711b82bc96e82eedc36942cff7f5e791729add1f5",
        "Options": null,
        "Scope": "local"
    }
]

Listing its contents:

root@ubuntu-local:/var/lib/docker/volumes/3d6151babad8bce9176eebc711b82bc96e82eedc36942cff7f5e791729add1f5/_data# ls -l
total 0
-rw-r----- 1 root adm 0 May 27 07:49 access.log
-rw-r----- 1 root adm 0 May 27 07:49 error.log
-rw-r----- 1 root adm 0 May 27 07:49 other_vhosts_access.log

The EXPOSE and HEALTHCHECK Directive

EXPOSE

The EXPOSE directive is used to tell docker that a container is listening on a port. By default the exposed port is only available for inter-container communication. To be able to access it externally, we have supply the -p flag. The format is:

EXPOSE [port]

EXPOSE [PORT]/[PROTOCOL]

EXPORT 80

EXPORT 80/udp

By just specifying EXPOSE and a port, docker assumes it’s TCP. On the command line, the port redirection is set like so:

docker run -p [host_port]:[container_port] ubuntu

docker run -p 80:80 ubuntu

HEALTHCHECK

The HEALTHCHECK directive will check whether the containers are running healthily. This is very importabt when running Docker containers in production environments. There can be only one HEALTHCHECK directive inside a Dockerfile. The format is:

HEALTHCHECK [options_optional] CMD COMMAND

HEALTHCHECK CMD curl -f http://localhost || exit 1

Return code 0 means a healthy container, and 1 is used to denote an unhealthy container. The avilable options are:

  • –interval=DURATION: Specifies the interval between each health check (default: 30s).
  • –timeout=DURATION: If a return code 0 or success is not received within this period, the health check is considered failed (default: 30s).
  • –start-period=DURATION: The duration to wait before running the first health check (default: 0s)
  • –retries=N: Number of retries before the container is considered a failure (default: 3).

An example:

HEALTHCHECK --interval=1m --timeout=2s --start-period=2m --retries=3 CMD curl -f http://localhost || exit 1

Exercise 2.07 Using EXPOSE and HEALTHCHECK

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.

The Dockerfile:

FROM ubuntu

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update > /dev/null && apt-get install apache2 curl -y > /dev/null

HEALTHCHECK CMD curl -f http://localhost || exit 1

EXPOSE 80

ENTRYPOINT ["apache2ctl", "-D", "FOREGROUND"]

Building the image:

root@ubuntu-local:~/Docker_Workshop# docker image build -t expose-healthcheck .
Sending build context to Docker daemon  11.26kB
Step 1/6 : FROM ubuntu
 ---> 7e0aa2d69a15
Step 2/6 : ENV DEBIAN_FRONTEND=noninteractive
 ---> Using cache
 ---> b1f4f232da91
Step 3/6 : RUN apt-get update > /dev/null && apt-get install apache2 curl -y > /dev/null
 ---> Running in 9b9e508fe56c
debconf: delaying package configuration, since apt-utils is not installed
Removing intermediate container 9b9e508fe56c
 ---> b824bf066d62
Step 4/6 : HEALTHCHECK CMD curl -f http://localhost || exit 1
 ---> Running in 35b6ac5bd9e4
Removing intermediate container 35b6ac5bd9e4
 ---> 2341fee312d6
Step 5/6 : EXPOSE 80
 ---> Running in e080756b440a
Removing intermediate container e080756b440a
 ---> f8fe8bedc33e
Step 6/6 : ENTRYPOINT ["apache2ctl", "-D", "FOREGROUND"]
 ---> Running in 2ae4a6b6b97e
Removing intermediate container 2ae4a6b6b97e
 ---> 934f8b63c83e
Successfully built 934f8b63c83e
Successfully tagged expose-healthcheck:latest

Running the container in the background, by redirecting port 88 on my host to port 80 on the container:

root@ubuntu-local:~/Docker_Workshop# docker run -p 88:80 --name exp_health01 -d expose-healthcheck

88e1848a715aa56307821c5b457c67944ca039de89b9d89a653fad153874284c

List the running containers, to verify its status(which is healthy):

root@ubuntu-local:~/Docker_Workshop# docker container list
CONTAINER ID   IMAGE                COMMAND                  CREATED              STATUS                        PORTS                               NAMES
88e1848a715a   expose-healthcheck   "apache2ctl -D FOREG…"   About a minute ago   Up About a minute (healthy)   0.0.0.0:88->80/tcp, :::88->80/tcp   exp_health01

Viewing the the Apache home page from our Docker host:

root@ubuntu-local:~/Docker_Workshop# curl -s http://localhost:88 | grep title
    <title>Apache2 Ubuntu Default Page: It works</title>

The ONBUILD Directive

The ONBUILD directive is used to create a reusable Docker image or a prerequisite image. When creating an image with ONBUILD, its instructions will only be used or executed when we’re creating another image on top of the prerequisite image(in a FROM directive).

Its instructions could be anything:

ONBUILD [INSTRUCTION]

ONBUILD ENTRYPOINT ["echo", "Running ONBUILD directive"]

ONBUILD COPY *.txt /home/test/

As the examples stated above, the instructions will not be executed, unless we create an image from that custom image. You can use docker image inspect to inspect if there’s any ONBUILD instructions listed.

Execise 2.08 Using ONBUILD

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.

Creating the Dockerfile:

FROM ubuntu

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update > /dev/null && apt-get install apache2 -y > /dev/null

ONBUILD COPY *.html /var/www/html

EXPOSE 80

ENTRYPOINT ["apache2ctl", "-D", "FOREGROUND"]

Creating an index.html:

<html>
      <body>
        <h1>Learning Docker ONBUILD directive</h1>
      </body>
</html>

Building the image:

root@ubuntu-local:~/Docker_Workshop/2.08/parent# docker image build -t onbuild-parent .
Sending build context to Docker daemon  3.072kB
Step 1/6 : FROM ubuntu
 ---> 7e0aa2d69a15
Step 2/6 : ENV DEBIAN_FRONTEND=noninteractive
 ---> Using cache
 ---> b1f4f232da91
Step 3/6 : RUN apt-get update > /dev/null && apt-get install apache2 -y > /dev/null
 ---> Using cache
 ---> 10383145a367
Step 4/6 : ONBUILD COPY *.html /var/www/html
 ---> Running in f97948937908
Removing intermediate container f97948937908
 ---> 5a921baf2544
Step 5/6 : EXPOSE 80
 ---> Running in 733d157255a8
Removing intermediate container 733d157255a8
 ---> f9841b541be1
Step 6/6 : ENTRYPOINT ["apache2ctl", "-D", "FOREGROUND"]
 ---> Running in 78cdf0607c08
Removing intermediate container 78cdf0607c08
 ---> 1ee32d241507
Successfully built 1ee32d241507
Successfully tagged onbuild-parent:latest

Spawning a container, and viewing the webpage:

root@ubuntu-local:~/Docker_Workshop/2.08/parent# docker container run -p 88:80 --name parent-container -d onbuild-parent
b07f7446c8ceeabe6c6cb653b0c1030b95f9fb3d85fdac26aecc1b34d50305af

root@ubuntu-local:~/Docker_Workshop/2.08/parent# curl -s http://localhost:88 | grep -i onbuild
root@ubuntu-local:~/Docker_Workshop/2.08/parent# curl -s http://localhost:88 | grep title
    <title>Apache2 Ubuntu Default Page: It works</title>

It’s still presenting the default apache web page. Remove the container, and create a new Dockerfile in another directory:

root@ubuntu-local:~/Docker_Workshop/2.08/child# cat Dockerfile
FROM onbuild-parent

Copy the index.html created previously inside the new directory, and build the image:

root@ubuntu-local:~/Docker_Workshop/2.08/child# docker build -t onbuild-child .
Sending build context to Docker daemon  3.072kB
Step 1/1 : FROM onbuild-parent
# Executing 1 build trigger
 ---> e9e0a5e69a29
Successfully built e9e0a5e69a29
Successfully tagged onbuild-child:latest

Spawn a container and view the web page:

root@ubuntu-local:~/Docker_Workshop/2.08/child# docker run -p 88:80 --name out-child01 -d onbuild-child
22015836d538895666d085f06932cfe3436c4084fe9b9639714e9cbc61b6970b
root@ubuntu-local:~/Docker_Workshop/2.08/child#
root@ubuntu-local:~/Docker_Workshop/2.08/child# curl -s http://localhost:88 | grep -i build
        <h1>Learning Docker ONBUILD directive</h1>

Now the ONBUILD directive gets executed.

Activiy 2.09 Running a PHP Application

Imagine that you want to deploy a PHP welcome page that will greet visitors based on the date and time using the following logic:

<?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;
  ?>

Your task is to dockerize the PHP application given here, using the Apache web server installed on an Ubuntu base image.

Save the above content in a file called welcome.php. Create a Dockerfile with the following content:

FROM ubuntu

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update > /dev/null && apt-get install apache2 php curl -y > /dev/null

COPY *.php /var/www/html

HEALTHCHECK --interval=5s --timeout=3s --retries=3 CMD curl -f http://localhost || exit 1

ENTRYPOINT ["apache2ctl", "-D", "FOREGROUND"]

Build the image:

root@ubuntu-local:~/Docker_Workshop/Activity_2.01# docker build -t activity-02-01 .
Sending build context to Docker daemon  3.072kB
Step 1/6 : FROM ubuntu
 ---> 7e0aa2d69a15
Step 2/6 : ENV DEBIAN_FRONTEND=noninteractive
 ---> Using cache
 ---> b1f4f232da91
Step 3/6 : RUN apt-get update > /dev/null && apt-get install apache2 php curl -y > /dev/null
 ---> Running in d8c0e429e323
debconf: delaying package configuration, since apt-utils is not installed
Removing intermediate container d8c0e429e323
 ---> 132fe6152280
Step 4/6 : COPY *.php /var/www/html
 ---> d84310848c58
Step 5/6 : HEALTHCHECK --interval=5s --timeout=3s --retries=3 CMD curl -f http://localhost || exit 1
 ---> Running in aa6c1fd070c6
Removing intermediate container aa6c1fd070c6
 ---> 397b9cdd621e
Step 6/6 : ENTRYPOINT ["apache2ctl", "-D", "FOREGROUND"]
 ---> Running in 1b2f6dd5720f
Removing intermediate container 1b2f6dd5720f
 ---> e979437e6edd
Successfully built e979437e6edd
Successfully tagged activity-02-01:latest

Running a container:

root@ubuntu-local:~# docker run -p 88:80 --name activity-02-01-container -d activity-02-01
dadee2424ab6aebfee26a8b6e2b55a652c1264b464f2455b01980e69e53346c7
root@ubuntu-local:~#
root@ubuntu-local:~# curl -s http://localhost:88/welcome.php ; echo
Good Morning
root@ubuntu-local:~#

The PHP application is up and running.