Upgrade Docker to 1.9 with the least pain possible

Now that Docker 1.9 is out, it's time once more to upgrade your installation to the latest version, in order to enjoy the awesome new features released, like Networking, the Volumes API and the Kernel memory limit flag. You can find all the new features released here.

Docker Logo

How do you handle a Docker upgrade? Especially in cases like ours, where user data is bound to server and thus you cannot simply spin up a new machine, run containers there and then swap your front-facing load balancer to serve the new containers? That's an interesting question and below is our take on approaching it.

Things to consider

As everything in life, there are some prerequisites that should exist in order to be able to use this method.

  1. First of all, you should be able to handle containers - apart from volumes - like ephemeral processes that their state need not be persisted.
  2. Then, you should be able to easily start needed containers on demand afterwards, as they cannot be automatically reastarted by Docker.

Upgrading Docker with the least impact possible

If you're with me up to now, then you'll most probably love the next steps.

First off, if you have a storage backend like devicemapper, provision a new storage for the new Docker installation. We use LVM thinpool as a backend, so we simply attached a new block storage device and provisioned it. If you don't use any special storage backend, just add a new Docker graph directory using the -g /some/other/graph/directory flag.

Then, just download the new version's binary and make it executable, for example

wget https://get.docker.com/builds/Linux/x86_64/docker-1.9.0 \
         -O /usr/bin/docker19

for an Ubuntu x64 machine.

Now, you can start a new Docker in the same host, using this time the new Docker engine and the newly provisioned storage. If you use LVM thinpool like us, your command should be pretty similar to:

docker19 daemon --dns --dns \
                -s devicemapper --storage-opt dm.basesize=5G \
                --icc=false --iptables=true \
                --log-opt max-size=10m --log-opt max-file=10 \
                --storage-opt dm.thinpooldev=/dev/mapper/docker--19-data-tpool\
                -g /var/lib/docker19 \
                -p /var/run/docker19.pid
                -H unix:///var/run/docker19.sock

A nice idea here is to use something like tmux, to avoid messing with multiple SSH connections.

Be careful not to interrupt the current Docker installation

In order not to have any bad surprises, make sure you don't listen to the same UNIX socket, or replace the PID file created by the previous version of Docker. Definitely make use of -p /var/run/docker19.pid and -H unix:///var/run/docker19.sock flags. Otherwise funny things might happen.

Getting all the data in advance

This is the time to download all these Docker images you need, in order for your service to start immediately after the new Docker installation. Just export DOCKER_HOST=unix:///var/run/docker19.sock to make make your client talk to the new intallation and start pulling!

There is just one last step before moving forward to upgrading your Docker installation and this is to update your main installation's config file. For Ubuntu, you can find it at /etc/default/docker. Replace everything in the DOCKER_OPTS variable with the new data that you provided before, excluding the -p and -H flags.

Pressing the red button

Everything seems set now, just upgrade Docker and everything should work nicely. Simply run curl https://install.docker.com | sh and wait for the magic to happen.

Starting services again

Now that the new installation is running, run your services again. Make sure to unset DOCKER_HOST environmental variable in order to talk to the main Docker engine of your system.

Cleaning up

After deploying everything and making sure everything is running as expected, you need to clean everything up now.

  1. Remove the downloaded Docker binary rm $(which docker19)
  2. Remove the old graph directory of Docker, i.e. rm -rf /var/lib/docker
  3. Dettach any not-needed volumes