Part 3: Bootstrapping Your Environment with Vagrant and Puppet

7 Oct

So now, we’ve installed Vagrant and built our own base box. That’s pretty cool, but you’re not really feeling the power quite yet. Lets take this to the next level. A really common problem that can affect dev, qa and ops is the inability to easily and accurately reproduce the production environment. Over time, this leads to deltas between production and staging that can show up and bite you after a production push. Wouldn’t it be super awesome if you could use the same tools you use to manage your production environment, Chef or in my case Puppet to build a production replica cluster using Vagrant?

Another possibility is that you’re early enough in your product life cycle that you haven’t even reached production yet. Perhaps you’re still bootstrapping your architecture and are doing things as cheaply as possible but need things to be reproducible so that you can move it to production when the time comes. Either way, Vagrant is up to the task.

Typical Web Environment

The provided diagram illustrates a pretty typical web environment.  Traffic from the Internet is distributed to a pool of web servers running varnish and apache by a pair of load balancers.  The web servers communicate with a pair of database servers on the back end configured to provide a reliable data store. Our example setup is an Nginx load balancer, an Apache web server and a mysql database backend. Each of these applications resides on it’s own VM and communicates via the private network defined in the Vagrantfile above.

Our first step in the process is to set up a Vagrantfile to define our cluster.


$ mkdir web-cluster && cd web-cluster
$ vagrant init

Next we’ll need to customize our Vagrantfile to get Vagrants view of the universe more in line with ours. You can define more than one node in a single Vagrantfile.


Vagrant::Config.run do |config|
  config.vm.define :lb do |lb_config|
      lb_config.vm.network("33.33.33.10")
      lb_config.vm.box  = "CentOS-5.7-x86_64"
      lb_config.vm.forward_port("nginx",80,8080)
      lb_config.ssh.timeout = 300
      lb_config.ssh.max_tries = 300
      lb_config.vm.provision  :puppet do  |puppet|
        puppet.manifests_path = "puppet/vagrant-manifests"
        puppet.manifest_file = "lb.pp"
        puppet.module_path  = "puppet/modules"
      end
  end
  config.vm.define :web do |web_config|
      web_config.vm.network("33.33.33.50")
      web_config.vm.box  = "CentOS-5.7-x86_64"
      web_config.vm.forward_port("apache",8081,8081)
      web_config.vm.forward_port("varnish",8082,8082)
      web_config.ssh.timeout = 300
      web_config.ssh.max_tries = 300
      web_config.vm.provision  :puppet do  |puppet|
        puppet.manifests_path = "puppet/vagrant-manifests"
        puppet.manifest_file = "web.pp"
        puppet.module_path  = "puppet/modules"
      end
  end
  config.vm.define :db do |db_config|
      db_config.vm.network("33.33.33.100")
      db_config.vm.box  = "CentOS-5.7-x86_64"
      db_config.ssh.timeout = 300
      db_config.ssh.max_tries = 300
      db_config.vm.provision  :puppet do  |puppet|
        puppet.manifests_path = "puppet/vagrant-manifests"
        puppet.manifest_file = "db.pp"
        puppet.module_path  = "puppet/modules"
      end
  end
end

If you can follow along in the config file, you’ll see that we’ve defined 3 virtual machines. These machines are of class lb, web and db and we’ve told Vagrant to try and bootstrap via puppet. We haven’t yet set up our puppet manifests though, so we’re definitely not ready to fire up our cluster just yet. You’ll also note that I’ve configured some port forwards for nginx, apache and varnish so you can access each service directly for testing. Next, we need to create and fill in the directory structure we referenced in the Vagrantfile. I’ve created a git project with my configuration and you can find it here.


$ mkdir -p puppet/{modules,vagrant-manifests}

Normally at this point you’d want to go ahead and populate the puppet modules directory and the manifests directory yourself, but for the purpose of this tutorial, I suggest you use mine. Once you’ve got things installed, you should be able to cd into the web-cluster directory and start bringing up machines.


$ vagrant up
[lb] Importing base box 'CentOS-5.7-x86_64'...
[lb] Preparing host only network...
[lb] Creating new host only network for environment...
[lb] Matching MAC address for NAT networking...
[lb] Clearing any previously set forwarded ports...
[lb] Forwarding ports...
[lb] -- ssh: 22 => 2222 (adapter 1)
[lb] -- http: 80 => 8080 (adapter 1)
[lb] Creating shared folders metadata...
[lb] Running any VM customizations...
[lb] Booting VM...
[lb] Waiting for VM to boot. This can take a few minutes.
[lb] VM booted and ready for use!
[lb] Enabling host only network...
[lb] Mounting shared folders...
[lb] -- v-root: /vagrant
[lb] -- manifests: /tmp/vagrant-puppet/manifests
[lb] -- v-pp-m0: /tmp/vagrant-puppet/modules-0
[lb] Running provisioner: Vagrant::Provisioners::Puppet...
[lb] Running Puppet with lb.pp...
[lb] <puppet output elided>
[web] Fixed port collision 'ssh'. Now on port 2200.
[web] Importing base box 'CentOS-5.7-x86_64'...
[web] Preparing host only network...
[web] Matching MAC address for NAT networking...
[web] Clearing any previously set forwarded ports...
[web] Forwarding ports...
[web] -- ssh: 22 => 2200 (adapter 1)
[web] -- http: 80 => 8081 (adapter 1)
[web] Creating shared folders metadata...
[web] Running any VM customizations...
[web] Booting VM...
[web] Waiting for VM to boot. This can take a few minutes.
[web] VM booted and ready for use!
[web] Enabling host only network...
[web] Mounting shared folders...
[web] -- v-root: /vagrant
[web] -- manifests: /tmp/vagrant-puppet/manifests
[web] -- v-pp-m0: /tmp/vagrant-puppet/modules-0
[web] Running provisioner: Vagrant::Provisioners::Puppet...
[web] Running Puppet with web.pp...
[web] <puppet output elided>
[db] Fixed port collision 'ssh'. Now on port 2201.
[db] Importing base box 'CentOS-5.7-x86_64'...
[db] Preparing host only network...
[db] Matching MAC address for NAT networking...
[db] Clearing any previously set forwarded ports...
[db] Forwarding ports...
[db] -- ssh: 22 => 2201 (adapter 1)
[db] Creating shared folders metadata...
[db] Running any VM customizations...
[db] Booting VM...
[db] Waiting for VM to boot. This can take a few minutes.
[db] VM booted and ready for use!
[db] Enabling host only network...
[db] Mounting shared folders...
[db] -- v-root: /vagrant
[db] -- manifests: /tmp/vagrant-puppet/manifests
[db] -- v-pp-m0: /tmp/vagrant-puppet/modules-0
[db] Running provisioner: Vagrant::Provisioners::Puppet...
[db] Running Puppet with db.pp...
[db] <puppet output elided>

You can ssh to each VM directly


$ vagrant ssh web

Once the cluster is up, you should be able to access the web server http://localhost:8080/ and verify the WordPress install. Port 8080 hits nginx on the load balancer. It then gets passed to varnish on the web server which talks to apache if necessary. Apache then communicates over the network to the db server.

Fine grain control of VM state is available, you can easily destroy and rebuild a single VM.


$ vagrant reload web
$ vagrant up web

Think of how much time you could save being able to destroy and recreate your entire app stack with a single command. Additionally, every time you perform this task, you’re testing the actual deployment scripts you use in production. You can give these tools to your engineering and qa teams and they can have their own production sandboxes in which to run rough shod. Win!

10 Responses to “Part 3: Bootstrapping Your Environment with Vagrant and Puppet”

  1. ʞuǝɥ (@BOK) October 9, 2011 at 5:42 pm #

    This is a great set of tutorials – thanks heaps!
    One (minor and known) issue: the absence of the group “puppet”:

    [lb] err: /File[/var/lib/puppet/rrd]/ensure: change from absent to directory failed: Could not find group puppet
    [lb]
    err: Could not send report: Got 1 failure(s) while initializing: change from absent to directory failed: Could not find group puppet

    • zsprackett October 9, 2011 at 6:07 pm #

      Good comment. I’m glad you like the tutorial. In part 2, when I was making my own base box, I did talk about adding the group

      I also made a change to the postinstall.sh to add a user/group for puppet to prevent an issue later.

      $ diff -ruN postinstall.sh.orig postinstall.sh
      — postinstall.sh.orig 2011-10-06 08:42:27.000000000 -0400
      +++ postinstall.sh 2011-10-06 08:42:36.000000000 -0400
      @@ -23,6 +23,7 @@
      rm ruby-enterprise-1.8.7-2010.02.tar.gz

      #Installing chef & Puppet
      +useradd -u 52 puppet
      /opt/ruby/bin/gem install chef –no-ri –no-rdoc
      /opt/ruby/bin/gem install puppet –no-ri –no-rdoc

      For people who use an off the shelf disk image rather than following from start to finish with this tutorial, you’ll likely see the warning you mentioned.

      • ʞuǝɥ (@BOK) October 11, 2011 at 8:06 am #

        That is true, but using the scripts / manifests / etc. from your “vagrant-tutorial” through a git clone gave this error anyway.
        Unable to verify now (I’m at work) if the group “puppet” is actually there in the box.
        However, running “useradd -u 52 puppet” on a CentOS system creates both user AND group. Weird…

  2. fii November 26, 2011 at 2:56 am #

    Excellent tutorials. Can’t wait to see Part 4

  3. Blarg Dragqueen June 27, 2012 at 7:43 am #

    So, nginx points at http://test, and how is this defined? Without me going through all the configs in detail :)

  4. Blarg Dragqueen June 27, 2012 at 8:23 am #

    Hm, had to “install” word press via a link at http://zacmini.local:8080 before zacmini.local:8080/wordpress/ showed a result and packets flowed to web vm.

    Also, added:

    127.0.0.1 zacmini.local

    to my vm host server in order to resolve that url.

  5. Blarg Dragqueen June 27, 2012 at 8:24 am #

    OK, answered my own question, see http://test is resolved via this setting:

    upstream test {
    server 10.100.100.50:8082;
    }

  6. wyattw September 5, 2012 at 5:45 pm #

    Syntax for “db_config.vm.network(“33.33.33.100″)” is slightly outdated. Should be:

    db_config.vm.network :hostonly, “33.33.33.100″

  7. Aryal November 4, 2012 at 1:30 pm #

    # -*- mode: ruby -*-
    # vi: set ft=ruby :

    # box definitions
    vboxes = {
    :’lb’ => {
    :ipaddress => ’33.33.33.10′,
    :port_forward => { ’80′ => ’8080′ },
    },

    :’web’ => {
    :ipaddress => ’33.33.33.50′,
    :port_forward => { ’8081′ => ’8081′, ’8082′ => ’8082′ },
    },

    :’db’ => {
    :ipaddress => ’33.33.33.100′,
    :port_forward => { },
    }
    }

    Vagrant::Config.run do |vbox_config|
    $vbox = ‘CentOS-5.7-x86_64′

    vboxes.each_pair do |name, options|
    vbox_config.vm.define “#{name}” do |config|
    config.vm.box = $devbox
    config.vm.network(options[:ipaddress])

    # ssh
    config.ssh.timeout = 300
    config.ssh.max_tries = 300

    # port forwards
    options[:port_forward].each_pair do |guest_port, host_port|
    config.vm.forward_port guest_port.to_i, host_port.to_i
    end

    # puppet provisioner
    config.vm.provision :puppet do |puppet|
    puppet.manifests_path = ‘puppet/vagrant-manifests’
    puppet.manifest_file = “#{name}.pp”
    puppet.module_path = ‘puppet/modules’
    end
    end
    end
    end

Trackbacks/Pingbacks

  1. Wilcox Development Solutions Blog » A Rails Development Environment with Puppet - March 10, 2012

    [...] can replicate the entire stack with Vagrant. For now, there’s an excellent starter article: Bootstrapping Vagrant with Puppet. Comments [...]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 74 other followers

%d bloggers like this: