Automating Development Enviroment using Vagrant and Puppet

Automating Development Enviroment using Vagrant and Puppet

What is Vagrant?

It’s a wrapper around a variety of VM providers like VirtualBox, VMware and provides a single command that uniformly creates, provisions, destroys and connects to machines.

Getting Started with your first VM

As an initial step, download and install Vagrant and VirtualBox specific to your machine operating system. Once installed, create a new project directory and initialize the same with vagrant, as below:

$ mkdir vagrant
$ cd vagrant
$ vagrant init

Above commands will create a new directory and get it ready with a file called Vagrantfile which will contain all the information that Vagrant needs. File have a lot of comments about the different things you can put in. For now you just care about configuring a base “box” (which is just a virtual machine image) and some other machine properties. I ended up with a file that looked something like this:

VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "hashicorp/precise32"
  config.vm.network :private_network, ip: "90.0.9.99"
end

Here are the things that you configured so far:

  1. config.vm.box: The name of the box that you are bringing up. You created a standard Ubuntu 12.04 LTS 32-bit box. For more boxes, you can look at Discover Vagrant Boxes.
  2. config.vm.network:private_network: Create a private network, which allows host-only access to the machine.

Once you have the file in place you can create your VM from the same directory executing command:

$ vagrant up

After command finishes running, you will have a VM ready to connect and start messing around with. You can access the machine via SSH using:

$ vagrant ssh

This is great and all, but you want to make things more automated, so you have to exit your SSH session and get rid of the VM with:

$ vagrant destroy

Provisioning

Provisioning invloves actions to prepare a server with appropriate systems, data and software or setting up a server for use in the network and to do the same Vagrant offers multiple options like:
– Shell scripts.
– Puppet
– Chef
– Ansible
– Salt.

For this article, I’m using puppet.

What is Puppet?

It’s an open-source configuration management tool and has its own declarative language which describe system configuration. It’s programs are called “manifests” and use the “.pp” file extension.

What is librarian-puppet?

It is an extension of ruby’s bundler model to install gems. Helps in managing the puppet modules on which our infrastructure depends on, whether the modules come from the Puppet Forge, Git repositories or a just a path. Of course you can manually download the modules from Puppet Forge and store them in the repository of our project in puppet/modules directory alongside with your own Puppet code. This is rather messy, you have to manually maintain the dependencies and it makes your repository bigger than necessary. So, librarian-puppet provides a better way of doing this. Once installed, allows to specify a list of Puppet modules in Puppetfile and manages the installation of these modules and their dependencies for you.

Provisioning your first VM

To start with provisioning you need to make sure puppet and librarian-puppet is installed on your Vagrant VM and you do it executing a script on your guest machine using Vagrant Shell provisioner, which result is editing the Vagrantfile to look something like this:

VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "hashicorp/precise32"
  config.vm.network :private_network, ip: "90.0.9.99"
  config.vm.provision "shell", path: "installation-script.sh"
  config.vm.provision :puppet do |puppet|
    puppet.manifests_path = 'puppet/manifests'
    puppet.module_path = 'puppet/modules'
    puppet.manifest_file ="init.pp"
  end
end

Here are the things that you configured in Vagrantfile:

  1. config.vm.provision: method call to configure the provisioner. config.vm.provision "shell" uses “shell” as a provisioner and config.vm.provision :puppet uses “puppet” as a provisioner.
  2. puppet.manifests_path: Path to the directory which contains the manifest files. This defaults to “manifests”.
  3. puppet.module_path: Path, on the host, to the directory which contains Puppet modules.
  4. puppet.manifest_file: The name of the manifest file that will serve as the entrypoint for the Puppet run. This manifest file is expected to exist in the configured manifests_path. This defaults to “default.pp”.

Next you will create installation-script.sh in your current project directory, which will install, librarian-puppet, it’s dependency GIT and puppet in your guest machine.

$ touch installation-script.sh

Content of installation-script.sh should look something like this:

#!/usr/bin/env bash
set -e
# Directory in which PuppetFile is placed to be scanned by librarian-puppet.
PUPPET_DIR=/vagrant/puppet

echo "Installing Git.."
apt-get -q -y install git

echo "Installing librarian-puppet.."
if [ "$(gem search -i librarian-puppet)" = "false" ]; then
  gem install librarian-puppet -v 1.0.0
fi
echo "librarian-puppet installed!"
echo "Executing PuppetFile.."
cd $PUPPET_DIR && librarian-puppet install --path modules

echo "Installing Puppet repo for Ubuntu 12.04 LTS"
wget -qO /tmp/puppetlabs-release-precise.deb \
        https://apt.puppetlabs.com/puppetlabs-release-precise.deb
dpkg -i /tmp/puppetlabs-release-precise.deb
rm /tmp/puppetlabs-release-precise.deb
aptitude update
echo Installing puppet
aptitude install -y puppet
echo "Puppet installed!"

Next you will create puppet directory and subdirectories inside your current project directory as specified in Vagrantfile.

$ mkdir puppet
$ cd puppet/
$ touch Puppetfile
$ mkdir modules
$ mkdir manifests
$ cd manifests
$ touch init.pp
$ cd ..
$ cd modules
$ mkdir java8
$ mkdir tomcat
$ cd java8
$ mkdir files
$ mkdir manifests
$ cd  manifests
$ touch init.pp
$ cd ../..
$ cd tomcat
$ mkdir manifests
$ cd manifests
$ touch init.pp

Next, Download the JDK 8 and copy to directory vagrant/puppet/modules/java8/files/ and content of vagrant/puppet/modules/java8/manifests/init.pp should look like:

class java8(
$java_archive = "jdk-8u51-linux-i586.gz",
$java_home = "/usr/lib/jvm/jdk1.8.0_51/",
$java_folder = "jdk1.8.0_51")

 { Exec{
     path => [ "/usr/bin", "/bin", "/usr/sbin"]
       }

   file {
     "/tmp/${java_archive}" : ensure => "present",
     source => "puppet:///modules/java8/${java_archive}",
     owner => vagrant,
     mode => 755
        }

   exec {
     'extract jdk': cwd => '/tmp',
     command => "tar xfv ${java_archive}",
     creates => $java_home,
     require => File["/tmp/${java_archive}"],
        }

   file {
     '/usr/lib/jvm' : ensure => directory,
     owner => vagrant,
     require => Exec['extract jdk']
        }

    exec {
     'move jdk': cwd => '/tmp',
      creates => $java_home,
      require => File['/usr/lib/jvm'],
      command => "mv ${java_folder} /usr/lib/jvm/"
         }

    exec {
      'install java': require => Exec['move jdk'],
      logoutput => true,
      command => "update-alternatives --install /bin/java java ${java_home}/bin/java 1"
         }

    exec {
      'set java': require => Exec['install java'],
      logoutput => true,
      command => "update-alternatives --set java ${java_home}/bin/java"
         }

    exec {
      'install javac': require => Exec['move jdk'],
      logoutput => true,
      command => "update-alternatives --install /bin/javac javac ${java_home}/bin/javac 1"
         }

    exec {
      'set javac': require => Exec['install javac'],
      logoutput => true,
      command => "update-alternatives --set javac ${java_home}/bin/javac"
         }

    file {
      "/etc/profile.d/java.sh": content => "export JAVA_HOME=${java_home} export PATH=\$PATH:\$JAVA_HOME/bin"
         }
}

Content of vagrant/puppet/modules/tomcat/manifests/init.pp should look like:

class tomcat {
    package {"tomcat7":
        ensure => installed
    }
    service {"tomcat7":
        ensure => running,
        enable => true,
        require => Package["tomcat7"]
    }
}

Content of Puppetfile should look like:

# Puppetfile Configuration for librarian-puppet.
forge "http://forge.puppetlabs.com"
mod "puppetlabs/apt"
mod "puppetlabs/apache"
mod "puppetlabs/mongodb"

Here are the things that you configured in Puppetfile:

  1. mod “puppetlabs/apache”: Refers to the puppetlabs/apache module which allows you to set up virtual hosts and manage web services with minimal effort.
  2. mod “puppetlabs/mongodb”: Refers to the puppetlabs/mongodb module which allows us to installs MongoDB.

Content of vagrant/puppet/manifests/init.pp should look like:

Exec { path => [ "/bin/", "/sbin/" , "/usr/bin/", "/usr/sbin/" ] }
include java8
include tomcat
class { 'apache':  }
class {'::mongodb::server':
  port    => 27018,
  verbose => true,
}

Here are the things that you configured in init.pp:

  1. include java8: Load Java 8 module placed inside puppet modules directory and install in our virtual machine.
  2. include tomcat: Load Tomcat module placed inside puppet modules directory and ensure it should be running on port 8080.
  3. class { ‘apache’: }: Install Apache in our virtual machine with the default parameters running on port 80.
  4. class {‘::mongodb::server’:
    port => 27018,
    verbose => true,
    }
    : Install and start MongoDB server instance in our virtual machine on port 27018.

Once done with all the file in place, your project directory structure should look like:

Vagrant Directory Structure

Now you can boot up your VM provisioned with Java 8, Apache, Tomcat 7 and MongoDB from the vagrant directory executing command:

$ vagrant up

Is Vagrant VM provisioned?

  1. Apache : Open browser in your machine(host) and enter url : http://90.0.9.99/
  2. Tomcat : Open browser in your machine and enter url : http://90.0.9.99:8080/
  3. Java 8 : Access the virtual machine(guest) via SSH and execute command: java -version.
  4. MongoDB Server Instance: Access the virtual machine(guest) via SSH and execute command: mongod --version.

Same you can fork from vagrant-puppet GitHub repository.

Leave a Reply

Your email address will not be published. Required fields are marked *