A server with Docker, part 3: The foundations

The third article in a series about the process of creating a flexible, multi-purpose webserver with Docker on a Digital Ocean droplet. This post will deal with installing and configuring Nginx, Docker and a few more necessary packages that will build the foudation for future work.

Now that a well behaving droplet is up and running on Digital Ocean, it is about time to finally get going with more exciting services. This post will deal with installing and configuring Nginx, Docker and a few more necessary packages that will build the foudation for future work. In the end, we will be able to get a “Hello World”-ish page from the new server and to run Docker containers without worries.

The Current State

Before we add new things, let’s take a look at what is running currently. In my case, the pstree command yields the following overview.

$ pstree

The diagram represents a tree of processes. Numbers with stars mean that there are multiple instances of a process. The init process is parent of all other running ones, marvelous. Not much is going on currently, we can see cron lurking in the shadows just as other common system services. In the sshd line, the pstree process is displaying itself, 6 getty that probably could be reduced in numbers, are waiting in vain. One of the best named daemons of all time, the whoopsie - Ubuntu crash database submission daemon is worth a special mention. In good time we will make this tree grow and prosper.

Before we get started, let’s install htop to investigate the state of the server in more detail. It is a very useful tool when you want to navigate through running processes and explore.

$ apt-get install htop

Just as a side note: if you are using urxvt as a terminal emulator on your local machine, here is a little fix that is required for htop to work properly.

# on the server you need to create this directory
mkdir -p ~/.terminfo/r/
# from your local machine copy the following file. Replace HOSTNAME with the name of the server from .ssh/config:
scp /usr/share/terminfo/r/rxvt-unicode-256color HOSTNAME:.terminfo/r/

After investigating with htop, we should see that the machine is not under great strain by far. Close to no CPU load and about 50MB of memory are required at this point. Everything is quiet:

Htop in its ultimate glory.

Apart from htop, there are other very useful command line tools to get insight on remote machines. For a great overview and pretty pictures, check out this great post by Anders Aarvik and its follow up.

PPA Prelude

PPAs are a useful thing to have access to, if you want to use **more recent or customized versions of certain packages ** than the common distribution repositories have to offer. Often there are well kept and thought out PPAs that are able to save you a lot of hassle. If you want to learn more about them check out this help page.

The Digital Ocean Ubuntu image we use is already configured in many neat ways, among others it uses local mirrors for common package sources which makes installing most things blazing fast and other sane settings. With all this in place however, it is not quite ready for PPAs yet. This is fixable in one line:

$ apt-get install python-software-properties software-properties-common


Nginx will be used to serve static files, and to function as a reverse proxy later on, routing connections to ports of docker containers. I prefer to use Nginx from this PPA as described on the installation page.

$ add-apt-repository ppa:nginx/stable
$ apt-get update
$ apt-get install nginx

If you navigate to the IP of the server in a browser, you should see a page saying “Welcome to nginx!”. Hooray!

All configurations of Nginx take place in the /etc/nginx/ directory. There you can find the main nginx.conf file containing default Nginx settings and some examples. It is worth a look before you continue but you probably will not need to touch it anytime soon. The reason why we see a default page is contained in the sites-available folder. The default file there once again contains many examples and great comments.

Server entries are the main building block of this file, and can be interpreted as representing one single gateway to a website or a service. Here you can define how to redirect traffic to wherever and however you wish. To learn more about Nginx configurations, you can check out the links, as stated in the default configuration file, namely:

# You should look at the following URL's in order to grasp a solid understanding
# of Nginx configuration files in order to fully unleash the power of Nginx.
# http://wiki.nginx.org/Pitfalls
# http://wiki.nginx.org/QuickStart
# http://wiki.nginx.org/Configuration
# Generally, you will want to move this file somewhere, and start with a clean
# file but keep this around for reference. Or just disable in sites-enabled.
# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.

The configurations for single sites can be split up in files, for a file to be active it needs to be linked into the sites-enabled directory. For changes in the configurations to take effect, the Nginx service needs to be restarted.

# create a soft link
$ cd sites-enabled
$ ln -s ../sites-available/new_file .
# restart the service
$ service nginx restart

As soon as needed, we will go through the process of creating new server entries for various purposes.

Seal The Gates!

Before we continue with Docker and have the opportunity to expose various unsuspecting services to the evil outside world, we should take care of the firewall. UFW which stands for uncomplicated firewall is already installed. It serves as a sane proxy tool for configuring iptables and only needs to be enabled with a few adjustments.

For now, it should just block everything but HTTP and SSH traffic.

# this may be 22, or a custom number as stated in /etc/ssh/sshd_config.
# Be VERY sure this is correct!
$ ufw allow 5060
# for http
$ ufw allow 80

# here goes ...
$ ufw enable

The status command should output something like this

# ... what we wanted
$ ufw status
Status: active

To                         Action      From
--                         ------      ----
5060                       ALLOW       Anywhere
80                         ALLOW       Anywhere
5060                       ALLOW       Anywhere (v6)
80                         ALLOW       Anywhere (v6)

We can open more ports for a mail server or HTTPS when needed. For now we can feel at ease.


Finally, it is time for the core of this endeavour, the Docker service itself. Reading the official instruction says for Ubuntu is a good idea as usually, but for this setup it is sufficient to do the following:

$ apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9
$ echo deb http://get.docker.io/ubuntu docker main> /etc/apt/sources.list.d/docker.list

$ apt-get update
$ apt-get install lxc-docker

Now you should be able to look at all existing docker containers with

# You will see, there are none. As expected.
$ docker ps -a

We can run the first disposable container on the server! The ‘precise’ tag is just a fancy and makes sure it is the same Ubuntu 12.04 release as is running on the server itself. For more on the syntax of starting containers you might want to check the reference.

$ docker run --rm -i -t ubuntu:precise /bin/bash

This should land you in a bash prompt that says you are the root user at some random hex number host. For now let’s just exit (by typing exit or ctrl-d as with any terminal session) and take a look around at the containers after the first run.

$ docker ps -a

As you should see there are no containers, thanks to the –rm option. This is really handy if you don’t want to accumulate lots of stopped containers over time that need to be cleaned. The images, on which containers are based, that have been fetched or created by docker can be examined with

$ docker images

For now, Ubuntu as far as the eye can see.

The Aftermath

With these steps, the most important services are installed. Once again it is tempting to take a look at what has changed on the server. Upon examining htop, we can see that the memory requirements have doubled to about 100MB. The CPU is still mostly unimpressed.

The output of pstree has gotten a little larger but is still comprehensible

$ pstree

Most notably, there are 4 Nginx and 9 Docker processes that have been added and are waiting eagerly to do something. The other additional cron line is not related to our manipulations. It is the script /etc/cron.daily/apt, which happened to be activated since the last pstree check and is currently sleeping for a random time (up to 30 minutes). The purpose of the sleep is to reduce the risk of the apt mirrors being hit and brought down by a legion of update-seeking machines.

In conclusion: we have added quite a bit, but it is only a beginning and nothing useful is happening still. Dang. In the next post it will be finally time to take care of serving non-default static HTML content (like this blog) by fiddling with Nginx, and to setup some glorious Git hosting with Gitolite.

Get My Weekly Newsletter

Weekly-ish articles about Kubernetes, Docker, DevOps, automation and deployment.

Free goodies too: get a list of my top 12 tools for deployment & automation and more exclusive content I only share with subscribers.