Advanced Puppet Pattern
Version 9 (James Turnbull, 03/14/2010 11:03 am)
| 1 | 8 | James Turnbull | # A More Advanced Puppet Pattern |
|---|---|---|---|
| 2 | 1 | ||
| 3 | 9 | James Turnbull | When we left off in the [[Simplest Puppet Install Pattern]], 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 | 4 | James Turnbull | [[Puppet Modules|module]]. 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 | 5 | James Turnbull | this [[Module Organisation|module]] 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 | 6 | James Turnbull | work we need to refer back to our [[Module Organisation|module]] 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 | 7 | James Turnbull | can also make use of [[External Nodes]] or [[LDAP Nodes]] 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 | 7 | James Turnbull | want to read the [[Puppet Best Practice]], [[Style Guide]], and |
| 225 | 7 | James Turnbull | [[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]] . |