Vagrant everywhere (and on Mac OS Mountain Lion as well), or how I nearly went mad...

Mon, 09/17/2012 - 06:13

You know, last week I was trying to get my local environments out of my box, trying to find a way to keep everything clean, separate and consistent. I'm very used to use apache with different virtual hosts, I used it on Ubuntu, and now I use it on Mac too.

But it was always a pain when I have to face a project with my team, or when we have a new team member, to get them all ready to go. The 'guys, I cannot get Magento to work', 'it always shows me index.php within the url, why cannot I remove it?' and other similar phrases or questions are always around. And after more than a year here, I was kind of tired of having to set everybody's environments up every now and then.

That's where Vagrant started to make sense to me (I have to be fair, I've never heard of it before last week, so thanks to Aldo on this one). The whole idea of Vagrant is great, but lets make a list so you understand what I mean:

  • First, it uses VirtualBox to work, so you will have to download it 
  • Then, when you have VirtualBox ready to go, you have to download Vagrant
  • After you have installed Vagrant, you can just run it, and that is the easiest thing ever:

{syntaxhighlighter brush:bash;} $ vagrant box add base http:/ / $ vagrant init $ vagrant up {/syntaxhighlighter}

Here is an explanation of what is going on:

On the first line you are just downloading a box to use with Vagrant, in this case an Ubuntu Lucid32 (pretty old, you may want to create a different box or maybe a newer version, check out this if you are interested )

On the second line you are starting the Vagrant (will create a Vagrantfile on the directory where you are running the command, so be aware that from now on, that will be your project's directory).

The last line just starts the box. And that's all you need if you want a plain vanilla box. But that won't do in most cases for most of us.

A little bit more about the features:

  • It will let you provision your environment with Puppet or Chef (among others).
  • It will give you out of the box ssh access (for you guys who love the console)
  • It will mount the 'proyects folder' in the box as the root folder for Apache.
  • with the proper configuration, it will redirect some ports to hit the box directly from your browser.


Ready? let's go!

Ok, now, there is something we should understand before going any further. With Vagrant, we have the chance to have multiple different environments to work on, and we can do it by using the powerful configuration that comes within the Vagrant file.

So, for this tutorial, I used the recomended box Lucid32 that you can find in the Vagrant site. I created a new directory where to put my project files, then I run lines 1 and 2 from above. After that, I started thinking about the provisioning process... It is supposed to be an easy and clean way to have your environment ready. My choice was to use Puppet, as it was the one that my limited mind understood faster. Now, be aware, the configuration is in Ruby. Is a nice girl if you have dated her before, but for me, she was a bit though... Anyway, I created my 'manifest' directory like the guide said, put the configuration and just restarted the box with 'vagrant up'... but nothing worked... 

Darn I was frustrated... 

I will save you all the digging I had to do, so if you follow this tutorial, you will have the additional tools that you need to make Puppet to work on Mac OS, but there is still more... for the last version of the fruits OS, you will need the app PackageMaker, that is no longer included on Xcode Cry. So you will have to install the 'Command Line Tools' to find it there. If you don't do this, you wont be able to create the packages... Kind of sad...

Almost there...

Now, the last part, once you have everything ready, you can create your manifest file, this is mine, I want to share it with you:

{syntaxhighlighter brush:ruby;} Exec { path => [ "/bin/", "/sbin/" , "/usr/bin/", "/usr/sbin/" ] } class system-update{ exec { 'apt-get update': command => 'apt-get update', } } class php5{ package { "php5": ensure => present, require => Exec['apt-get update'], } } class mysql { package { ['mysql-common', 'mysql-client', 'mysql-server']: ensure => present } package { "php5-mysql" : ensure => installed, require => Package['php5'], } service { 'mysql': enable => true, ensure => running, require => Package['mysql-server'] } exec { 'set-root-password': subscribe => [Package['mysql-common'], Package['mysql-client'], Package['mysql-server']], refreshonly => true, unless => "mysqladmin -uroot -proot", command => "mysqladmin -uroot password root", #require => Package['mtop'] # mtop needs an empty root password } } class extras{ package { "openjdk-6-jre": ensure => installed, require => Exec['apt-get update'], } } # Basic Puppet Apache manifest class apache { package { "apache2": ensure => present, require => Package["php5-mysql"], } service { "apache2": ensure => running, require => Package["apache2"], } } include apache include system-update include php5 include mysql include extras {/syntaxhighlighter}


Then you have to update your Vagrantfile to load Puppet like this:

{syntaxhighlighter brush:ruby;}# -*- mode: ruby -*- # vi: set ft=ruby : do |config| # All Vagrant configuration is done here. The most common configuration # options are documented and commented below. For a complete reference, # please see the online documentation at # Every Vagrant virtual environment requires a box to build off of. = "lucid32" # Forward guest port 80 to host port 4567 config.vm.forward_port 80, 4567 # The url from where the '' box will be fetched if it # doesn't already exist on the user's system. # config.vm.box_url = "" # Boot with a GUI so you can see the screen. (Default is headless) # config.vm.boot_mode = :gui # Assign this VM to a host-only network IP, allowing you to access it # via the IP. Host-only networks can talk to the host machine as well as # any other machines on the same network, but cannot be accessed (through this # network interface) by any external networks. # :hostonly, "" # Assign this VM to a bridged network, allowing you to connect directly to a # network using the host's network device. This makes the VM appear as another # physical device on your network. # :bridged # Forward a port from the guest to the host, which allows for outside # computers to access the VM, whereas host only networking does not. # config.vm.forward_port 80, 8080 # Share an additional folder to the guest VM. The first argument is # an identifier, the second is the path on the guest to mount the # folder, and the third is the path on the host to the actual folder. # config.vm.share_folder "v-data", "/vagrant_data", "../data" # Enable provisioning with Puppet stand alone. Puppet manifests # are contained in a directory path relative to this Vagrantfile. # You will need to create the manifests directory and a manifest in # the file lucid32.pp in the manifests_path directory. # # An example Puppet manifest to provision the message of the day: # # # group { "puppet": # # ensure => "present", # # } # # # # File { owner => 0, group => 0, mode => 0644 } # # # # file { '/etc/motd': # # content => "Welcome to your Vagrant-built virtual machine! # # Managed by Puppet.\n" # # } # config.vm.provision :puppet do |puppet| puppet.manifests_path = "manifests" puppet.manifest_file = "default.pp" end # Enable provisioning with chef solo, specifying a cookbooks path, roles # path, and data_bags path (all relative to this Vagrantfile), and adding # some recipes and/or roles. # # config.vm.provision :chef_solo do |chef| # chef.cookbooks_path = "../my-recipes/cookbooks" # chef.roles_path = "../my-recipes/roles" # chef.data_bags_path = "../my-recipes/data_bags" # chef.add_recipe "mysql" # chef.add_role "web" # # # You may also specify custom JSON attributes: # chef.json = { :mysql_password => "foo" } # end # Enable and configure the chef solo provisioner # config.vm.provision :chef_solo do |chef| # We're going to download our cookbooks from the web # chef.recipe_url = "" # Tell chef what recipe to run. In this case, the `vagrant_main` recipe # does all the magic. # chef.add_recipe("vagrant_main") # end # Enable provisioning with chef server, specifying the chef server URL, # and the path to the validation key (relative to this Vagrantfile). # # The Opscode Platform uses HTTPS. Substitute your organization for # ORGNAME in the URL and validation key. # # If you have your own Chef Server, use the appropriate URL, which may be # HTTP instead of HTTPS depending on your configuration. Also change the # validation key to validation.pem. # # config.vm.provision :chef_client do |chef| # chef.chef_server_url = "" # chef.validation_key_path = "ORGNAME-validator.pem" # end # # If you're using the Opscode platform, your validator client is # ORGNAME-validator, replacing ORGNAME with your organization name. # # IF you have your own Chef Server, the default validation client name is # chef-validator, unless you changed the configuration. # # chef.validation_client_name = "ORGNAME-validator" end {/syntaxhighlighter}

Thats pretty much it, you now can restart your box with 'vagrant up' and everything should be working fine.

From this point you are on your own, you are free to modify this file as you need, and then you can pack everything and share it among your team. 

I hope this was helpful your you, happy coding!