Elliot posted in: Developer

Memset has recently begun providing Miniserver images pre-installed with Docker. These images are available as a Memset Stock Image from the “Server Snapshots and Re-imaging” page for each miniserver in your Memset account. This article will take a look at what is Docker, how it compares to similar technologies and a quick guide to creating and running a custom dockerised application.

What is Docker?

Docker is an application containerization technology. Containerization is where an application, and all the components of the OS that it needs to run, are packaged up into a single, distributable image. The image and the application it contains can thereafter be run on any machine that has docker installed.

This may sound quite a lot like a virtual machine in that one can simply spin up a new virtual machine image containing a pre-installed application in much the same way. However, there are important differences that which can be extremely useful in certain situations. A virtual machine contains not only the system binaries required by the app but the entire operating system including the kernel. This makes the images much more resource intensive larger both in terms of storage and when running and also comes with all the problems of managing an entire OS. Docker, on the other hand, shares the kernel with the host OS and any other running docker images but employs strict user resource isolation to separate the docker images fro each other and the host OS. This results in much smaller docker images, faster startup times and far less system resource usage when running.

The most important characteristic of Docker which isn't perhaps the most notable initially is the reproducibility of not just the application but its environment. When a new docker image is created for an application all of the binaries that the application needs to run are installed and configured alongside the application just as if the application was installed on a server. This means that the application and its environment are packaged together into the image. When the image is run on a new server it will behave in exactly the same way as it did on the original server because it and all of its dependencies and configuration have been incorporated into the image along with it.

The fact that the applications environment is packaged along with the application means that workstation environment of the developer no longer has to be kept the same as the laptop of the DevOps engineer or the final production environment. The app carries its environment with it. Once the dev has finished a version the DevOps engineer can test locally on his laptop and subsequently do the same in the staging and production environments.

In addition, the coupling of the environment to the application means that it is possible to run legacy, production and testing applications on the same machine. All of which may have completely different and incompatible environmental requirements e.g. different versions of OS, different DB requirements etc.

Getting started with Docker

This guide will walk through the basic commands for using Docker by creating a simple docker image for the Apache web server and use it to serve a simple “Hello World” index.html webpage.

Installing Docker

If you are using the Memset Docker image on your Miniserver then you can skip this step as it is already installed for you.

If you are not running the Memset Docker Miniserver then please refer to the official installation instructions here for your distribution:

  • https://docs.docker.com/installation/

Docker images and containers

The fundamental parts of Docker are images and containers. An image represents a static snapshot of all the data, binaries, configuration etc that is required to run an application. A container is what is created by Docker when that image is running.

In order to start using custom docker images a base image is required. A base image is the starting point from to which customisations are later added. Images can be created from scratch or download from a repository (either private or public). Docker hosts a public repository of images which can be browsed at https://hub.docker.com/.

This repository contains both official and unofficial images. Official images are those produced by the team that created the original OS or application e.g. there is an official image of Ubuntu produced by Canonical Ltd, or an Apache image produced by the Apache Software Foundation. In general, a base image should be selected from an official source as they are more trustworthy.

Images can either be searched for with a browser from the or from the Docker Hub or from command line:

docker search string

where “string” is the search term.

Downloading an image is done with the following command:

docker pull image

Image names are typically composed of three parts i.e.: username/imagename:tag, where the tag will be the version number or other additional info regarding the image. A typical user/image:tag combination would look like:

smithj/myubuntubase:14.04

Official repositories do not have the user part of the image name. Also, leaving off the tag part will default to the latest version. So the command:

docker pull ubuntu

is equivalent to:

official-ubuntu/ubuntu:latest

All images that have been downloaded or created on the server can be listed with the following:

docker images

For this custom image we are doing to use the official Apache image with is obtained with the following:

docker pull httpd:2.4

We don't want to to use the latest version as the latest image will change to keep track of the latest version of Apache so both the Apache binary and the environment will change over time. By selecting a version number the image will always have that version of Apache. The available versions for an image can be found on their respective Hub pages.

Now that we have a base image to work from we need to create our own custom image by adding layers to the official image we have downloaded. A layer is a modification that is made to an image. A layer could be the inclusion of website data, modifying a configuration file, installing an application or any other change permitted by the Docker image building system.

A new image is created by a series of instructions contained in a build file called a Dockerfile. Each modification is a layer.

The Dockerfile that we need is very simple as we only need to copy an index.html file into the image filesystem. Everything else will be left as supplied by the official image as we do not need to make any changes to Apache's configuration or install any other packages such as PHP to serve a simple index.html file.

Please note that the image created in this guide is only a simple example and should not be run as in a production environment.

The following is a very simple index.html file that we can use to test if our image is able to serve a webpage:

<!DOCTYPE html PUBLIC "-//IETF//DTD HTML 2.0//EN">
<HTML>
   <HEAD>
      <TITLE>
         A Memset docker example
      </TITLE>
   </HEAD>
<BODY>
   <P>
   Hello World!
   </P> 
</BODY>
</HTML>

This file is then placed it into a new directory called “public-html” located in the build directory.

Our Dockerfile contains the following:

FROM httpd:2.4
COPY ./public-html/ /usr/local/apache2/htdocs/

These command mean:

  • FROM – Take the httpd:2.4 image as the base image.
  • COPY – Add the contents of the source dir ./public-html/ and copy it to the destination dir /usr/local/apache2/htdocs/ inside the image filesystem.

If the image specified in the FROM command is not present on the build server docker will download it automatically.

Now that the index.html file and the Dockerfile are present in the build directory the command to build the new image is the following:

docker build -t my-apache2 .

This instructs Docker to build a new image from the current directory (which contains the Dockerfile) and give it the name my-apache2 via the -t switch.

Once the build is finished:

docker images

will list our new image.

Now that the image is ready we need to start it in a container so that we can test it. Docker has many options for starting and running containers. The command we will use to start our new container is the following:

docker run -it --rm -p 80:80 my-apache2

The parts of this command are explained as following:

  • -it These two switches will allocate a new tty and make it interactive.
  • -rm By default, once a container has finished running all its final state will be preserved on the host server for debugging. This can add up to a lot of space if containers are started and stopped a great deal e.g. during testing. The -rm command will instruct docker not to store a copy of the container state on exit.
  • -p 80:80 This command maps a port on the host server to a port on the container. In this instance requests received by the host server on port 80 will be diverted to port 80 on the container. This allows the internet to connect to the web server when the image is run.

This command will run the container in the foreground of the shell with all messages and logs being outputted directly to the shell where they can be easily viewed.

We can now test the container once it is running by visiting the server's public IP in a browser. The browser will show a white page with the words "Hello World" and the following line will get printed to the terminal running the container:

186.76.252.69 - - [02/Nov/2015:21:56:24 +0000] "GET / HTTP/2.0" 200 197

This indicates that the web server has successfully served the index.html file.

The Docker container will continue running until it is stopped. The correct way to stop the container is with the command:

docker stop container

where “container” is either the name of the running container or its ID. The name of the container will be randomly allocated by Docker if no name is specified in the startup command. Docker allocates a name comprised of two randomly chosen words separated by an underscore e.g. "boring_mestorf" . The command to see a list of running containers is:

docker ps

The output will show some information about each running container including their names and IDs.

This guide has only touched on the basic commands and uses of Docker. If you wish to learn more then you should pursue the excellent documentation available on the Docker documentation repository.