Over Christmas/New Years I had a fair amount of spare time for relaxation; so naturally this was spent tinkering around with various bits of software and kit I havent had time to play with during the past few months. One of the things I wanted to test and try in anger was Docker; a wrapper/software suite that wraps LXC into something a bit more usable. There is a video below that explains what Docker is and how it works:

One of the biggest benefits of Docker is with a bit of skill, it allows you to satisfy your tech curiosity without compromising your production environments / home environments, i.e. “Oh cock, I spent 2 hours playing around with X and getting it working and its actually not very good; now i have to go remove databases, apache entries, etc”. In Docker, you can simply just spin up a new ‘ubuntu container’ (instance; like a virtual machine essentially – if you think ‘abstract’), play around and develop for a few hours, then either save it, or crash and burn it – without any impact to your underlying server.

This is perfect for those of us who can spend 8+ hours testing out redis across 5 servers, or elasticsearch with packetbeat, etc yet dont want to spend 30 minutes making a new virtual machine (KVM ftw), or even worse running it on our stable kit.

Therefore the purpose of this blog is give you all the tools and commands to turn Docker into something useful that you can use on a daily basis. So, lets begin!

1. Getting started

At home I run Ubuntu 14.04 as my operating system of choice, therefore all instructions pertaining to the installation of Docker are for Ubuntu (Debian too, I imagine) – if you require RHEL/Centos then follow the guide here.

For detailed instructions (and per platform instructions), follow the Docker on Ubuntu installation guides here – but for brevitys sake, here are the steps you need to take in order to get docker ready to roll on  Ubuntu 14.04:

apt-get install apt-transport-https
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9
sudo sh -c "echo deb https://get.docker.com/ubuntu docker main\
> /etc/apt/sources.list.d/docker.list"
sudo apt-get update
sudo apt-get install lxc-docker

To test that everything worked, run:

docker ps -as

This should return the following:

root@server:/home/sam# docker ps -as
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES SIZE
root@server:/home/sam#

If you get:

root@server:/home/sam# docker ps -as
FATA[0000] Cannot connect to the Docker daemon. Is 'docker -d' running on this host?

Then the Docker service isnt running. On Ubuntu, run ‘service docker start’, and re run ‘docker ps -as’, and it should now work.

2. Using Docker

So now you’ve got Docker, how do you actually use it!?

Docker works using containers – which are essentially your ‘virtual machines’, if you are familiar with virtualization. You can view all your containers by running ‘docker ps -as’, as below:

root@server:/home/sam# docker ps -as
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES SIZE
283ac561a135 centos:centos7 "/bin/bash" 14 seconds ago Up 3 seconds CentosBox 5 B
root@server:/home/sam#

As you can see, I have a single container called ‘CentosBox’ running that is of the type ‘centos:centos7’. The container ID is 283ac561a135, which can be used if you dont give your container a human name (i.e. CentosBox!).

So how did i get a Centos container and how does it work? Docker works using something called ‘images’, which are essentially templates (like virtual appliances, essentially). You can create your own image, share it with others, and vice versa, use other peoples images on your box. To find an image you can run the command:

docker search <imagename>

See below for an example:

root@server:/home/sam# docker search debian
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
debian (Semi) Official Debian base image. 291 [OK]
google/debian 30 [OK]
hanswesterbeek/google-debian-oracle-jdk Oracle's JDK installed on top of Google's ... 4 [OK]
neurodebian NeuroDebian provides neuroscience research... 4 [OK]
shuron/debian-openjdk-7 Open JDK 7 64x on plain debian (Jessie) La... 2 [OK]
webhippie/cedarish-debian Heroku cedar-ish base images for Docker bu... 2 [OK]
jesselang/debian-vagrant Stock Debian Images made Vagrant-friendly ... 1 [OK]
maxexcloo/debian Docker base image built on Debian with Sup... 1 [OK]
tutum/debian Debian image with SSH access. For the root... 1 [OK]
mschuerig/debian-subsonic Subsonic 5.0 on Debian/wheezy. 1 [OK]
fike/debian-postgresql PostgreSQL 9.4 beta until 9.0 version runn... 1 [OK]
jprjr/debian-nginx 1 [OK]
icco/simple-debian A debian build with Ruby 2.1 installed and... 1 [OK]
calebj/debian 0 [OK]
eboraas/debian Debian base images, for all currently-avai... 0 [OK]
thedutchselection/debian 0 [OK]
yaronr/debian-wheezy Debian Wheezy, 85mb, with a few extras 0 [OK]
aostanin/debian 0 [OK]
razmo/debian Debian base 0 [OK]
takeshi81/debian-wheezy-php Debian wheezy based PHP repo. 0 [OK]
tianon/debian-devel 0 [OK]
etna/drone-debian 0 [OK]
essembeh/debian My own Debian Jessie image 0 [OK]
kalabox/debian Kalabox Debian Wheezy image for all the ot... 0 [OK]
reinblau/debian Debian with usefully default packages for ... 0 [OK]
root@server:/home/sam#

Here we can see a number of images that contain the word Debian; the one at the top has the most stars and is also a ‘semi’ official image (??), so lets use that one! To download it locally, use the ‘pull’ command as below:

root@server:/home/sam# docker pull debian
debian:latest: The image you are pulling has been verified
1aeada447715: Pull complete
479215127fa7: Pull complete
511136ea3c5a: Already exists
Status: Downloaded newer image for debian:latest
root@server:/home/sam#

Now lets make sure that it has been downloaded; run the ‘images’ command to view all the images stored locally on your Docker server:

root@server:/home/sam# docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
debian latest 479215127fa7 4 days ago 84.99 MB
root@server:/home/sam#

 

Now that the image has been downloaded, lets go ahead and deploy a container based on the image so we can get cracking and start to use Debian!

root@server:/home/sam# docker run -it --name debserver debian /bin/bash
root@dc4e5b4e5311:/#

Within seconds (i.e. <3 seconds), we now have a container running Debian ontop of our Ubuntu 14.04 server, cool hey! To prove this is a Debian box and not Ubuntu, you can go ahead and install lsb_release (apt-get update & apt-get install lsb-release), and run it:

root@dc4e5b4e5311:/# lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description: Debian GNU/Linux 7.7 (wheezy)
Release: 7.7
Codename: wheezy
root@dc4e5b4e5311:/#

Cool huh! Now, you can go ahead and tinker to your hearts content, set stuff up, install apache, etc and its all safely contained within Docker. To shutdown the container, simply exit the shell:

root@dc4e5b4e5311:/# exit
exit
root@server:/home/sam# docker ps -as
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES SIZE
dc4e5b4e5311 debian:latest "/bin/bash" 3 minutes ago Exited (0) 1 seconds ago debserver 39.1 MB
283ac561a135 centos:centos7 "/bin/bash" 12 minutes ago Up 11 minutes CentosBox 5 B
root@server:/home/sam#

You can then restart it using the command ‘docker start’, as below:

root@server:/home/sam# docker start debserver
debserver
root@server:/home/sam# docker ps -as
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES SIZE
dc4e5b4e5311 debian:latest "/bin/bash" 3 minutes ago Up 1 seconds debserver 39.1 MB
283ac561a135 centos:centos7 "/bin/bash" 12 minutes ago Up 12 minutes CentosBox 5 B
root@server:/home/sam#

And if you want to view the IP address without having to go into the container, run the command:

root@server:/home/sam# docker inspect debserver | grep IPAddress
"IPAddress": "172.17.0.8",
root@server:/home/sam#

If you do wish to connect to the console again, run the ‘attach’ command:

root@server:/home/sam# docker attach debserver
root@dc4e5b4e5311:/#

One of the coolest things about Docker, in my opinion, is the ability to save your containers locally for re-use in the future.

Say for example, I wanted to install my companies software / configure a series of packages and software on my Debian box and then save it locally so i can quickly deploy it in the future.

After installing apache2, mysql-server, and configuring so your super happy with your Debian container, you can use the ‘commit’ command to save the container locally:

root@server:/home/sam# docker commit debserver smnet
34c640602df03c496e67e64d51c557255f6206a521570bd9d22812ae0736124a
root@server:/home/sam# docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
smnet latest 34c640602df0 9 seconds ago 124.1 MB
debian latest 479215127fa7 4 days ago 84.99 MB
root@server:/home/sam#

Here we have saved debserver as an image, and called that image ‘smnet’. I can then go and delete that container safely, as below:

root@server:/home/sam# docker ps -as
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES SIZE
dc4e5b4e5311 debian:latest "/bin/bash" 9 minutes ago Exited (0) 4 minutes ago debserver 39.1 MB
283ac561a135 centos:centos7 "/bin/bash" 17 minutes ago Up 17 minutes CentosBox 5 B
root@server:/home/sam# docker stop debserver
debserver
root@server:/home/sam# docker rm debserver
debserver
root@server:/home/sam# docker ps -as
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES SIZE
283ac561a135 centos:centos7 "/bin/bash" 18 minutes ago Up 17 minutes CentosBox 5 B
root@server:/home/sam#

Then, in a minute, or years time, i can easily redeploy that Debian container that i installed lsb-release on, using the command:

root@server:/home/sam# docker run -it --name debserver2 smnet /bin/bash
root@614ede51e5f8:/# lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description: Debian GNU/Linux 7.7 (wheezy)
Release: 7.7
Codename: wheezy
root@614ede51e5f8:/#

Very cool! This is so powerful for a development and testing perspective as it allows you to RAPIDLY spin up a large number of platforms (Ubuntu, RHEL, you name it) and configure them as you like. You can then rapidly and safely tear them down, or save them locally to be redeployed back onto the same box, or sent to other servers to be deployed there – very awesome.

One final thing – removing images! Say you dont like smnet anymore, you can simply remove it using the command ‘rmi’ (ReMove Image):

root@server:/home/sam# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
smnet               latest              34c640602df0        30 minutes ago      124.1 MB
opsview46-trusty    latest              d761a27fa999        3 days ago          1.029 GB
debian              latest              479215127fa7        4 days ago          84.99 MB
root@server:/home/sam# docker rmi smnet
Untagged: smnet:latest
Deleted: 34c640602df03c496e67e64d51c557255f6206a521570bd9d22812ae0736124a
root@server:/home/sam#

And c’est tout, its gone!

3. Even cooler stuff

There are even cooler things that be done with Docker.

For example, you can expose container ports via the public IP of the underlying server. What does that mean? Well, say for example I have apache2 running on debserver, i can currently only access it via http://172.17.0.9. What Docker allows you to do is essentially NAT ports from the host server through to the container, so I can say:

‘If anyone hits http://192.168.0.2:8082, redirect the traffic to http://172.17.0.9’ i.e. if anyone goes to ubuntu:8082, they’ll get apache on the Debian container. To do this, simply run the command:

docker run -it --name debserver3 smnet -p 8082:80 /bin/bash

Then you can get your browser and go to http://192.168.0.2:8082 and voila, you have an apache box!

One of the other neat things you can do is quickly deploy and link containers, similar to Ubuntu Juju (see my write-up from 18 months ago here:  http://www.everybodyhertz.co.uk/ubuntu-juju/).

One example I’ve tested which works a treat is to deploy a mysql container, and deploy a wordpress container, and link them together using just 2 commands:

root@server:/home/sam# docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=mysqlroot -d mysql
09dc2f70808fb1c18ba3b553461886c1b131788583de29006eb37cd7dfe3fd09
root@server:/home/sam# docker run --name some-wordpress --link some-mysql:mysql -p 8084:80 -d wordpress
37f632a8f447e309da106934beaf7e0fca5efa21e765c85ae45f14389dec3f59
root@server:/home/sam# docker ps -as
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES SIZE
37f632a8f447 wordpress:latest "/entrypoint.sh apac 6 seconds ago Up 2 seconds 0.0.0.0:8084->80/tcp some-wordpress 0 B
09dc2f70808f mysql:latest "/entrypoint.sh mysq 13 seconds ago Up 11 seconds 3306/tcp some-mysql 943 B
614ede51e5f8 smnet:latest "/bin/bash" 17 minutes ago Exited (0) 12 minutes ago debserver2 219 B
283ac561a135 centos:centos7 "/bin/bash" 36 minutes ago Up 36 minutes CentosBox 5 B

Then, hit your host server IP address:8084 in your browser and voila:

4. Gotchas / Limitations

So far I have found a few limitations of Docker, namely that is ignorant of sysv/upstart and an ordered startup. The real world impact of this (mainly) is that your services dont start when you ‘docker start debserver’; i.e. if i installed apache2 on my debserver and used svc to tell it to run on start, it wouldnt be running when i started that container – as docker doesnt listen to svc.

This is a pain in the arse, but the way around it (at the moment at least) is to modify /etc/bash.bashrc to contain the startup commands you need to execute via bash, i.e.:

‘service apache2 start’

This should startup apache2 for you when the container starts, so you can simply get the IP and test apache2.