<http://reductivelabs.com/trac/puppet/wiki/PuppetBestPractice>
--
Digant C Kasundra <dig...@stanford.edu>
Technical Lead, ITS Unix Systems and Applications, Stanford University
>
> Hi,
> my 2 cents on your post on the best practices.
>
> 1 - What you have called template classes for me are roles: similar
> logic, a class used by nodes with the same function that imports
> common modules, but with a unique $role variable that may be used
> mostly when sourcing files for a modules that differ just for the role
> of the server.
> Something like what follows has been quite useful for me, with the use
> of a single httpd class for different kind of servers with the
> flexibility to make per host or per role configurations.
> file {
> "/etc/httpd/conf/httpd.conf":
> source => [
> "puppet://$servername/
> apache/httpd.conf-$hostname",
> "puppet://$servername/
> apache/httpd.conf-$role",
> "puppet://$servername/
> apache/httpd.conf"
> ]
> }
> Examples or roles definitions here:
> http://live.lab42.it/puppetinfrastructure/browser/modules/project_general
> /manifests/roles.pp
That is an interesting approach. We actually don't use templates at all. I
leave it in just because I know others do use templates (or roles as you
call them). Using the variable to indicate what role the server will use
and then using logic in a class to make decisions on configuration is to me
equivalent to having explicitly named classes that drop in the appropriate
files. In my experience, I've found the explicit naming of classes to be
less ambiguous when one admin is deciphering another's catalog. When I
look at a server's catalog and I see something like ldap::master, I know
that this server is configuring itself as an ldap master. But if if I had
simply set a variable in the node, perhaps to indicate that role and only
saw ldap in the catalog, it would be ambiguous. In your example, I would
look for something like ldapmaster_role, which again is equivalent to
ldap::master so really, one approach seems similar to another.
But with the vagaries of variables and scoping, I prefer not to use
variables to indicate these sorts of things unless facter provides that
value. (For instance, what happens if someone includes scan_role and
ids_role?)
>
> 2 - What you place in /manifests/ where you can actually define the
> architecture of your infrastructure for me is useful to place in a
> dedicated infrastructural module (the project_general in the example
> before). Doing this it's possible to present as modules different
> infrastructure layouts (from quite easy to complex) with project_**
> naming convention (totally arbitrary) which can be adapted and
> populated with own nodes.
Also interesting. So instead of a site.pp file, I suppose you start the
puppetmaster by pointing at the init.pp file of the particular module?
In general, the site.pp and nodes.pp will be going away with the completion
of better node-class-mapping tools (used to be call kinial but I think Luke
changed that name).
>
> 3- I wonder why:
> class somehost_postfix inherits postfix {
> # blah blah blah
> }
>
> node somehost {
> include somehost_postfix
> }
> should not be something like:
> class postfix::somehost inherits postfix {
> # blah blah blah
> }
>
> node somehost {
> include postfix::somehost
> }
> Even if generally prefer to keep class names generic, subclass names
> related to twists on the main class (ie: postfix::mysql) and handle
> nodes differences in the class themselves (as in the example in point
> 1).
postfix::somehost only make sense if you plan to keep all your host
specific subclass in the main postfix module, which to me is
counterintuitive. We generally have modules specific to each client
(external department) and core infrastructure service and keep subclasses
specific to those grouped with those modules. So really, what we would do
is actually:
class s_ldap::postfix inherits postfix {
# blah blah blah
}
class s_ldap {
include s_ldap::postfix
}
node ldap-server inherits basenode { include s_ldap }
The problem right now is that a bug in Puppet prevents "class
s_ldap::postfix inherits postfix" from working.
>
> 4- If've not fully understood the meaning of the /service/ and the
> real necessity of the /clients/ directories. Or better, if I'd
> understood well your point, I'd place them in dedicated
> project_something modules.
Service is for modules related to core infrastructure services that we run
as the central information technology services department. Clients is for
modules related to different servers we run for external departments. We
use three module areas instead of one and wouldn't use project_* because
you lose the benefit of magic namespace mapping. It can also create
isolated pockets of puppet deployments which is counter to the reason we
adopted puppet: to ensure all our admins adhere to the same set of
practices regardless of the servers they are supporting. So, we actively
discourage sys admins from duplicating a manner in which a certain
computing server (ssh for instance) is managed.
I guess I would need to better understand how you are using project_*. If
I understand it correctly, it sounds similar to what we intend to use
environments for.
>
> 5- I Like the "ssh::disabled" logic and I think I'll follow it
>
> Thank you for your document, I think that when you approach a tool so
> flexible as puppet actually there's not the "right" way but the best
> way for own needs.
Absolutely. This document started as just the Stanford Best Practice,
which translates into "if you work at Stanford, you better be doing things
this way or I'll beat you with a stick," and was later generalized as a
guide for others. But with different sets of needs and different business
rules, everyone is going to have to come up with their own set of
practices. Really, maybe that is something we should extract from my
document and make more clear. I think what both you and I are absolutely
agreeing on is that modules should be used wherever and whenever possible.
> Nevertheless seeing as other have faced and solved same, similar or
> related problems is always very interesting and may give unexpected
> perspectives.
I'm always curious to see how others have approached their setups.
> I've used and introduced puppet in different architectures, from very
> simple to quite complex and what I've seen (which is quite obvious
> after all) is that the architecture logic shapes the puppet logic.
> I'm trying to build a set of modules that can be easily adapted to
> different architectures and I've definitvely have seen that trying to
> provide a general purpose, adaptable logic, handling differect
> architectures, distros, repositories, is much more difficult than
> expected.
It is indeed hard. I tried to use the best practices guide to get everyone
off on the same page so that it would be easier to exchange modules if
everyone took a similar approach to the architecture. But alas, that never
happened. For instance, I have doubts if your modules will be easily
usable/adaptable with our architecture. But only time will tell.
> But I would like to design a puppet logic that adapts easily and
> smoothly to different architecture logics.
>
> Just at the beginning... :-)
That's where you have to start, right? :) I'll try to find time to put
our Puppet infrastructure in depth on the web so others can compare notes.
That will likely take a few months as we're all pretty busy at the moment.
That's excellent news! We too are going heavy modules and no longer use
the master area for much of anything except the site.pp and the nodes.pp
files. The first best practice was drafted long before modules even
existed, which is why I'm glad I finally found some time to revise and
update it.
That's actually a very elegant approach. I like it!
> - different software version (this is an issue that I still haven't
> faced but surely needs a deeper look: what is the best way to handle,
> for example, different versions of apache (1.3, 2...) with not totally
> compatible configuration syntax? Differnet module names? Switch cases
> inside the apache class?)
This is also very interesting because we found that generic modules for a
service like apache was not suitable for us. In other words, we have
apache subclasses for each version of apache that builds off of a generic
apache class that handles things that are similar between the two (if any).
This is because we have very specific requirements about which apache we
want to run on which server and this is the most explicit way to declare
that. Again, it comes down to being able to look at a servers catalog and
know exactly what classes are being associated with a server and being able
to determine from the classname alone exactly what is being done, which is
why we use classes instead of variables. I can tell from class names alone
which version of apache I'm configuring on any given server. If this isn't
a requirement than a generic "handle any version" class would be more
appropriate. So it depends on the intention.