Posted by & filed under Uncategorized.

Vagrant and Puppet

The ability to easily start new virtual machines has brought us multiple challenges, some of which are :

  • The ability to maintain our infrastructure over time, ensuring we have the right configuration in the right place.
  • The ability to version and record the history of our configuration.
  • The ability to re-use pieces of automated configuration in order to minimize work.

This blog post will introduce Vagrant, a tool which makes it dead-simple to start and stop virtual environments which run on your local workstation, and Puppet, a tool which allows us to express our configuration as code. By using Vagrant and Puppet together, we can easily and iteratively create complex configurations, and re-use our efforts across local, remote virtual, and cloud environments.

Vagrant

Vagrant is a command line program and a tiny DSL for creating virtual machines hosted on a local workstation. Vagrant depends on a provider, meaning a platform or hypervisor to host the virtual machines. The default Vagrant provider is Oracle VM VirtualBox. As of this writing, both Vagrant and VirtualBox are free and open-source to one degree or another.

Vagrant works with the concept of “base boxes”, which are virtual machine templates which Vagrant then clones and customizes as specified using the Vagrant DSL. As described on the Vagrant website, getting started with Vagrant requires only three commands.

$ vagrant box add base http://files.vagrantup.com/lucid32.box
$ vagrant init
$ vagrant up

The commands up add a template called base to the local vagrant repository from an http:// URI, initialize a default configuration file, and start the machine specified in the initial configuration file. Your base box and starting configuration files would likely vary, but the above is a good way to get started. There are lots of Vagrant boxes available from a variety of sources. Eventually you will create your own to fit your own needs.

Configuration Management

We will now use Vagrant to create an initial virtual machine and use Puppet to provision this machine. Our high-level requirement is to enable multicast DNS, so that our host laptop can reach the virtual machine without having to explicitly set entries in /etc/hosts or manage a DNS resolver.

Add Vagrant base box

First, add a Vagrant base box. In the example below, we’re going to use a template available from Vagrantbox.es which includes an appropriate version of Puppet for our needs.

vagrant box add centos64 http://developer.nrel.gov/downloads/vagrant-boxes/CentOS-6.4-x86_64-v20130731.box

The base box is now available to Vagrant under the name centos64.

Vagrantfile

By default, a Vagrant virtual machine will start with a single network interface, eth_0. This is a NAT interface, meaning all network operations performed by the virtual machine will be done through a virtual Network Address Translation interface provided by the host. Vagrant will also set port-forwarding options for VirtualBox so that the virtual machine’s internal port 22 is available on the host as port 2222 or above. The result of this is that ssh commands to localhost:2222 will be directed at the guest’s ssh server.

vagrant commmands

Most vagrant commands for operating on virtual machines are usually to be executed from within the same ‘top level’ directory as the Vagrantfile. The vagrant command vagrant ssh will automatically connect to the virtual machine, making use of existing ssh public-key authentication. Please note that most vagrant base boxes are not suitable for production use, as the default Vagrant ssh keypairs they often employ are known and widely published.

VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "centos64"
  config.vm.hostname = "db2"
  config.vm.network "private_network", ip: "192.168.50.4"
  config.vm.provision "puppet" do |puppet|
    puppet.manifest_file  = "site.pp"
  end
end

The following actions are taken when executing vagrant up using the above Vagrantfile:

  • The base box “centos64” is copied and started as a new virtual machine.
  • The hostname “db2” is assigned to the new virtual machine.
  • A second network interface is created and assigned to the virtual machine. This network interface is a private_network in Vagrant configuration files, also known as a host-only network. This means that only the host machine (your workstation) will be able to communicate with this one virtual machine.
  • The “puppet” block tells vagrant to execute the puppet apply command with the file site.pp as an argument. The location of this file must be in the same directory as our Vagrantfile.

The output from vagrant up should look something like:

Bringing machine 'default' up with 'virtualbox' provider...
[default] Importing base box 'centos64'...
[default] Matching MAC address for NAT networking...
[default] Setting the name of the VM...
[default] Clearing any previously set forwarded ports...
[default] Creating shared folders metadata...
[default] Clearing any previously set network interfaces...
[default] Preparing network interfaces based on configuration...
[default] Forwarding ports...
[default] -- 22 => 2222 (adapter 1)
[default] Booting VM...
[default] Waiting for machine to boot. This may take a few minutes...
[default] Machine booted and ready!
[default] Setting hostname...
[default] Configuring and enabling network interfaces...
[default] Mounting shared folders...
[default] -- /vagrant

The contents of our site.pp

$vagrant_packages = ['nss-mdns', 'avahi']

  package { $vagrant_packages: ensure => installed }

  service { 'messagebus':
    ensure  => running,
    require => Package[$vagrant_packages],
    }

    service { 'avahi-daemon':
    ensure   => running,
    require  => Service['messagebus'],
  }

.pp files are known in Puppet as manifests, and the above manifest does three things.

  • Lines 1-3: define two packages and install them.
  • Lines 5-8: start the service messagebus, but only after the nss-mds and avahi packages are installed.
  • Lines 10-13: start the service avahi-daemon, but only after nss-mds is started.

Now, assuming everything went to plan (and iptables is disabled or similarly turned off), the machine should be available on the host-only under its hostname and the .local DNS suffix.

ping db2.local
PING db2.local (192.168.50.4): 56 data bytes
64 bytes from 192.168.50.4: icmp_seq=0 ttl=64 time=0.389 ms

Conclusion

The above snippets show us how we can use Vagrant and Puppet in order to create machine configurations as if they were source code. Much like source code, these Puppet code behind these environments can and should be re-used whether across production servers or on local development workstations. In a future post, we will explore features of the puppet tool and language share an evolutionary approach to creating re-useable puppet configurations.