Sunday 8 July 2012

Puppet with no strings attached



In my previous post I talked about configuring a single node on a virtual machine using Vagrant with a Shell provisioner. The shell script which is run on the first boot to a vanilla OS, will install puppet and git then clone your puppet repository onto the node and run apply on it.  I won't go into the ins and outs of Puppet as they cover it really well in there documentation.

The standard setup of puppet makes use of a puppet server which all your nodes (as puppet clients) connect to, however it requires a bit of setup, and has issues which are covered in this excellent blog post. The blog post explains how to set up puppet using an empty git repo which you then push to via git.  Git has hooks that you can run code at on certain events. A post receive hook on the node will run after you have pushed to it.  I do this setup of the empty repo and the post receive hook via my base puppet module. Below is an example of a puppet script, this is run as part of the initial puppet apply described in my last post.

class my-base {

    user { "git":
      ensure => "present",
      home => "/var/git",
    }

    file { "/etc/sudoers.d/100-git":
        owner => root,
            group => root,
            mode => 440,
        content => "git ALL=(ALL) NOPASSWD:ALL",
        require => User["git"]
    }

    file {
      "/var/git": ensure => directory, owner => git, 
      require => User["git"];
      "/var/git/.ssh": ensure => directory, owner => git, 
      require => [User["git"], File["/var/git"]];
      "/var/git/puppet": ensure => directory, owner => git, 
      require => [User["git"], File["/var/git"]];
    }


    ssh_authorized_key { "git":
      ensure => present,
      key => "YOURKEY",
      user => git,
      name => "git@yourdomain.com",
      target => "/var/git/.ssh/authorized_keys",
      type => rsa,
      require => File["/var/git/.ssh"],
    }

    package { "git":
      ensure => installed,
    }

    exec { "createPuppetRepo":
      cwd => "/var/git/puppet",
      user => "git",
      command => "/usr/bin/git init --bare",
      creates => "/var/git/puppet/HEAD",
      require => [File["/var/git/puppet"], Package["git"], User["git"]],
    }

    $hook_puppet = "#!/bin/sh
     git archive --format=tar HEAD | (cd /etc/puppet && sudo tar xf -)
    sudo sh -c \"puppet apply /etc/puppet/manifests/site.pp >> /var/log/puppet/puppet.log 2>&1\"
    "

    file { "/var/git/puppet/hooks/post-receive": 
        ensure => present,
        content => $hook_puppet,
        group => git,
        mode => 755,
        owner => git,
        require => Exec["createPuppetRepo"]
    }

}

After this is applied I can push puppet updates from my local puppet repo using the command below, which adds my vagrant vm node as a remote and then pushes my changes. You would also want to push the changes to your hosted repo so new nodes get the latest version.

dev-macbook # git remote add mynode ssh://git@mynode/var/git/puppet
dev-macbook # git push mynode master

Once this is received it will apply the new puppet config vi the hook we setup.  I also create similar push git repos for application code (Python service code), in the post receive for that I restart the Supervisor daemon which manages my service instances, I will cover Supervisor in a later post.

Now we have the mechanisms to manage a node using the two commands below.

dev-macbook # vagrant up
....Some time later after updating puppet......
dev-macbook # git push mynode master

This setup can be used in most places, including Amazon's EC2, where the initial shell script would be passed as the user-data, you could do this via a boto python script. Also Linode has stackscripts which would allow you to run this script to get the node provisioned ready to go.

You will probably quickly grow out of having one node, or one type of node, for example you may have a database node and a application server node, and a load balancer node etc.  You can still use the mechanism above to achieve this with just an addition to the Shell script and a special Puppet module.  I will cover this next time.




No comments:

Post a Comment