in DevOps

Playtime with Docker.io

Today we are gonna dip our toes into Docker. To everyone who never heard of Docker, I recommend reading the introduction at their website.

Everytime I start fiddling around with new software and the possibility of breaking my computer I learned to love using virtual machines. You should do the same 😉

First, we need a new virtual machine – I prefer Vagrant:

vagrant init ubuntu/trusty64
vagrant up
vagrant ssh

The parameter ubuntu/trusty64 is important, because Docker needs an up-to-date system. trusty64 is shorthand for Ubuntu 14.04 (Trusty Tahr). Installation instructions for your favourite system can be found here.

We install Docker using the built in package manager.

sudo su # i'm no fan of typing sudo
apt-get update
apt-get install docker.io
ln -sf /usr/bin/docker.io /usr/local/bin/docker

Be careful to type apt-get install docker.io and not just docker.
To use the Docker command we symlink docker.io to /usr/local/bin/docker. Believe me, it simplifies copy/paste from samples around the web 😉

Let the fun begin

Without any further explanation type this command:

docker run centos /bin/echo hello world

The output lists some downloads:

Unable to find image centos locally
Pulling repository centos
0b443ba03958: Download complete 
539c0211cd76: Download complete 
511136ea3c5a: Download complete 
7064731afe90: Download complete 
hello world

A nice little hello world at the end tells us it worked. But what exactly has worked?
Docker downloaded the CentOS-image, started a container from it and ran /bin/echo hello world in it. That’s it. With one little command.
If you are interested in the deeper mechanics of what happened, there is another blog post for that.
Run this command again – now it immediately returns hello world. Docker stored the downloaded image and could immediately start a container. Take a closer look at the created containers

docker ps -a
CONTAINER ID   IMAGE            COMMAND                STATUS 
0a299d24fcb1   centos:centos6   /bin/echo hello world   Exit 0
31d50a766f87   centos:centos6   /bin/echo hello world   Exit 0 

We ran an echo command which returned immediately, so we need to use the -a flag (shows all containers, including closed ones) to display our containers. Looking at the STATUS column, we see our containers Exiting with status code 0, which is good. Now what is a container?
A Docker image is a read only blueprint to create containers. Containers run your application.
Let’s clarify this with an example.

docker run centos touch /test
docker run centos ls /

What happened here is important to understand. The second call listed no file named test in the root directory. That is because Docker created two different containers from the CentOS image.
But we can create a new image with the first container as base and run the second command on it. Every started container gets an id. This is what you see in the CONTAINER ID column.

docker ps -a | grep 'touch /test' | awk '{print $1;}'

We are lazy and just interested in the first column of the line containing the string touch /test.

docker commit 20ed75725e46 centos:centos6_test

The commit command takes the container id as first argument. With the second argument you can name your new image. Looking at the output of docker images it has this format: REPOSITORY:TAG.
Now take a look at the content of our first image:

docker run centos:centos6_test ls /

Bingo! It contains our previously created file named test.
It gets even more exciting. The Docker run command gives us the possibility to start interactively and with a terminal attached.

docker run -i -t centos:centos6_test bash

This opens a terminal connection to the container and we can work like in a “real” system.

Get our application inside

We create a simple web server presenting our homepage.
Inside our newly created terminal session, we install an apache web server and create our index.html.

yum -y install httpd
echo "This is our new homepage powered by Docker" > /var/www/html/index.html
exit

After exiting the terminal session, the container gets closed and we can now create an image named webserver from it.

docker commit __CONTAINER_ID__ webserver

To expose the ports used by apache we have to do some port mapping to our host system. We want to use our favourite browser to see the whole beauty of our new homepage 🙂
If you are using vagrant, like you really should, exit your vagrant machine and add the following line to your Vagrantfile:

config.vm.network "forwarded_port", guest: 80, host: 8080

It simply exposes port 80 of the virtual machine to port 8080 on the host machine.
To apply our new Vagrantfile we have to reload the virtual machine. After the successful reload enter the vm and become root again.

vagrant reload
vagrant ssh
sudo su

A quick recap:
We created a Docker image with a webserver.
We exposed port 80 of our virtual machine to our host system.
What we need to do now is to start a new container from our webserver image and expose the port of the apache running inside this container to port 80 on our virtual machine.

docker run -i -t -p 80:80 webserver /bin/bash
service httpd start

Do not start the container with the service https start as argument, cause the service command returns immediately and the container therefore closes.
When we visit http://localhost:8080 with our browser of choice, we are greeted with a wonderful “This is our new homepage powered by Docker”. Isn’t that nice?
Let’s close the terminal session – and with it our container.

exit

The web server is gone. This is another important concept of Docker. A Docker container is run in single process and should run a single process in the foreground. No services or daemons. Therefore we have to start the apache server in foreground

docker run -p 80:80 webserver httpd -DFOREGROUND

The command immediately returned, but after reloading our browser pointing to http://localhost:8080 we are greeted with our homepage.
A quick look at the output at the list of the running containers shows as our first running container.

docker ps
CONTAINER ID    COMMAND              CREATED          STATUS        PORTS
eb014569ef6f    httpd -DFOREGROUND   3 minutes ago    Up 3 minutes  0.0.0.0:80->80/tcp  

It even shows the mapped ports and how long this container has been running. Docker provides us with a basic set of monitoring commands like top to see the running processes.

docker top eb014569ef6f

A full list can be found at the Docker homepage.
To close the running container we can run the docker stop command.

docker stop eb014569ef6f

Dockerfiles

Commits are one – but not the preferred – way of creating Docker images. The standard way is a text file called Dockerfile describing the creation steps. For a detailed tutorial I really recommend the official [Dockerfile tutorial] (https://www.docker.io/learn/dockerfile/).

This is the Dockerfile to create the above image:

# My first webserver with Docker
#
# VERSION       1.0

FROM centos
MAINTAINER Markus Klepp, me@mklepp.com
RUN yum -y install httpd
RUN echo "This is our new homepage powered by Docker" > /var/www/html/index.html

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

EXPOSE 80

We build the the image named webserver with the following command run in the directory containing the Dockerfile:

docker build -t webserver .

To run this image we just type:

docker run -p 80:80 webserver

A visit to http://localhost:8080 with our host machines browser loads our website. This is much easier and more maintainable than our previous approach.