Blog» Building a development environment from a production website with Vagrant and VirtualBox

Building a development environment from a production website with Vagrant and VirtualBox

By Philipp Kamps  | November 16, 2015  |  Business solutions, Case study

When it comes to local development environments, in a lot of cases Mugo uses VirtualBox images and manages them with Vagrant. These environments are sometimes created after the production environment has been set up. To make a local development environment as similar to production as possible, one approach is to actually copy the production server.

VirtualBox is a virtualization product that runs on all major operating systems. For example, on my Mac OS laptop I can create a virtual machine running Red Hat Enterprise Linux with all project-required services like PHP, MySQL, Solr, and so on.

Vagrant allows me to configure, download, start, and stop a VirtualBox image. It is also responsible for sharing the project source code between the virtual machine and the host operating system. For example, on my Mac OS laptop I use PhpStorm to work with the project source code. The source code is shared via NFS with the virtual machine, so that all of the code changes I make in PhpStorm directly affect the local development environment.

Copying from production to the local environment

There are many ways to create a local virtual machine setup. For example, you can use Ansible to configure the virtual machine as described in another blog post. Or, you can use only VirtualBox and build the virtual machine manually. Another possibility, as I will describe in this post, is to convert the raw contents of the production or staging hard drive into a VirtualBox image file. Basically, you build a copy of your production or testing environment containing all services, OS configurations, data, and source code.

Here is an example shell script I wrote for an eZ Publish project:

#!/bin/sh

echo 'Stop services'
/etc/init.d/mysqld stop
/etc/init.d/httpd stop
/etc/init.d/solr stop
/etc/init.d/crond stop
 
echo '"Defrag" disks'
cd /tmp; cat /dev/zero > zero.file; sync; rm zero.file; sync;
cd -
cd /media/data; cat /dev/zero > zero.file; sync; rm zero.file; sync;
cd -
 
echo 'Build data image'
rm tmp/data.img
dd if=/dev/xvdf of=tmp/data.img
echo 'Build root image'
rm tmp/root.img
dd if=/dev/xvda of=tmp/root.img
 
echo 'Start services'
/etc/init.d/mysqld start
/etc/init.d/httpd start
/etc/init.d/solr start
/etc/init.d/crond start
 
echo 'Fix bad sectors'
#just making sure loop2 is available
losetup -d /dev/loop2 &> /dev/null
losetup -o 1048576 /dev/loop2 tmp/root.img
e2fsck -p /dev/loop2
losetup -d /dev/loop2
losetup /dev/loop2 tmp/data.img
e2fsck -p /dev/loop2
losetup -d /dev/loop2
 
echo 'Convert data img'
rm build/box-disk2.vmdk
VBoxManage convertfromraw tmp/data.img build/box-disk2.vmdk --format VMDK --variant Stream
echo 'Convert root img'
rm build/box-disk1.vmdk
VBoxManage convertfromraw tmp/root.img build/box-disk1.vmdk --format VMDK --variant Stream
 
echo 'Set disk UUIDs'
VBoxManage internalcommands sethduuid build/box-disk1.vmdk '6a69323a-0a25-4540-93c9-b6834553a1c9'
VBoxManage internalcommands sethduuid build/box-disk2.vmdk '7eaad47b-7475-44b8-80c9-a28b49bf3e79'
 
echo 'Package to examplesite.box'
tar -czvf .examplesite.box_build -C build .
mv -f .examplesite.box_build examplesite.box
 
echo 'done'

The basic idea is to use the linux tool dd to capture the raw hard-drive content into a file. Then, the VBoxManage tool, which comes with VirtualBox, turns the raw image into a VirtualBox image.

As you can see in the script, this is quite involved, as it does the following:

  • Stop all services to reduce the activity on the hard drive; this avoids errors in the dd image. This takes the site down temporarily of course, so you usually have to do this from the staging or testing environment.
  • Fill the entire hard drive content from /dev/zero; this reduces the resulting image size
  • Check the image for errors and fix those, as dd sometimes leaves some lost inodes
  • Set a specific UUID for the hard drive, to ensure that it matches your VirtualBox configuration
  • Make sure you have an additional hard drive to create the virtual machine

Having an exact copy of the production or testing environment for local development is great, in order to reduce any surprises when it's time to deploy your work. However, there are some challenges. One challenge is if you have a scheduled script that should be run on the production environment only. That's why the configuration or system scripts sometimes need to know the environment they are in. Here is an example that checks for the presence of VirtualBox:

machineID='lspci | grep -o 'VirtualBox Graphics Adapter''
 
if [ "$machineID" == "VirtualBox Graphics Adapter" ]; then
    #I'm a virtual machine
fi

Editing site code

Usually, we use Vagrant to mount the source code from the host operating system to the virtual machine. You can also do the opposite: set up an NFS server on the virtual machine and tell Vagrant to mount the share to the host OS. This unusual setup has some pros and cons:

  • Pro: Performance is much better when the code base is located directly on the virtual machine.
  • Pro: It is more straightforward to have Windows mount an NFS location than to have a Windows NFS server.
  • Con: The source code is only available once the virtual machine is started. Sometimes you only want to implement simple code changes and it is a bit overkill to start the virtual machine for that. You should consider to check out another instance of the source code on your host OS for these types of code changes.
  • Con: Accessing the code base over NFS lowers performance for full text search or code synchronization with a remote code repository. This can be mitigated if you use an editor such as PhpStorm that creates its own search index.

Here is an example Vagrant configuration file to manage the virtual machine, including the mount from the virtual machine to the host OS:

# -*- mode: ruby -*-
# vi: set ft=ruby :
#

Vagrant.require_version ">= 1.3.0"
 
# Define the host OS
module OS
  def OS.windows?
    (/cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM) != nil
  end
 
  def OS.mac?
    (/darwin/ =~ RUBY_PLATFORM) != nil
  end
 
  def OS.unix?
    !OS.windows?
  end
 
  def OS.linux?
    OS.unix? and not OS.mac?
  end
end
 
unless Vagrant.has_plugin?("vagrant-hostmanager")
  raise "!*! Plugin required !*!\n\n\tvagrant plugin install vagrant-hostmanager\n"
end
 
unless Vagrant.has_plugin?("vagrant-triggers")
  raise "!*! Plugin required !*!\n\n\tvagrant plugin install vagrant-triggers\n"
end
 
Vagrant.configure("2") do |config|
  config.hostmanager.enabled = true
  config.hostmanager.manage_host = true
  config.hostmanager.ignore_private_ip = false
  config.hostmanager.include_offline = true
  config.vbguest.auto_update = false
  config.vm.define 'examplesite' do |node|
    node.vm.box = 'boxname'
    node.vm.box_url = "http://url.to/the/vagrant/box/file"
    node.vm.hostname = 'dev.project.com'
    node.vm.network "private_network", ip: "172.28.128.3"
    node.hostmanager.aliases = %w(alias.project.com)
  end
 
  config.ssh.username = "ec2-user"
  config.ssh.private_key_path = "id_ExampleKey.pem"
 
  config.vm.provider :virtualbox do |vb|
    vb.customize ["modifyvm", :id, "--natdnshostresolver1", "on", "--memory", 4096]
   #vb.gui = true
  end
 
  # only an example - does not support Windows
  config.trigger.after :up do
    run "sudo mount -o resvport dev.project.com:/var/www ./share"
  end
 
  config.trigger.before :halt do
    run "sudo umount -f ./share"
  end
end

Join our mailing list for news, events, tips, and tools

Related Blog Posts

VirtualBox virtual machines with Vagrant and Ansible for website development

Vagrant and Ansible are tools to efficiently provision virtual machines (also called VMs or simply boxes).

This blog post will begin with a short discussion...

Read more »

Using VirtualBox to install and test eZ Publish 5

Using virtual machines when developing eZ Publish websites have been useful in 2 main ways (if not more): working in a different server environment than...

Read more »

Comments

blog comments powered by Disqus

Hi, we're Mugo Web - Nice to meet you!

We're a group of web experts who solve complex web problems.

Learn more about us »

Search


Categories


Yes - we can do that.

Many years of experience with complex websites allows us to offer total solutions.

Learn more about what we can do »

We love our clients (and they love us too)

We've solved problems across North America and around the world.

Learn more about what we've done »

We tweet too

Follow us on Twitter for the latest Mugo happenings

mugo twitter page @mugo

© 2008 - 2018 Mugo Web. All rights reserved.