Yet Another Blog About Geek Experiments

A first look into Dockerfiles

A week ago, I introduced the framework Docker. Docker is a lightview virtualized environment. It allows to build, manage and run containers to easily deploy an app in an iso environment.
I will introduce today how to create containers interactively and through Dockerfile.

Hands on containers and images

If you’re not familiar with Docker, I strongly recommend you to have a look to their interactive tutorial. It is well done, efficient, and get you into the swing of things. The Dockerfile tutorial will also give you all the basis. I will try here to sum up the more important concepts.

What is a container?

A container can be represented by two main components:

A running job

A filesystem modified by the job

The filesystem itself is a multilayered union filesystem, with the top layer saving the current modifications, and the underlying layers read only images. Let’s have a deeper look on what is a multilayered union filesystem.

Images and AUFS

Let’s build our own customized image! First of all, let’s pull an image from the docker index.

~$ sudo docker pull ubuntu:precise

This will download an image with the files of Ubuntu Precise distribution (without the Kernel, as it uses the host kernel).

You can have information about downloaded images with the command docker images:

We will then interact with it by launching a job from this filesystem.

~$ sudo docker run ubuntu:precise apt-get install -y memcached
[...]

When executing this command, we create a container. This container will create a writable layer for its filesystem, and base it upon the image ubuntu:precise. It will then launch the process apt-get with the argument install -y memcached

I didn’t affect any name to the container in the previous command. To find the id of a running container, you can type the command docker ps:

Here, I don’t have any running container. Indeed, the status of the container is related to the status of its running job. As a consequence, once apt-get install -y memcached returned, the container is stopped.

To display all containers currently on your machine, you can type docker ps -a:

You will now run a new container, with the process /bin/bash, -i and -t options for respectively running it interactively and attaching a pseudo tty, --name to name our container, that will put a writable layer upon our image 9f97edd, which is itself based on our images ubuntu:precise.

You may have understand now that referencing the images by their id won’t be handy, That’s why you will prefer to organize your images under

If you want to organize your images, it is better to commit them under your local repository.
You could have run this command to commit from the container:

~$ sudo docker commit 9f97e yabage/ubuntu-memcached

Or you can tag the current image directly

~$ sudo docker tag 9f97e yabage/ubuntu-memcached

The Dockerfile

You have seen how to build an image by interacting directly with the container. Nevertheless, most of the time you will want to share your images through “recipes” allowing others to build themselves your images.
This is what Dockerfiles are for.

The Dockerfile lists the instruction on how to build your image and what to run on the container.

As you want to obtain the same image in every circumstances, you will have to avoid any operation that doesn’t result in a controlled and guaranted state. For instance, you should not do any apt-get upgrade in a Dockerfile, as you don’t controlled the result : indeed, depending the date your launching it, the upgrade could be different. Moreover, for technical reasons, there is a high chance that the upgrade fails. If you want to upgrade your distribution, you should update the base image your Dockerfile rely on.

I kept this Dockerfile minimalistic on purpose, but you will want to define the maintainer of the Dockerfile with the tag MAINTAINER, define the default process to launch with CMD and a lot of other useful instructions.
For now I will stick to this version.

I would like to focus your attention on a point: each RUN instruction will commit the current layer and create a new one upon it. The state of the memory is forgotten between two RUN instructions. Only the filesystem is kept.

To build an image, you put these instructions in a file called Dockerfile, then execute the build command from the same folder:

sudo docker build .

It will build the container and return its id. If you want to name the container when building, you can do it with the -t option

sudo docker build -t ubuntu-memcached .

You have seen a very quick introduction to Dockerfile. I will soon write an article about how to write a proper Docker image, following good practices, so that you can share them with your friends and the community!