The last couple of months I’ve been working on building a new Puppetmaster for our network. The running setup is filled with leftovers from when the infrastructure was bootstrapped one-and-a-half year ago, before there was any notion of modules or environments. I’ve tried to convert my existing setup to use modules and environments, but it’s really hard.
So I’ve actually chosen to build a new setup parallel to the existing one, and slowly convert what I have to the new setup. I’ve also chosen to go with another solution that the Apache/Mongrel based one I’m running now. Recently Christian Hofstaedtler made it possible to use Phusion Passenger together with Puppet, and I wanted to use that in my new setup. I also wanted to make a shift from Mercurial to Git, for reasons I won’t delve into here.
So, even though it’s not used that much, you can use the “puppet” executable to run puppet manifests, without having a puppetmaster. It’s not all manifests that you’re able to use that way, but constructed the right way, it’s possible. So I chose to write a puppet-bootstrap manifest, which I will present in this blogpost.
I opted for dividing it up into 3 classes: One for installing and puppetmaster, apache, passenger and all supporting software, and make sure that the right directories, configfiles and services was in place. Then one for setting up a server Git repo and lastly one for setting up our admin users so it’s possible for them to connect to the server, and publish the complete puppet catalog through Git.
Class puppetbootstrap
My puppetbootstrap class ended up looking like this:
# Install puppetmaster, apache2 and Phusion Passenger # Set up the directories, configure apache and Passenger # and ensure that the apache service is running class puppetbootstrap { package { [ "apache2", "puppetmaster", "rubygems", "apache2-threaded-dev", "ruby1.8-dev", "make", "gcc", "g++" ]: ensure => "installed"; "rack": ensure => "0.4.0", provider => "gem", require => Package["rubygems"]; "passenger": ensure => "installed", provider => "gem", require => [ Package["rubygems"], Package["apache2-threaded-dev"], Package["ruby1.8-dev"], Package["make"], Package["gcc"] ]; } exec { "Enable-a2-module-ssl": command => "a2enmod ssl", require => Package["apache2"]; "Enable-a2-module-headers": command => "a2enmod headers", require => Package["apache2"]; "Disable-default-website": command => "a2dissite 000-default", require => Package["apache2"]; } file { [ "/srv/www", "/srv/www/${fqdn}", "/srv/www/${fqdn}/public", "/srv/www/${fqdn}/tmp" ]: ensure => "directory", require => [ Package["rack"], Package["puppetmaster"] ], } file { "/etc/apache2/conf.d/${fqdn}.conf": source => "file:///root/apache.conf", require => Package["apache2"], notify => Service["apache2"], } file { "/srv/www/${fqdn}/config.ru": source => "file:///root/config.ru", owner => "puppet", group => "puppet", require => Package["rack"], notify => Service["apache2"], } service { "apache2": ensure => "running", enable => "true", hasstatus => "false" } }
Class gitrepo
My gitrepo class is simple:
# Setting up the top dir for the git # repos class gitrepo { package { "git-core": ensure => "installed", } group { "puppeteers": ensure => "present", } file { "/srv/gitrepos": ensure => "directory", owner => "root", mode => "775", group => "puppeteers", require => Group["puppeteers"], } }
Class admins
And lastly my admins class:
class admins { group { [ "adm", "adminuser" ]: ensure => "present", } user { "adminuser": ensure => "present", home => "/home/adminuser", managehome => "true", gid => "adminuser", shell => "/bin/bash", groups => [ "adm", "puppeteers" ], require => [ Group["puppeteers"], Group["adm"] ], } file { "/home/adminuser/.ssh": ensure => "directory", mode => "700", owner => "adminuser", group => "adminuser", require => User["adminuser"], } ssh_authorized_key { "adminuser@laptop": ensure => "present", key => "AAAA....", target => "/home/adminuser/.ssh/authorized_keys", user => "adminuser", type => "ssh-rsa", require => Group["adminuser"], } }
Now I have put all these classes into one file called setup.pp, and included the classes like this:
include puppetbootstrap include gitrepo include admins
And to ensure that exec is behaving correctly:
# Set some sane defaults for Exec Exec { path => "/usr/bin:/usr/sbin:/bin:/sbin:/usr/local/bin:/usr/local/sbin" }
Now to make this work I only need two configfiles, that Christian already has provided on the Puppet wiki, but I’ll provide them here in my adapted form:
config.ru
# Author: Christian Hofstaedtler <hofstaedtler@inqnet.at> # Copyright (c) 2007 Luke Kanies, 2008 Christian Hofstaedtler # # This file is mostly based on puppetmasterd, which is part of # the standard puppet distribution. require 'rack' require 'puppet' require 'puppet/network/http_server/rack' # startup code stolen from bin/puppetmasterd Puppet.parse_config Puppet::Util::Log.level = :info Puppet::Util::Log.newdestination(:syslog) # A temporary solution, to at least make the master work for now. Puppet::Node::Facts.terminus_class = :yaml # Cache our nodes in yaml. Currently not configurable. Puppet::Node.cache_class = :yaml # The list of handlers running inside this puppetmaster handlers = { :Status => {}, :FileServer => {}, :Master => {}, :CA => {}, :FileBucket => {}, :Report => {} } # Fire up the Rack-Server instance server = Puppet::Network::HTTPServer::Rack.new(handlers) # prepare the rack app app = proc do |env| server.process(env) end # Go. run app
I haven’t edited that file in any way.
apache.conf
And finally the config file for Apache:
Listen 8140 <VirtualHost *:8140> LoadModule passenger_module /var/lib/gems/1.8/gems/passenger-2.0.3/ext/apache2/mod_passenger.so PassengerRoot /var/lib/gems/1.8/gems/passenger-2.0.3 PassengerRuby /usr/bin/ruby1.8 SSLEngine on SSLCipherSuite SSLv2:-LOW:-EXPORT:RC4+RSA SSLCertificateFile /var/lib/puppet/ssl/certs/puppet.example.com.pem SSLCertificateKeyFile /var/lib/puppet/ssl/private_keys/puppet.example.com.pem SSLCertificateChainFile /var/lib/puppet/ssl/ca/ca_crt.pem SSLCACertificateFile /var/lib/puppet/ssl/ca/ca_crt.pem # CRL checking should be enabled; if you have problems with Apache complaining about the CRL, disable the next line SSLCARevocationFile /var/lib/puppet/ssl/ca/ca_crl.pem SSLVerifyClient optional SSLVerifyDepth 1 SSLOptions +StdEnvVars # The following client headers allow the same configuration to work with Pound. RequestHeader set X-SSL-Subject %{SSL_CLIENT_S_DN}e RequestHeader set X-Client-DN %{SSL_CLIENT_S_DN}e RequestHeader set X-Client-Verify %{SSL_CLIENT_VERIFY}e RackAutoDetect On DocumentRoot /srv/www/puppet.example.com/public/ <Directory /srv/www/puppet.example.com> Options None AllowOverride None Order allow,deny allow from all </Directory> </VirtualHost>
And that should be it. Now all I did was running
puppet --verbose setup.pp
and watch puppet “do the right thing”. The only thing I couldn’t automate, was running “passenger-install-apache2-module” – you have to do that manually.
Feel free to make any comments.
ShareNOV
2008
About the Author:
Juri er en af de tre stiftere i Fab:IT. Han har beskæftiget sig med computere siden 1990, Linux siden 1998 og startede sin første virksomhed i 1999.