Docker - A Simple Use Case
In a previous post, we showed how to setup an Apache web server on an Amazon EC2 instance.
This was fairly quick and straightforward. However, when deploying an actual web application, it is likely to take a lot longer as your application will have dependencies and require other software to be installed, run-time environment variables need to be set, the web server needs to be configured, etc.
Further, what happens if one day you want to deploy your app on a different server? Will it work out-of-the-box? Do you remember all the steps to do the configuration, setup, etc.? Do you have the necessary tests ready to check the deployment went successfully? Even if all this is documented, you will have to follow each step carefully. Upshot - all this can be time-consuming and stressful.
Enter Docker to the rescue. Your whole app and everything required to run it is packaged neatly into a self-contained unit, a Docker container which is simply a Unix process. All you need to do is start it.
In this post, we are going to setup the simplest possible web application on a new server using Docker. All the app does is serve up a single static HTML file, i.e. the app consists of just a homepage.
Step 1
Login to your new server. As the server is new, there is no web server software (Apache, Nginx, etc.) installed thus navigating to http://myipaddress
returns an unable to connect error
in the browser (even after opening up port 80).
Step 2
Install Docker. The exact command(s) will depend on your server’s OS, but on Amazon Linux AMI
sudo yum install -y docker
suffices.
Step 3
Start the Docker service
sudo service docker start
Step 4
We are going to use Apache to serve up our homepage. Usually, we look for /var/www/html
and place our HTML files there. However, as Apache isn’t installed, /var/www
does not exist.
Instead, our app is housed in the following directory structure
home
└── ec2-user
└── project-root
├── Dockerfile
└── public-html
└── pokemon.html
pokemon.html
contains our homepage’s HTML
<table>
<tr>
<th>#</th>
<th>Pokémon</th>
<th>HP</th>
<th>Attack</th>
<th>Defence</th>
<th>Speed</th>
</tr>
<tr>
<td>001</td>
<td>Bulbasaur</td>
<td>45</td>
<td>49</td>
<td>49</td>
<td>45</td>
</tr>
<tr>
<td>002</td>
<td>Ivysaur</td>
<td>60</td>
<td>62</td>
<td>63</td>
<td>60</td>
</tr>
<tr>
<td>006</td>
<td>Charizard</td>
<td>78</td>
<td>84</td>
<td>78</td>
<td>100</td>
</tr>
</table>
Step 5
At this point, it is worth saying a couple of things about Docker images and containers.
A Docker image is to a Docker container what a class is to an object in OOP, i.e. one can think of the latter as an instance of the former.
So far, all we have done is start the Docker service. We have no images nor containers, thus
sudo docker image ls
and
sudo docker container ls
return nothing.
In OOP, we know that before we can create an object, we need to define a class. Similarly, before creating any containers, we need an image. Images are created via the Docker command build
.
Go into the project root
cd ~/project-root # adjust accordingly
By default, build
looks for a file called Dockerfile
in the path specified. It also takes a bunch of options
sudo docker build --help
In particular, we are interested in
-t, --tag list Name and optionally a tag in the 'name:tag' format (default [])
A Dockerfile is just a text file, in our case with the lines
FROM httpd:2.4
COPY ./public-html/ /usr/local/apache2/htdocs/
As our Dockerfile is in the project root,
sudo docker build --tag=myfirstdockerimage:v0 .
should output something like
Sending build context to Docker daemon 3.584 kB
Step 1/2 : FROM httpd:2.4
2.4: Pulling from library/httpd
aa18ad1a0d33: Pull complete
57095d1c44f2: Pull complete
b020b6bcf3ce: Pull complete
c2a092dd4899: Pull complete
df8c38ba7997: Pull complete
f6150a1ea909: Pull complete
59a7f628db89: Pull complete
Digest: sha256:9fb3f88deb7b743e10261d4c91614b085fe5d85c11a244fca5fad5bb0edea491
Status: Downloaded newer image for httpd:2.4
---> b669148bb5a5
Step 2/2 : COPY ./public-html/ /usr/local/apache2/htdocs/
---> c0b0772949bc
Removing intermediate container 08e120a74476
Successfully built c0b0772949bc
on the command line which means we just created our first Docker image!
The first line of our Dockerfile downloaded the image of a bare-bones Linux environment with Apache 2.4 installed from the offical Apache Docker repository, the second augmented this image by copying the contents of public-html
into /usr/local/apache2/htdocs/
in the newly-created Linux environment.
Now,
sudo docker image ls
should output something like
REPOSITORY TAG IMAGE ID CREATED SIZE
myfirstdockerimage v0 c0b0772949bc 53 seconds ago 177 MB
httpd 2.4 b669148bb5a5 32 hours ago 177 MB
which shows Docker incrementally builds images following the Dockerfile line-by-line.
Step 6
Now that we have an image, containers can be created from it. This is done via the Docker command run
sudo docker run --detach --name=pokemon --restart=always --publish=80:80 myfirstdockerimage:v0
Here we use the options name
to specify the name of the container, restart
to automatically restart the container if it crashes, publish
to map the relevant port inside the container (here 80) to the desired port on the server (set by convention to 80). We supply the name and the tag of the image we wish to run (if we omit the tag, it defaults to myfirstdockerimage:latest
which errors as this image does not exist).
This should output something like
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
966ffc2fc69f myfirstdockerimage:v0 "httpd-foreground" 2 minutes ago Up 2 minutes 0.0.0.0:80->80/tcp pokemon
and navigating to
http://myipaddress/pokemon.html
in a browser should return our app’s homepage

and
sudo docker container ls
should return something like
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
966ffc2fc69f myfirstdockerimage:v0 "httpd-foreground" 2 hours ago Up 2 hours 0.0.0.0:80->80/tcp pokemon
Finally, as mentioned above, the container runs as a process on your server. To stop it,
sudo docker container stop pokemon