Advanced Puppet Pattern

Version 1 (Anonymous, 03/13/2010 08:01 pm)

1 1
# A More Advanced Puppet Recipe
2 1
3 1
When we left off in the [[Simplest Puppet Install Recipe]] , we had
4 1
two files, our site manifest and a class configuring the sudo
5 1
tool.
6 1
7 1
# Our initial site manifest
8 1
9 1
The site manifest, /etc/puppet/manifests/site.pp, contained:
10 1
11 1
    # /etc/puppet/manifests/site.pp
12 1
    
13 1
    import "classes/*"
14 1
    
15 1
    node default {
16 1
        include sudo
17 1
    }
18 1
19 1
This file imports all the .pp files in the classes directory and
20 1
then specifies a special node, called default, whose configuration
21 1
will be applied to all nodes. In this case the default node only
22 1
contains a single class: sudo.
23 1
24 1
# Our first class
25 1
26 1
The sudo class contained:
27 1
28 1
    # /etc/puppet/manifests/classes/sudo.pp
29 1
    
30 1
    class sudo {
31 1
        file { "/etc/sudoers":
32 1
            owner => "root",
33 1
            group => "root",
34 1
            mode  => 440,
35 1
        }
36 1
    }
37 1
38 1
This simple class performs only one function: managing the
39 1
/etc/sudoers file.
40 1
41 1
This is a good start but Puppet is capable of a whole lot more so
42 1
let's expand on this initial example.
43 1
44 1
# Revision Control
45 1
46 1
Before we get too far along though, you'll notice we've got a
47 1
couple of configuration, .pp, files. As you configure more and more
48 1
resources you'll find yourself adding to this collection of files.
49 1
This collection of files also needs to be managed and we strongly
50 1
recommend you implement a
51 1
[revision control system](http://en.wikipedia.org/wiki/Revision_control),
52 1
such as Subversion or Git. You should place all your manifests and
53 1
potentially other aspects of your Puppet configuration under
54 1
revision control and preferably host your repository on another
55 1
system. The repository should be regularly backed up. This will
56 1
allow you to make changes to your manifests and configuration and
57 1
know you can safely roll them back or recreate an earlier state
58 1
without needing to re-write or edit a large number of files.
59 1
60 1
# Our first module
61 1
62 1
We've seen our first class, sudo, but a better (and recommended!)
63 1
way of bundling configuration resources exists. This is called a
64 1
[[Puppet Modules]] . Puppet best practice is to put as much
65 1
configuration into modules as possible. A module is a portable
66 1
collection of classes, configuration resources, templates and files
67 1
that configures a particular application or function, for example a
68 1
module might be created to manage Apache or MySQL.
69 1
70 1
In this case we're going to create a module to manage our sudo
71 1
configuration. But our earlier work on the sudo class is not all
72 1
lost and we can make use of our existing class to create our first
73 1
module.
74 1
75 1
First, let's create a module environment. Create a directory to
76 1
hold your modules (this should match the directory set in the
77 1
modulepath configuration option which usually defaults to
78 1
/etc/puppet/modules):
79 1
80 1
    # mkdir -p /etc/puppet/modules
81 1
82 1
Now make a directory to hold our new module, sudo:
83 1
84 1
    # mkdir -p /etc/puppet/modules/sudo/manifests
85 1
86 1
Each module has a specific directory structure that allows Puppet
87 1
to find all elements of the module and auto-load them. The heart of
88 1
this [[Module Organisation]] is the init.pp file, located in the
89 1
module/manifests/ directory. We're going to use our existing sudo
90 1
class as the basis of our new init.pp file:
91 1
92 1
    # cp /etc/puppet/manifests/classes/sudo.pp /etc/puppet/modules/sudo/manifests/init.pp
93 1
94 1
# Expanding The Sudo Module
95 1
96 1
Now we've got the core of our new sudo module let's expand on the
97 1
functions it performs for us. First, we're going to have it handle
98 1
the installation of the sudo package and then we're going to use
99 1
Puppet's built-in file server to distribute a /etc/sudoers file for
100 1
us. This file will contain all a central sudo configuration that
101 1
all our nodes will use. Let's look at our updated sudo class now
102 1
re-invented as a module.
103 1
104 1
    # /etc/puppet/modules/sudo/manifests/init.pp
105 1
    
106 1
    class sudo {
107 1
    
108 1
        package { sudo: ensure => latest }
109 1
    
110 1
        file { "/etc/sudoers":
111 1
            owner   => root,
112 1
            group   => root,
113 1
            mode    => 440,
114 1
            source  => "puppet:///sudo/sudoers",
115 1
            require => Package["sudo"],
116 1
        }
117 1
    }
118 1
119 1
First, we've added a new resource type, package, and told it to
120 1
install the sudo package and always ensure the package installed is
121 1
the latest version.
122 1
123 1
Next, we've updated our original file type resource to include a
124 1
few more attributes. Instead of now just managing a local file
125 1
we've added the source attribute that tells Puppet to look for our
126 1
/etc/sudoers file on Puppet's built-in file server. To make this
127 1
work we need to refer back to our [[Module Organisation]] and add a
128 1
new location to hold this file and potentially other files we might
129 1
want to distribute with this module. First we make a directory to
130 1
hold the file:
131 1
132 1
    # mkdir /etc/puppet/modules/sudo/files
133 1
134 1
Then we copy in our new sudoers file:
135 1
136 1
    # cp /etc/sudoers /etc/puppet/modules/sudo/files/sudoers
137 1
138 1
We've just copied in an arbitrary sudoers file but you could create
139 1
your own and modify it to suit your environment. Now, when we look
140 1
at the source attribute, we can see it is constructed like this
141 1
puppet:///sudo/sudoers. The puppet tells Puppet that we're using
142 1
the internal file server. This works much like a normal protocol
143 1
prefix, like http:// or ftp:// (although Puppet only supports file
144 1
serving from its internal file server so far, in later releases
145 1
support for other sources will be supported). But notice we've got
146 1
an extra /. This isn't a mistake. Normally, the source attribute
147 1
would look like puppet://server\_name/module/file. Instead of
148 1
specifying the server name explicitly using the / tells Puppet to
149 1
look for the file on the Puppet master currently managing the node.
150 1
This makes our configuration a bit more portable. We've then
151 1
specified the module we want Puppet to look into the find the file,
152 1
sudo, and the name of the file to be sourced. Puppet knows from
153 1
this attribute that the sudoers file contained in the
154 1
/etc/puppet/modules/sudo/files/ directory is the file it needs to
155 1
source and send to the client.
156 1
157 1
Lastly, we've added a meta-parameter called require which allows us
158 1
to build a dependency. It says to Puppet that before it should do
159 1
anything with the /etc/sudoers file then it should ensure the
160 1
resource, Package["sudo"], has been processed. In this case this
161 1
means that the sudo package will always be installed before the
162 1
/etc/sudoers file is sourced and managed.
163 1
164 1
# Importing Modules
165 1
166 1
Modules need to be imported into Puppet to make use of them.
167 1
However, generally everything under the modulepath, in our case
168 1
/etc/puppet/modules/, is automatically imported into Puppet and is
169 1
available to be used. There are cases where you will need to
170 1
explicitly import modules (such as pretty much every module from
171 1
David Schmitt's [[Complete Configuration]] set). Since it never
172 1
hurts to import them explicitly anyway, let's go ahead and make a
173 1
file for that purpose:
174 1
175 1
    # /etc/puppet/manifests/modules.pp
176 1
    
177 1
    import "sudo"
178 1
179 1
# Node Definitions
180 1
181 1
Next, let's create some nodes that we'll apply our new module too.
182 1
We'll want to put node definitions into their own file, nodes.pp,
183 1
like so:
184 1
185 1
    # /etc/puppet/manifests/nodes.pp
186 1
    
187 1
    node basenode {
188 1
      include sudo
189 1
    }
190 1
    
191 1
    node 'web.example.com' inherits basenode {
192 1
    }
193 1
194 1
Here in our new nodes.pp file we've created two nodes: basenode and
195 1
web.example.com. The basenode we can use to store configuration we
196 1
intend to apply to all nodes (you don't have to do this - this is
197 1
just one style). We also created our first client node,
198 1
web.example.com, which inherits our first node, basenode, and hence
199 1
includes the sudo module.
200 1
201 1
In addition to individually specifying nodes in your manifests you
202 1
can also make use of [[External Nodes]] or [[LDAPNodes]] nodes to
203 1
store your node configurations.
204 1
205 1
# Updating site.pp
206 1
207 1
All this leads to some changes to the site.pp file.
208 1
209 1
    # /etc/puppet/manifests/site.pp
210 1
    
211 1
    import "modules"
212 1
    import "nodes"
213 1
    
214 1
    # The filebucket option allows for file backups to the server
215 1
    filebucket { main: server => 'my.server.name' }
216 1
    
217 1
    # Set global defaults - including backing up all files to the main filebucket and adds a global path
218 1
    File { backup => main }
219 1
    Exec { path => "/usr/bin:/usr/sbin/:/bin:/sbin" }
220 1
221 1
# What's Next?
222 1
223 1
At this point you should have a decent grasp on the basics. You'll
224 1
want to read the [[Puppet Best Practice]] , [[Style Guide]] , and
225 1
[[Language Tutorial]] , probably in that order.
226 1
227 1
To learn from other people's work, or simply incorporate it in your
228 1
own, you can look at [[Recipes]] and [[Puppet Modules]] .
229 1
230 1
It is also important to remember that the default Puppet server
231 1
uses an internal webrick web server. The webrick web server does
232 1
not scale very well and is not recommended for production use
233 1
beyond 10 to 20 nodes. It is recommend that you move your Puppet
234 1
server to Mongrel or Passenger. You can find instructions at
235 1
[[Using Mongrel]] and [[Using Passenger]] respectively.
236 1
237 1
For reference information, see [[Reference Index]] .