- My blink is that in 2012 Puppet is safer and more productive.
- Puppet is declarative, Chef procedural.
- Puppet brings system into compliance (state), Chef "does" things (recipes).
- Puppet has strong security practices; Chef has a toleration for loose security in Chef itself.
- Puppet makes it very hard to get "outside the lines" or violate its strong opinions; in Chef this is routine.
Not used very much, but . . .
- Doesn't depend on a puppet master (client/server bottleneck)
- Puppet docs at "Massively scalable deployments"
- See strategy at Loggly
- Declarative, not procedural.
- Feels Ruby-ish. But never think that it is Ruby.
- The fact, though, that it isn't Ruby is arguably good: One seeks for the best "Puppet"-style solution.
- Not Ruby. It has its own syntax. Lots of opportunities for screwups.
- Difficult to understand differences between fundamental resources and what goes where (e.g., classes, modules, nodes).
- Very hard to understand scopes.
- Hard to inject.
- Every puppet example I've read has been written in Puppet.
- But there is a full-blown Ruby DSL: http://puppetlabs.com/blog/ruby-dsl/ and http://projects.puppetlabs.com/projects/1/wiki/Ruby_Dsl
- A Puppet class is really something like a class, but everything inside of it "runs" as class-level behavior. There seem to be no "instances" of classes.
- This is a big deal. When you import (include?) a class, its behavior runs.
- Gotcha: There is really no such thing as a module
- It just happens that a module is a directory with a conventional pattern
- Our config has a puppet user with a local Ruby 1.8.7 and its own gems.
- The puppet user has a ~/.puppet directory; not clear if in play.
etc/
puppet/
puppet.conf
manifests/
site.pp # manifest for site
modules/
# your modules here
I put them in a module called icis::params
# /etc/puppet/modules/icis/manifests/params.pp
class icis::params {
$faye_port = $environment ? {
production => 9293,
preproduction => 9293,
staging => 9292,
training => 9294,
vagrant => 9295,
rackspace => 9296,
bluebox => 9297,
default => fail("No defined faye port for #{environment}")
}
}
Then these are injected, via the site manifest, into other modules:
# /etc/puppet/manifests/site.pp
include icis
class {'nginx':
faye_port => $icis::params::faye_port,
}
(NOTICE: class {'nginx': ... }
is essentially the same as include nginx
but the class syntax allows the injection of parameters.)
The nginx class is defined to take a parameter:
# /etc/puppet/modules/nginx/manifests/init.pp
class nginx($faye_port) {
# [much deleted]
file { "/opt/nginx/conf/sites-available/${environment}.icisapp.com.conf":
ensure => file,
content => template('nginx/environment.icisapp.com.conf.erb'),
}
# [much deleted]
}
Then the $faye_port variable is used in the erb template like this:
# /etc/puppet/modules/nginx/templates/environment.icisapp.com.conf.erb
server {
server_name faye<%= environment %>.icisapp.com;
listen 443;
location / {
proxy_pass https://localhost:<%= faye_port %>;
}
}
Ruby: "#{whatever}"
Puppet: "${whatever}"
Instead,
file {
[
"/var/www/",
"/var/www/${environment}.icisapp.com/",
"/var/www/${environment}.icisapp.com/logs/",
]:
ensure => directory,
}
Instead,
exec { 'reload':
command => '/sbin/service nginx force-reload',
refreshonly => true,
}
File['/opt/nginx/conf/nginx.conf'] -> Exec['reload']
File["/opt/nginx/conf/sites-available/${environment}.icisapp.com.conf"]
-> Exec['reload']
Example: When we have a shared resource such as nginx.conf, we may want lines in that file for each particular environment. The patterns for this in Puppet look difficult.
To get this, we created a directory called sites-available/ (like Apache), and then when extra nginx config is needed, the environment-specific file is put in place.
However, this is problematic if we remove a shared resource. Puppet is good for declarative creation of resources, but to remove a resource would imply that to build the server, you need to build all of the environments at the same time, so that sites-available/ doesn't have leftovers from old setups.
What can be "removed" in Puppet? A "package" (see http://puppetcookbook.com/posts/remove-package.html). This suggests that the ICIS "identify" is itself a package, not a module.
Few docs that explain how to write a single Puppet module that would work right across Centos, Ubuntu, etc. For example, our ICIS nginx setup makes assumptions about directory locations that probably won't work on Ubuntu. But see http://puppetcookbook.com/posts/packages-with-different-name-per-distro.html
E.g., http://semicomplete.com/presentations/puppet-presentation/puppet-at-loggly.html
How do you keep a Capistrano role (database) with a Puppet role (database)? E.g., where do migrations run? Should Capistrano look up role assignments from somewhere else?
Learning Puppet: http://docs.puppetlabs.com/learning/ Language: http://docs.puppetlabs.com/guides/language_guide.html Types: http://docs.puppetlabs.com/references/latest/type.html Cheat sheet: referenced from: http://puppetlabs.com/blog/learning-puppet-and-cheat-sheets/
- http://www.allgoodbits.org/articles/view/33
- http://puppetcookbook.com/
- http://ro.stanford.edu/~llao/puppet/puppet-trainning.html
- http://www.engineyard.com/blog/2011/why-puppet-should-manage-your-infrastructure/ (compare http://www.engineyard.com/blog/2011/why-chef-should-manage-deploying-your-application/)
- http://www.slideshare.net/PuppetLabs/scalable-systems-management-with-puppet (helpful if we want to implement client/server)
Wow, thanks for sharing this!