15 Nov 2011 fxn   » (Master)

A Virtual Machine-based Development Environment

As many independent software developers, I normally have several projects going on: clients, side-projects, open source projects... Over the last years I have refined a development environment optimized for that use case that has worked really well for me. Let me share it in this post.

Isolated Environments

In a normal day I may work for a client that has a Rails 2.3.5 application with Solr, memcached, some custom vhosts, PostgreSQL 8.4, and a CAS running on Tomcat. Later at night I may work on Ruby on Rails and be able to, e.g., run the Active Record test suite with Ruby 1.9.3 and PostgreSQL 9.1.

If you multiply that by some number of projects, it becomes clear that a single machine can't possibly handle such a variety of environments in any predictable way. Well, you can predict chaos perhaps :).

And not only for today. If client C needs something three months after our last collaboration, I want to be able to launch his exact environment right away. No matter whether in the meantime I installed/uninstalled a gazillion things, upgraded the operating system, or got a new machine. I want to be able to launch the environment of client C anytime.

In summary, you need a robust setup that provides isolation to every project you work on. Virtual machines are a solution to that problem that allow you to have one single development (real) computer.

My development environment is totally based on virtual machines.

Software Choices

My laptop is a 13'' MacBook Pro from mid-2009, 4 GB of RAM, running Lion nowadays.

I have VMware Fusion and all the virtual machines run Linux. Since as a user I love Mac OS X I do most of my work in the host (I'll tell you how in a minute). In the virtual machine I basically need just a console, a light desktop is hence enough. My distro of choice nowadays is Lubuntu running the open-vm-tools Ubuntu package. That package provides desktop resize on window resize, copy & paste between the guest and the host, etc.

As I said, I do not work inside the virtual machine. I launch the virtual machine and there I have the complete runtime I need. Web servers and test suites run in the virtual machine, but editing, browsing, etc. happens in the Mac. To accomplish that I have a couple of tricks.

First, sharing the file system. VMware allows you to mount your home in the guest via what they call Shared Folders. But the virtual machines should be round and complete, so the code of my clients should be there, not in the host. In addition to that, Shared Folders do not work well with git, I think because of the hard linking going on. I rather go the other way around: I mount the guest file system in the host via SSHFS. If you use MacPorts just run sudo port install sshfs and you're golden. Looks like Homebrew also has a sshfs package. It surely works, but I don't use Homebrew.

Now, SSHFS mounts the file system through (local) SSH, not as fast as the hard drive. But I only need the files for editing and I don't really care if saving a file takes 5 ms. So no big deal. The only detail you probably want to tweak is disabling automatic project tree sync on focus in your editor/IDE if it has the concept of a project tree and tree sync on focus.

Second, I have a personal rule: One project at a time. If I am working for client C, I am totally focused on C's project. That's all I want to have up. Given that rule, I can implement a couple of convenient simplifications: All virtual machines have the same IP, and all virtual machines have the same mount point. Let me explain that in a dedicated section.

One Single IP, One Single Mount Point

VMware has several network modes. My virtual machines are configured to run under the default NAT mode, in which VMware does DHCP for the guest. That by itself is a little bit cumbersome because you get different IPs in different sessions, and sometimes you even get the IP changed while the machine is running. I've found that fixed addresses work better for my needs, up to a point where all the virtual machines have the same one.

The configuration for DHCP as of this writing lives in the file /Library/Preferences/VMware Fusion/vmnet8/dhcpd.conf, and to assign a fixed IP to a virtual machine you just need to know its MAC address. The MAC address can be manually set in the virtual machine settings, but I just grab whatever is printed by ifconfig eth0 after a standard installation.

Then, open the config file mentioned earlier and add towards the bottom something like this:


host rails {
hardware ethernet 00:0c:29:0a:98:b8;
fixed-address 172.16.132.127;
}

That's the DHCP configuration for the virtual machine where I have the development environment for Ruby on Rails. The host name "rails" is an arbitrary string. It has MAC address 00:0c:29:0a:98:b8 and a fixed IP of 172.16.132.127. I have a host configuration like that one per virtual machine.

To choose the IP have a look at the subnet block generated by VMware towards the middle of the file:


subnet 172.16.132.0 netmask 255.255.255.0 {
range 172.16.132.128 172.16.132.254;
...
}

According to the first line you have to choose an IP within 172.16.132.* that it is outside the specified range. My choice is 172.16.132.127.

Since we have the same IP for any virtual machine, we can create an entry in /etc/hosts that gives us a single hostname to rule them all:


172.16.132.127 vm

Mounting the file system is easily scriptable:

sshfs -o StrictHostKeyChecking=no -o reconnect -o workaround=rename fxn@vm:. $HOME/vm

Unmounting is also easily scriptable:

diskutil umount $HOME/vm 2>/dev/null

Spurious Files

Some programs in the host may create metadata files for the Mac like .DS_Store and friends. I don't like such files in the virtual machines. For those few programs that do that there's normally a configuration option or somesuch to disable it. There's a shell one-liner for TextMate for example, but I no longer remember it because it's been a while since I used TextMate, but you can Google for it. AFAICT, Emacs, Vim, Sublime Text 2, and RubyMine leave no spurious files out of the box.

As a last resort, if I get any of these spurious files for whatever reason I just run unmac. That's a little utility of mine implemented as a Ruby gem that cleans a given directory. To install it gem install unmac, possibly with admin privs. In my experience I rarely need to run unmac though.

Backups

You do not want Time Machine to do incremental backups of your virtual machines because they are big files on disk that change continually. For backups I use Carbon Copy Cloner.

That's another big win. Backups are trivial, no matter whether your hard disk breaks, or you get a new computer, you are ready to work in no time and with the guarantee that all those complicated environments are consistent and safe. The peace of mind that gives is invaluable.

Latest blog entries     Older blog entries

New Advogato Features

New HTML Parser: The long-awaited libxml2 based HTML parser code is live. It needs further work but already handles most markup better than the original parser.

Keep up with the latest Advogato features by reading the Advogato status blog.

If you're a C programmer with some spare time, take a look at the mod_virgule project page and help us with one of the tasks on the ToDo list!