In this episode I wanted to introduce you to Puppet. Puppet allows you to quickly automate many sysadmin tasks, like deploying new machines, pushing changes out to existing machines, and helps you verify the configs of existing machines. We will also be setting up development environment for testing and playing around with puppet using Vagrant.
In this episode I wanted to introduce you to Puppet. Puppet allows you to quickly automate many sysadmin tasks, like deploying new machines, pushing changes out to existing machines, and helps you verify the configuration of existing machines.
Puppet is essentially a piece of software called an agent, that runs on each of your machines, this talk to a central server called a Puppet master. You can also run these agents in a standalone mode, without the need for a Puppet master, like we are going to do today. The standalone mode is not only useful for learning Puppet, but also great for testing scripts before deploying them to your Puppet master.
But, I am getting ahead of myself, rather than telling you all about Puppet, let me just show you. This might not make sense at first, but bear with me, and it should come together.
I have put together some example Puppet scripts, which are typically called manifests. The first file we are going to look at, is something called a node definition. The node definition defines which Puppet scripts or manifests are assigned to a particular node via these include lines.
In this file you will define all your servers, something like this. Then you will assign Puppet manifests to each node. So lets say you wanted to configure, httpd and mysql. Under the node definition for that particular server, you will just need to include your Puppet manifests, like this.
Or maybe you have multiple server and you want to spread the load across multiple machines for load balancing, you could do something like this. Now we are going to define server one and two as httpd instances, and server three as a mysql server. This is just an example, I just wanted to show you the power of these include files.
So via these Puppet manifests and the node definition we can deploy changes to any number of servers without actually having to login to any of them! These manifests are also self documenting, in that, if you use Puppet to build servers from scratch, then you have a clear picture of what your infrastructure looks like just by reviewing the node definition.
Okay, now that you know what the node definition is about, lets take a look at these manifest files. A manifest can be as simple as creating a file, tweaking some permissions, or range to very complex, multi-platform behemoths.
As you can see here, that we included a motd manifest, which modifies the message of the day. Lets go ahead a take a look at how this script works. You can see the directory structure over on the left, under the modules sub directory, there is a directory called motd. In here, you will see two directories, one called files, and the other called manifests. Lets take a looks in the manifests directory, and you will see a files called init.pp.
In this init file, you will see that we have defined a motd class. This is what a very simple Puppet manifest will look like, and it used the programming language and syntax of ruby. Do not be scared if you do not know ruby, actually, before we look at this manifest, let me show you a website that was immensely helpful in helping me learn Puppet and a bit of ruby syntax too. The site is called Puppetcookbook.com, and it has simple straight forward examples, that will help you get your foot in the door. Armed with Puppet Cookbook you will have Puppet installing packages, creating directories, and deploying changes in no time.
Okay, lets head back to our editor and take a look at the motd class. Here, we define a file, /etc/motd, make sure it exists, set some permissions, and set the file contents by pointing it at a source file. This can be extremely helpful for deploying configuration files. Lets take a look at the file contents we are going to deploy, by navigating over to the motd module, and looking in the files sub directory. You can see there is a files called m-o-t-d, or message of the day.
You can see my nice little message of the day message. On machines that I manage with Puppet, I like to throw a little line in the message of the day, giving everyone a heads up, that this machines is managed via Puppet. Just in case someone tries to make a change to the machine, and it gets reverted.
You see, Puppet not only deploys changes, but it also makes sure those changes persist. What I mean by this, is that Puppet will, by default run every 30 minutes and check our Puppet manifests, and then verify all changes. So, for example, say we manage /etc/motd via Puppet, then someone logs in and modifies /etc/motd manually, on the next Puppet run, Puppet with notice that /etc/motd has changed (via it checksum), and change it back! This can be extremely powerful for verifying the state of your infrastructure, especially if you have tens, hundreds, or thousands of nodes.
Just to review, lets flip back to the motd manifest, here are make sure a file has our ownership and permissions, that we set, along with the file contents. On every Puppet run, this file will be checked, and if it does not these requirements, it will be changed so that they are.
Now that we know a little bit more about Puppet, lets flip back to the node definition. In the node definition there is a manifest called openssh. This example is a little more complex, but extremely powerful. Lets take a look.
Again, on the left hand side, we will navigate down to the modules, and open openssh. We will open up the init file and have a look. In here you will see a very common pattern that is typically referred to as the package, file, service pattern. First we define the openssh class (using the ruby syntax). Then we make sure the openssh-server package is installed, then we define a configuration file, and finally we make sure the sshd service in running.
Lets go over this example in a little more detail.
Puppet acts as an abstraction layer above your operating system. So we say, make sure the openssh-server package is installed, how does it actually do that, you might wonder? Well, Puppet knows facts about your operating system, like, for example, that this is a Redhat Enterprise Linux machine, so it should use yum to install these packages, but this mechanism works on other operating systems too. Puppet is very clever like that.
In this next block we define a file, just like we did in the message of the day example. Then the next block here, we ensure the sshd service is running. This can be very handy, in that you can define a bunch of manifests for each service and remotely control your infrastructure.
Before we move on, I just want to show you one more thing, here. You can define relationships in Puppet. In the top block, you can see that we have a "before" option set, and it points to a file. What this does, is it tell Puppet, that this package should be installed, before this file is deployed. Say, for example, if we did not do this, our file could be deployed, and possibly be overwritten when our package is installed. This just adds some insurance that the package will be installed before our configuration file is deployed.
In this last block be define a second. You can see we have the subscribe option set, and this also points to a file. What this does, and it is pretty cool, it tells Puppet, that if this files changes, restart sshd. So say for example, say we tweak a setting in sshd_config, Puppet will notice, based on its checksum, and from this subscribe option, will automatically restart sshd for us. Pretty cool!
I should mention that there are tons of great example on GitHub and Puppet even has a community driven repository of modules called the Puppet Forge, see links in episode notes below. There are already Puppet manifests for just about anything you can think of, so it makes deploying new scripts fairly easy.
Now that we know what Puppet is, and how it works, lets see it in action. Lets look at setting up development environment for testing and playing around with Puppet. We are going to use our newly gained Vagrant knowledge from episodes #4. If you have not watched episodes #4, you should do that, since this demo take for granted some key Vagrant concepts, and it will help you replicate my results.
I have put together a CentOS 6.4 minimal Vagrant box, with Puppet installed. We are going to use this Vagrant box to play around with Puppet and test our scripts. You can download the Vagrant box along with all supporting files, via the episode notes below, its about 375 meg compressed file. You will need to have Ruby, VirtualBox, and Vagrant installed, before you can use this Vagrant box.
Alright, just download the package, with wget or curl, like so, then unpack it and change to the directory. In here, you will find the Vagrant box, some example Puppet scripts that we previewed earlier in this epise, a readme file, and our Vagrantfile. I also threw in the VeeWee scripts used to create the CentOS box, just in case you were interested in seeing how they work.
# download episode content wget https://s3.amazonaws.com/sysadmincasts.com/static/data/vagrant-centos64-x86_64-minimal.tar.gz tar zxvf vagrant-centos64-x86_64-minimal.tar.gz cd vagrant-centos64-x86_64-minimal
The Vagrantfile is a little bit special, in that I have trimmed it down, and also enabled Puppet provisioning. This line tells Vagrant what box to use, then here is our Puppet provisioning block, which tells vagrant to execute Puppet along with some settings, right after the VM has started. Here we have the node definition, which we covered earlier, and the supporting modules and manifests directories.
Now that you have seen the Vagrant file, lets add the CentOS box. We do this by running, vagrant box add, the box name, in this instance centos64-64bit-minimal, and then we provide the box file, centos64-64bit-minimal dot box.
# add the centos64-x86_64-minimal box vagrant box add centos64-x86_64-minimal centos64-x86_64-minimal.box
Now lets fire it up via running, vagrant up. Vagrant is going to boot our CentOS Vagrant box, and then run Puppet. Let me just give you an overview of what is happening. Lets scroll up a bit to where we executed vagrant up. This block here, is standard vagrant output, telling us about networking, port forwarding, booting the VM, and then mounting /vagrant to our current directory.
# start the box vagrant up
These next couple lines are additional mounts related to our Puppet configuration. Then the Puppet provisioning starts, as you can see, Puppet is running our node definition here. The rest of the output is generated by the example Puppet scripts I have put together. You do not need to understand all the output, but lets touch on a couple things.
Looks like the message of the day was updated, and something to do with selinux, along with our openssh tweaks. Then we are notified about the finished Puppet catalogue run. Pretty cool, lets login, and take a look around.
We connect by running, vagrant ssh. Sweet, we are greeted with our custom message of the day, which was deployed with Puppet by the way! Lets take a look at the /etc/redhat-release file, and then run df to see what mounts this system has.
# ssh to the box vagrant ssh
You can see our /vagrant mount. Lets take a look in there. You can see that this /vagrant mounted is our CentOS project directory, that we downloaded earlier. I love this feature, because it allows you to share files quickly between your host machine and your Vagrant box. Okay, now lets take a look at our Puppet node definition file that we used to create this machine.
I have included some example commands here to run Puppet manually, so if you make any changes to the Puppet manifests, and then run this command to apply the changes. You can also run the noop option, this means, that Puppet will run but not change anything, this is handy for getting a handle on what might change.
The reason why I love Puppet and Vagrant together, is that you can test clean Puppet deployments, lets walk through what I mean by this.
First we are going to logout, then we are going to run, vagrant destroy, this is going to delete this instance of our Vagrant box. Then we are going to run, vagrant up, again. You can see that we created a whole new vagrant box and our Puppet scripts were executed again.
# destroy the box vagrant destroy
Whats so great about this, is that we did not reuse our previous instance of a Vagrant box, this is a completely new one! So you have freedom to test destructive changes with Puppet, or totally screw up the OS, and quickly create a clean environment again!
Anyways, I have probably been talking long enough. There is a bit of a learning curve with Puppet, but, hopefully after watching this demo, you will see it is well worth it!