in General

Using Vagrant and Docker to Automate Local Development Environments

A typical software project will have a team of developers, each running their own development environment. Then there will be several more boxes created for test, pre-production and production environments. Unless they’re standardized, there’s a risk these environments can fall out-of-sync. If an over eager ops engineer installs the latest update for MySql, code that runs fine locally can suddenly start failing on production.

The easiest way of ensuring the same software is on every environment is to use Virtual Machines. In my current project we use VirtualBox to run the VM. It is configured with Vagrant and Docker.

This gives the developer access to a long list of services, all exactly the same version which are run on our other environments. This includes:

  • RabbitMq
  • MariaDb
  • phpMyAdmin
  • StatsD
  • Graphana

…and many more. Although I develop on Windows, each of these services runs on the same Ubunto Linux distro we deploy to. Each of the service is available from the host computer using Port Forwarding.

To achieve this we have a simple vagrantfile checked into the root directory of our source code. The vagrantfile is simply a Ruby file, with information about the type of box to create, and how to configure it. Getting all the software up and running is as simple as opening a command prompt, navigating to a path within this directory and running:

vagrant up

So if a new developer joins the team, they can simply grab the latest source code and run “vagrant up”. This will spin up a new Virtual Machine, and upon completion each of the services is available on their typical ports. They’re ready to develop!

There are plenty of primers out there for using vagrant, so we’ll focus in on how Vagrant can use Docker to easily install and configure the software – a process called Provisioning. This is made possible using a vagrant plugin (vagrant-docker-compose) that let’s us provision using Docker Compose.

The plugin could be installed manually by running the following from a command prompt:

vagrant plugin install vagrant-docker-compose

but to ensure each developer has the plugin installed, this can also be automated in the vagrantfile so it’ll be installed automatically the first time we try to “vagrant up”:

unless Vagrant.has_plugin?("vagrant-docker-compose")
puts "Requires Vagrant plugin for docker-compose, installing..."
system("vagrant plugin install vagrant-docker-compose")
puts "docker-compose plugin installed, please run `vagrant up` again"
exit
end

Then within the vagrantfile, we will set the location of the docker-compose configuration file. In this case it’ll be set to “docker-compose.yml”. This file lives on the root directory alongside the vagrantfile:

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

config.vm.provision :docker
config.vm.provision :docker_compose,
yml: "/vagrant/docker-compose.yml",
run: "always"

The docker-compose.yml file contains the list of all the software to be provisioned on the VM, along with the ports to make it available on.

rabbitmq:
  image: rabbitmq:3.4.4-management
  container_name: rabbitmq
  ports:
    - "5672:5672"
    - "15672:15672"
  volumes:
    - /var/lib/rabbitmq:/var/lib/rabbitmq

Finally the vagrantfile forwards the ports from the host machine to the VM, to make the services available:

config.vm.network "forwarded_port", guest: 5672, host: 5672                  # Rabbit MQ
config.vm.network "forwarded_port", guest: 15672, host: 15672                # Rabbit MQ (Management Console)
config.vm.network "forwarded_port", guest: 3306, host: 3306                  # Maria DB
config.vm.network "forwarded_port", guest: 13306, host: 13306                # phpMyAdmin

Write a Comment

Comment