Contents
- The problem - Dev/Prod Parity, multiple development environments and troubles
- Enter Virtualization - How to solve the problem
- The solution - Vagrant and Puppet
- An example scenario - Complete example
- Sharing your VM - Distributing VMs between developers and deployment to the production environment
The problem
As a developer you may need to work on multiple customer projects over time.
This introduces the problem of having to set up a multitude of different development environments: You need different tools, libraries, servers, configurations - sometimes this can get really nasty for instance when it comes down to running a specific version of a library or tool, which conflicts with other tools running on your system.
Aside of that, there is the aspect of disc space: You can’t always throw away such a local development environment when the first version of the project is launched. What if the customer wants changes? What if there are bugs that have to be fixed? What if we need that environment to demonstrate or evaluate something with it?
Thus, these environments start to pile up and clutter your system.
Enter: Virtualization
Nowadays system virtualization is easy. You can choose between Vmware, Xen, KVM, VirtualBox, HyperVisor et. al. to find the easiest and most suitable solution for your virtualization needs. But still: These machines have to be kept somewhere - and you have to copy around large files, including the box-configuratons, the virtual disk images - or large ova-packages.
This is where Vagrant and Puppet jump in - and they’re free!
The solution
Imagine this: Someone creates a virtual machine with everything on board, the project needs. This way you can start testing by just starting the VM - and he can package this in a few small and structured config files and send these around in seconds. Some smart tools then recreate the VM to be just the same as the original. Additionally you can tear down VMs with a single command and build them up to be completely the same with another command.
That sounds cool, right? No more sending around large VM-images
An example scenario: LAMP stack with a web project
Imagine you have a git repository with a project leveraging PHP and MySQL in your company and you are now asked to fix a few issues on that project.
The Vagrant box for this should include everything that is necessary to run PHPUnit and the LAMP-Stack, hosting the web project.
After having installed Vagrant you need to add the first basebox like that:
vagrant box add base http://files.vagrantup.com/lucid32.box
Baseboxes are the basis for creating new Boxes. The box mentioned above will set up a Ubuntu Lucid 32-bit VM to start with. You can find different base boxes at vagrantbox.es
Let’s create our VM!
Vagrant
You start by creating an new directory and initialize Vagrant. We will make the two directories called “manifests” and “configs” to house config files and puppet manifests:
mkdir lamp-project && mkdir lamp-project/configs && mkdir lamp-project/manifests
cd lamp-project
vagrant init
That was simple. Okay - now we want to setup some portforwarding to access the website from the host plus we make the first step towards Puppet. Put the following to the Vagrantfile:
config.vm.box = “lucid32”
config.vm.provision :puppet do |puppet|
puppet.manifests_path = File.expand_path(“../manifests”, __FILE__)
end
config.vm.forward_port 80, 8080
config.vm.share_folder “configs”, “/configs”, File.expand_path(“../configs”, __FILE__)
This way, the port 80 from the VM is forwarded to Port 8080 on the host.
On the last line (config.vm.share_folder) we make our configs/ folder be mounted as /configs in the guest VM.
The “config.vm.provision” line sets up Puppet for provisioning.
Nice and fine - but how do we use Puppet and for what?
Puppet
Puppet is a provisioning system, that allows you to automatically setup and configure a system to fit your needs. To tell Puppet how to set up the system it uses Manifests.
Lets create our first basic manifest by creating a manifests/default.pp with the following contents:
$config_path = “/configs”
$vagrant_base_path = “/vagrant”
Exec { path => “/bin:/usr/bin:/usr/local/bin” }
group { “puppet”: ensure => present }
exec { “apt-get update”: command => “apt-get update” }
class apache {
file { “/etc/apache2/sites-enabled/000-default”:
ensure => file,
source => “${config_path}/000-default”,
before => Service[“apache2”],
}
exec { “enable-mod_rewrite”:
require => Package[“apache2”],
before => Service[“apache2”],
command => “/usr/sbin/a2enmod rewrite”
}
package { “apache2”:
ensure => present,
before => File[“/etc/apache2/sites-enabled/000-default”],
}
service { “apache2”:
ensure => running,
require => Package[“apache2”]
}
}
class php {
package { “libapache2-mod-php5”: ensure => present }
package { “php5”: ensure => present }
package { “php5-cli”: ensure => present }
package { “php5-dev”: ensure => present }
package { “php5-mysql”: ensure => present }
package { “php-pear”: ensure => present }
exec { “pear upgrade”:
command => “/usr/bin/pear upgrade”,
require => Package[“php-pear”],
}
}
class mysql {
package { “mysql-server”:
require => Exec[“apt-get update”],
ensure => present,
}
service { “mysql”:
enable => true,
ensure => running,
require => Package[“mysql-server”],
}
exec { “Set MySQL server root password”:
require => Package[“mysql-server”],
unless => “/usr/bin/mysqladmin -uroot -proot status”,
command => “/usr/bin/mysqladmin -uroot password root”,
}
}
include apache
include php
include mysql
This manifest is used by Vagrant to run puppet on the first start of the VM.
To learn more about Puppet, see the learning guide.
Enjoy the ride
Now it is time to start our VM. Run the command
vagrant up
and watch it happen. When it is finished, visit http://localhost:8080 and enjoy :)
When you need to do something on the VM, you can do “vagrant ssh” to connect to the VM and when you’re finished testing your work, you can do “vagrant halt” to stop it.
One of the big benefits: Whenever you decide to leave the project for a longer period of time, you can do “vagrant destroy” to kill the VM and whenever you need to get back to it, you just run “vagrant up” again and you’re ready to go.
Sharing your VM
The last step of this journey is about sharing your work. If you work in a team, it would be great to be abled to push the VM around without having to handle large binary files.
The great news: Sharing your VM is dead-simple: Share the Vagrantfile along with manifests and referenced config files you use and you’re done!
You could also make your VM a base box to be used by others to start from a common ground.
Let’s pretend you’re working in a web development agency and you have some projects requiring Ruby on Rails with RSpec, Sass, Capistrano and some other nifty gems, together with Passenger and Apache. On the other hand you still have to maintain PHP-Projects with a classical LAMP-Stack - these configurations are the same for all the projects - why not create two base boxes for that?
To do this, you just create the two VMs as usual and then export them.
First, you copy your Vagrantfile to Vagrantfile.pkg, then run
vagrant package —vagrantfile Vagrantfile.pkg —include configs,manifests
If you have looked at the tutorial on vagrantup.com you may have noticed the little differences between my Vagrantfile and the Vagrantfile in their example. I made them so that the configs and manifests can be included and properly used and referenced in the packaged box. This way, you do not have to ship around multiple files. Just the box.
I would be glad to hear your thoughts on this topic.