They are very much complementary.
I have been using a combination of VirtualBox, Vagrant and Docker for all my projects for several months and have strongly felt the following benefits.
In Vagrant you can completely do away with any Chef solo provisioning and all you need your vagrant file to do is prepare a machine that runs a single small shell script that installs docker. This means that my Vagrantfiles for every project are almost identical and very simple.
Here is a typical Vagrantfile
# -*- mode: ruby -*-
# vi: set ft=ruby :
VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.box = "mark2"
config.vm.box_url = "http://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box"
[3000, 5000, 2345, 15672, 5672, 15674, 27017, 28017, 9200, 9300, 11211, 55674, 61614, 55672, 5671, 61613].each do |p|
config.vm.network :forwarded_port, guest: p, host: p
end
config.vm.network :private_network, ip: "192.168.56.20"
config.vm.synced_folder ".", "/vagrant", :type => "nfs"
config.vm.provider :virtualbox do |vb|
vb.customize ["modifyvm", :id, "--memory", "2048"]
vb.customize ["modifyvm", :id, "--cpus", "2"]
end
# Bootstrap to Docker
config.vm.provision :shell, path: "script/vagrant/bootstrap", :privileged => true
# Build docker containers
config.vm.provision :shell, path: "script/vagrant/docker_build", :privileged => true
# Start containers
# config.vm.provision :shell, path: "script/vagrant/docker_start", :privileged => true
end
The Bootstrap file that installs docker looks like this
#!/usr/bin/env bash
echo 'vagrant ALL= (ALL:ALL) NOPASSWD: ALL' >> /etc/sudoers
apt-get update -y
apt-get install htop -y
apt-get install linux-image-extra-`uname -r` -y
apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9
echo deb http://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list
apt-get update -y
apt-get install lxc-docker -y
apt-get install curl -y
Now to get all the services I need running I have a docker_start script that looks somthing like this
#!/bin/bash
cd /vagrant
echo Starting required service containers
export HOST_NAME=192.168.56.20
# Start MongoDB
docker run --name=mongodb --detach=true --publish=27017:27017 --publish=28017:28017 dockerfile/mongodb
read -t5 -n1 -r -p "Waiting for mongodb to start..." key
# Start rabbitmq
docker run --name=rabbitmq --detach=true --publish=5671:5671 --publish=5672:5672 --publish=55672:55672 --publish=15672:15672 --publish=15674:15674 --publish=61613:61613 --env RABBITMQ_USER=guest --env RABBITMQ_PASS=guest rabbitmq
read -t5 -n1 -r -p "Waiting for rabbitmq to start..." key
# Start cache
docker run --name=memcached --detach=true --publish=11211:11211 ehazlett/memcached
read -t5 -n1 -r -p "Waiting for cache to start..." key
# Start elasticsearch
docker run --name=elasticsearch --detach=true --publish=9200:9200 --publish=9300:9300 dockerfile/elasticsearch
read -t5 -n1 -r -p "Waiting for elasticsearch to start..." key
echo "All services started"
In this example I am running MongoDB, Elastisearch, RabbitMQ and Memcached
A non-docker Chef solo configuration would be considerably more complicated.
A final big plus is gained when you are moving into production, translating the development environment over to an infrastructure of hosts that are all the same in that they just have enough config to run docker means very little work indeed.
If you interested I have a more detailed article on the development environment on my own web site at