Building containerized apps with vagrant

Published 1/27/2015 6:34:00 AM
Filed under Docker

Building apps that use docker as a means to deploy different services for the
application is somewhat weird. You have to type in a lot of docker commands
to get everything up and running. That is not all, on top of having to
execute a lot of commands you also have to deal with a the rather long commands.
There's quite a few things you can configure and you have to specify all that
on the commandline.

In this post I will show you a tip that will save you a lot of time when you
work a lot with docker containers.

Use vagrant instead of running on your host

Believe me, running docker on your machine will cause headaches. Docker leaves
intermediate images behind and sooner or later you will run into errors because
you forgot to delete that old container.

If you haven't done so already, install Vagrant.
You will quickly learn to love this tool.

After you have done that, come back here and follow the rest of this guide
to get going quickly with docker on vagrant.

Using docker images on top of Vagrant

The easiest method of using docker in combination with Vagrant is to create
a new Vagrantfile in your project directory with the following contents:

Vagrant.configure(2) do |config|
  config.vm.box = "ubuntu/trusty64"

  config.vm.provider "virtualbox" do |v|
    v.customize ["modifyvm", :id, "--memory", "2048"]
    v.customize ["modifyvm", :id, "--cpus", "2"]
  end

  config.vm.provision "docker" do |docker|
    # Configure docker images on your machine
  end
end

This vagrantfile uses the 14.04 version of ubuntu as a starting point.
On top of that base box, it will automatically install the latest version of
docker.

I've changed the settings for the box a bit, so that you can get a little bit
more power out of it. Tweak these settings when you only have one CPU or
rather want to use more CPU and memory.

To pull in images from the central docker repository, you change the
config.vm.provision "docker" call to include pull_images statements.
For example, the following piece of code pulls in the consul and registrator
images.

config.vm.provision "docker" do |docker|
  docker.pull_images "progrium/consul"
  docker.pull_images "progrium/registrator"
end

To run these images add the following extra code to the provisioning block:

config.vm.provision "docker" do |docker|
  docker.run "progrium/consul",
    args: "-p 8500:8500",
    cmd: "-server -bootstrap"
end

The command starts with run, the first argument is the name of the image.
Additionally you can provide extra arguments for the docker run command through
the args parameter. The cmd parameter allows you to add a command after the image
portion of the docker run command. So the docker run statement above translates
into docker run -p 8500:8500 progrium/consul -server -bootstrap

Building docker containers

Running existing images as containers in your Vagrant box is okay, but wouldn't
it be nice to be able to build images from your sources and run that as a container
in the vagrant box?

You can build a new image through the following piece of code:

config.vm.provision "docker" do |docker|
  docker.build_image "/vagrant/my_app", args: "--tag=my_app/container:latest"

This code issues a docker build command in the directory /vagrant/my_app, which is
the my_app subfolder of the folder where you have your Vagrantfile stored.
The args parameter can be used to pass in things like the tag for your docker image.

Make sure you run this command before the run command, so you have images ready
to run inside the vagrant box.

Final tips

The docker provision of Vagrant makes it easy to build and run docker containers
on any developer machine, without having to setup docker. Especially developers
using Mac OS X will find the Vagrant setup a lot easier to workwith then boot2docker.

You can make things really easy by making sure you map the ports exposed by your
docker containers to ports on your host. This enables you to access the application
services in the docker containers from your host by going to http://localhost:\<port\>/
instead of messing around with SSH etc.

Need to reload the docker config? Use vagrant destroy -f && vagrant up to completely
wipe your setup or just use vagrant provision to do an incremental upgrade of your box
with the latest docker images.

Enjoy!