|
| 1 | +# wtf is a puppet? |
| 2 | + |
| 3 | +Puppet is configuration management tool, written in Ruby, that compiles |
| 4 | +and runs code written in the Puppet language. |
| 5 | + |
| 6 | +But what does that actually mean in words a human can understand? |
| 7 | +Puppet is a tool that runs some code and that code can do all sorts of |
| 8 | +really powerful things to configure computers for you. |
| 9 | + |
| 10 | +Why do I want code mucking about with my laptop though? |
| 11 | +For the exact same reasons you want code configuring your server. |
| 12 | +In particular, homogeneity, reproducibility, reusability, and automation. |
| 13 | +When you use code to express how a machine should be configured, |
| 14 | +you know that all of your machines are configured the same way, |
| 15 | +that you can repeat that configuration any number of times, |
| 16 | +and that you don't have to do it manually each time. |
| 17 | + |
| 18 | +## How does Puppet work? |
| 19 | + |
| 20 | +A Puppet run has two main steps: compilation and application. |
| 21 | + |
| 22 | +The compilations step starts with reading in what's called the site manifest. |
| 23 | +The site manifest is a single file that contains Puppet code that is responsible |
| 24 | +for telling the compiler what other Puppet code it should compile. |
| 25 | + |
| 26 | +Here are the first few lines of the default site manifest for Boxen: |
| 27 | + |
| 28 | +``` |
| 29 | +include boxen::environment |
| 30 | +include homebrew |
| 31 | +include gcc |
| 32 | +``` |
| 33 | + |
| 34 | +This tells the Puppet compiler that it must include the classes |
| 35 | +`boxen::environment`, `homebrew`, and `gcc`. |
| 36 | +Puppet will look for those classes on the modulepath. |
| 37 | +Typically, these files would be located at |
| 38 | +`$modulepath/boxen/manifests/environment.pp` and |
| 39 | +`$modulepath/homebrew/manifests/init.pp`. |
| 40 | + |
| 41 | +These might include other classes as well, or define **resources**. |
| 42 | +Resources are the building blocks of Puppet. |
| 43 | +A resource is an abstract description about a **thing** and the **state** |
| 44 | +that thing should be in. |
| 45 | +Every resource has a **type**, which is just a classification of resources. |
| 46 | +For example, we might have a resource `Package[mysql]` and its type would be |
| 47 | +`Package`. |
| 48 | +Puppet also supports multiple providers for types which act as pluggable |
| 49 | +backends depending on the operating system it's running on or the tools |
| 50 | +available on a particular system (in the case of the `Package` type, |
| 51 | +we might have providers for `yum` and for `aptget`). |
| 52 | + |
| 53 | +So now the Puppet compiler has finished tracking down and loading all the |
| 54 | +classes and resources it needs. |
| 55 | +Assuming we haven't encountered any compile-time errors, Puppet then begins |
| 56 | +the next phase of a run: applying the catalog. |
| 57 | + |
| 58 | +All of the resources Puppet has collected into the catalog have formed a |
| 59 | +DAG (directed, acyclic graph) that represents the order in which all these |
| 60 | +resources can be applied in a correct order. |
| 61 | +Puppet simply grabs the first "node" in this graph and traverses all nodes |
| 62 | +in the graph, applying each resource as it goes. |
| 63 | + |
| 64 | +Application of an individual resource starts with Puppet asking "is this |
| 65 | +resource in the state requested?" If the answer is yes, Puppet moves onto the |
| 66 | +next node and repeats this process. If the answer is no, Puppet will make |
| 67 | +whatever changes it can to reconcile the current state of the resource with |
| 68 | +what it should be, and then continues on. |
| 69 | + |
| 70 | +Once all resources have been applied, the Puppet run is complete. |
| 71 | + |
| 72 | +## Declarative by nature |
| 73 | + |
| 74 | +One of the most confusing parts of the Puppet language to many newcomers is |
| 75 | +how the Puppet catalog orders and applies resources. |
| 76 | +The key thing to remember is that Puppet is a **declarative** language rather |
| 77 | +than a procedural one. |
| 78 | +In human words, the only order that matters in Puppet is order specified by |
| 79 | +relationships between resources. |
| 80 | + |
| 81 | +Here's an example of how someone new to Puppet might write a class: |
| 82 | + |
| 83 | +``` puppet |
| 84 | +class mysql { |
| 85 | + file { '/etc/my.cnf': source => 'puppet:///modules/mysql/my.cnf.erb' ; } |
| 86 | + package { 'mysql': } |
| 87 | + service { 'mysql': } |
| 88 | +} |
| 89 | +``` |
| 90 | + |
| 91 | +Someone expecting Puppet to be procedural would read this manifest as a list |
| 92 | +saying: |
| 93 | + |
| 94 | +* First create the my.cnf file |
| 95 | +* Then install the mysql package |
| 96 | +* Then start the mysql service |
| 97 | + |
| 98 | +The problem is, they would be **wrong**. |
| 99 | +There is no guarantee Puppet will apply these resources in that order, |
| 100 | +and it's quite likely that it won't. |
| 101 | + |
| 102 | +The proper way to ensure ordering in Puppet is to use one of the four |
| 103 | +**relationship metaparameters**: |
| 104 | + |
| 105 | +* before - Run this resource before another resource |
| 106 | +* notify - Same as before, but triggers a refresh on that resource |
| 107 | +* require - Run this resource after another resource |
| 108 | +* subscribe - Same as require, but triggers a refresh on that resource |
| 109 | + |
| 110 | +The behavior of these metaparameters should be pretty clear. |
| 111 | +Triggering a resource simply means that a resource will "refresh" itself. |
| 112 | +That typically means something like a `Service` resource restarting itself. |
| 113 | +It's important to remember that these metaparameters are valid on **any** resource. |
| 114 | + |
| 115 | +So, let's rewrite our example properly: |
| 116 | + |
| 117 | +``` puppet |
| 118 | +class mysql { |
| 119 | + file { '/etc/my.cnf': |
| 120 | + source => 'puppet:///modules/mysql/my.cnf.erb', |
| 121 | + notify => Service['mysql'] |
| 122 | + } |
| 123 | +
|
| 124 | + package { 'mysql': |
| 125 | + notify => Service['mysql'] |
| 126 | + } |
| 127 | +
|
| 128 | + service { 'mysql': } |
| 129 | +} |
| 130 | +``` |
| 131 | + |
| 132 | +Now we're telling Puppet what order to apply these resources in: |
| 133 | + |
| 134 | +* Make sure /etc/my.cnf is in place before we start the mysql service |
| 135 | +* If /etc/my.cnf changes, tell mysql to restart |
| 136 | +* Make sure the mysql package is in place before we start the mysql service |
| 137 | +* If the mysql package changes, tell mysql to restart |
| 138 | + |
| 139 | +It's important to note that we didn't tell Puppet if `File[/etc/my.cnf]` |
| 140 | +should come before or after `Package[mysql]`. |
| 141 | +This is intentional. |
| 142 | +Most package managers won't overwrite a configuration file that already exists |
| 143 | +at package install-time, so in this case, we can assume installing the mysql |
| 144 | +package won't clobber our custom `/etc/my.cnf` file. |
0 commit comments