Vagrant and load balancing

Have you ever needed your local dev environment to support load balancing? I have. And luckily for me it was much easier to create than I expected.

Long story short, I needed to check if web app I was working on can work on multiple web nodes. For this particular project I was using Vagrant as my server. My goal was to introduce load balancing into dev stack.

It turned out that it's easier than I expected. There's a whole section in Vagrant docs called "Multi-Machine". After few experiments I was ready to test load balancing.

To build my load balancing env I needed to:

  1. Define web and db servers.
  2. Create separate Ansible roles for load balancer, web and db servers (this step is optional. I use Ansible to provision my machines, you can use whatever you want).
  3. Provision VMs.
  4. Provision load balancer.

Main difference between default Vagrantfile and my configuration was a simple loop I used to iterate over list of web servers and additional configuration for load balancer. This is how my Vagrantfile looks like.

# list of servers I need to create
hosts = {
  'web1' => '192.168.56.111',
  'web2' => '192.168.56.112',
  'web3' => '192.168.56.113',
  'db1' => '192.168.56.121'
}

Vagrant.configure(2) do |config|

  config.vm.box = "bento/centos-7.4"

  config.ssh.forward_x11 = true
  config.ssh.insert_key = false

  total_hosts = hosts.length

  # iterate over list of servers
  hosts.each_with_index do |(name, ip), index|

    config.vm.define name do |machine|
      machine.vm.hostname = "#{name}.local"
      machine.vm.network :private_network, ip: ip

      # this could be done better, db servers don't need this
      machine.vm.synced_folder "/host/path/", "/vm/path/", mount_options: ["dmode=777,fmode=777"]

      # Ansible will be responsible for provisioning web and db servers. that's why I use one list for web and db servers
      machine.vm.provider "virtualbox" do |v|
        # VM configuration
      end

      # with Ansible we don't need to provision each server separately. we can run it once for all machines 
      if index == total_hosts - 1
        machine.vm.provision "ansible" do |ansible|
          # Ansible configuration
        end
      end
    end
  end

  # load balancing
  config.vm.define "load_balancer" do |lb|
    lb.vm.hostname = "web-site.local"
    lb.vm.network :private_network, ip: "192.168.56.101"
    lb.vm.provider "virtualbox" do |v|
      # VM configuration
    end

    lb.vm.provision provisioner do |ansible|
      # Ansible configuration
    end
  end

end

For load balancing I have used nginx. Here's the configuration.

upstream webnodes {
    server 192.168.56.111; # web1
    server 192.168.56.112; # web2
    server 192.168.56.113; # web3
}

server {
    listen 80;
    server_name web-site.local;

    location / {
        proxy_pass http://webnodes;
    }
}

And that's all. With this few simple steps I was able to load balance Vagrant machines.

Here are some useful links I used while I was building my configuration: