<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Brian L. Troutwine: A Fairly Okay Fellow</title>
  <id>http://blog.troutwine.us/</id>
  <link href="http://blog.troutwine.us/"/>
  <link href="http://blog.troutwine.us/feed.xml" rel="self"/>
  <updated>2012-04-29T00:00:00-05:00</updated>
  <author>
    <name>Brian L. Troutwine</name>
  </author>
  <icon>http://blog.troutwine.us/images/favicon.png</icon>
  <entry>
    <title>Ternary Search Trie in Erlang</title>
    <link rel="alternate" href="/2012/04/29/tenary_search_trie.html"/>
    <id>/2012/04/29/tenary_search_trie.html</id>
    <published>2012-04-29T00:00:00-05:00</published>
    <updated>2012-04-29T00:00:00-05:00</updated>
    <author>
      <name>Brian L. Troutwine</name>
    </author>
    <summary type="html">&lt;p&gt;The last few months I&amp;#8217;ve not done a terrible amount hobby programming; I spent much of the time interviewing (successfully!) at Rackspace and, since arriving in San Antonio, have been busy learning the team code-base. There were a &lt;em&gt;few&lt;/em&gt; hobby projects I finished during February-April period, the one I&amp;#8217;m most pleased&lt;/p&gt;</summary>
    <content type="html">&lt;p&gt;The last few months I&amp;#8217;ve not done a terrible amount hobby programming; I spent much of the time interviewing (successfully!) at Rackspace and, since arriving in San Antonio, have been busy learning the team code-base. There were a &lt;em&gt;few&lt;/em&gt; hobby projects I finished during February-April period, the one I&amp;#8217;m most pleased with is a &lt;a href='http://en.wikipedia.org/wiki/Ternary_search_tree'&gt;ternary search trie&lt;/a&gt;, a data-structure described by Bentley and Sedgewick in their 1996 paper &lt;a href='http://www.cs.tufts.edu/~nr/comp150fp/archive/bob-sedgewick/fast-strings.pdf'&gt;Fast Algorithms for Searching and Sorting Strings&lt;/a&gt;, among other places.&lt;/p&gt;

&lt;p&gt;I wrote an implementation from the paper with the intention of writing a Scrabble bot around it, but I&amp;#8217;ll probably not finish that project. I&amp;#8217;m releasing the code, extracted from the Scrabble bot under the MIT license. Find it on &lt;a href='https://github.com/blt/tst'&gt;Github&lt;/a&gt;. Novel features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;extensively typed&lt;/li&gt;

&lt;li&gt;uses &lt;a href='https://github.com/manopapad/proper'&gt;proper&lt;/a&gt; to test algorithm properties&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;works only on strings, as typed&lt;/li&gt;

&lt;li&gt;never stress tested nor optimized for performance&lt;/li&gt;

&lt;li&gt;the work of idle hands, not necessarily careful&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Patches welcome!&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>LOVE Maze</title>
    <link rel="alternate" href="/2012/02/26/love2d_maze.html"/>
    <id>/2012/02/26/love2d_maze.html</id>
    <published>2012-02-26T00:00:00-06:00</published>
    <updated>2012-02-26T00:00:00-06:00</updated>
    <author>
      <name>Brian L. Troutwine</name>
    </author>
    <summary type="html">&lt;p&gt;I find great joy in programming. Mostly, I make tools, to solve perceived problems or, my favorite, to ease burdens seen otherwise as only natural. It&amp;#8217;s a joy informed by a stubborn implicit reductionism in my thinking: any system can be analyzed, faults isolated and then repaired or mitigated without inherently&lt;/p&gt;</summary>
    <content type="html">&lt;p&gt;I find great joy in programming. Mostly, I make tools, to solve perceived problems or, my favorite, to ease burdens seen otherwise as only natural. It&amp;#8217;s a joy informed by a stubborn implicit reductionism in my thinking: any system can be analyzed, faults isolated and then repaired or mitigated without inherently changing the system. Trivial counterexamples abound for almost any system you might imagine: laws that carry perverse incentives and reduce, rather that lift up, the behaviors of a society; empirical observations that become a myopic end unto themselves, driving innovation toward the numbers rather than toward the initial problem; a &lt;a href='http://docs.puppetlabs.com/learning/'&gt;state management daemon&lt;/a&gt; that does not guarantee deterministic ordering of events, injecting the unexpected into what was previously perceived as a well-ordered environment.&lt;/p&gt;

&lt;p&gt;Emergent phenomena can be real tough to combat; crafting tools that create problems even as they solve others can be a heartbreaking experience.&lt;/p&gt;

&lt;p&gt;Still, there&amp;#8217;s the joy in programming&amp;#8211;even with the heartbreak&amp;#8211;and I occasionally find it necessary to strike off on a new path, usually one of complete frivolity, to have delight without grief. To that end, I made a maze running game in &lt;a href='https://love2d.org/'&gt;LOVE&lt;/a&gt; in a kind of a mad rush on Saturday. I knew neither lua nor love2d at the start, all the better as I&amp;#8217;d been meaning to at least become familiar with both.&lt;/p&gt;

&lt;p&gt;I&amp;#8217;m impressed with lua&amp;#8217;s minimal design, but it goes rather too far. The most standout issue is not nearly the worst sticks in my mind nonetheless:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;string.format cannot format all of lua&amp;#8217;s provided types, by default&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How do you throw a boolean value into string.format? You have to redefine string.format, that&amp;#8217;s how. It seems to me that, at a minimum, a language should have the facility of string formatting its base types. I found the lack of bitwise operations frustrating&amp;#8211;though there are various C libraries available and lua 5.2 ships with one natively&amp;#8211;and tables could use more operations by default. Note &amp;#8216;rand_key&amp;#8217; is pretty ugly as lua can&amp;#8217;t tally up non-numerical keys in a table &lt;code&gt;wall[keys] = nil&lt;/code&gt; exists in place of a &lt;code&gt;table.remove&lt;/code&gt; for non-numerical keys.&lt;/p&gt;

&lt;p&gt;I am looking forward to using lua from C projects as glue. Lua&amp;#8217;s C API is a far sight better than CPython&amp;#8217;s&amp;#8211;which I&amp;#8217;ve traditionally used a a glue language&amp;#8211;and all of the non-optimal implementation choices made in Lua can easily be addressed in C. I&amp;#8217;ll need to figure out how to compile native code alongside a LOVE project.&lt;/p&gt;

&lt;p&gt;Speaking of which, I have nothing but exuberant enthusiasm for LOVE2d. It was nothing other than an absolute pleasure to work with and the community behind it answered my 0.7.1 bug-related questions with what turns out to be a characteristic irreverence and sincere warmth to folks that just happen to wander in.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;You can find the source code for &amp;#8216;maze&amp;#8217; at &lt;a href='http://github.com/blt/maze'&gt;github&lt;/a&gt; or just below this sentence. Patches welcome!&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;
-- A maze runner.
--
-- This program is a maze running game. There's not death, no time limits and
-- nothing in the way of story. Go from the green square to the red, little
-- golden square!
--
-- Inspired by https://love2d.org/wiki/Tutorial:Gridlocked_Player
--
-- Developed with love 0.8.0, straight out of bitbucket tip.


-- Pre-define some colors. Love doesn't have any built in and it's rather nice
-- to refer to color names.
white = { 255, 255, 255, 255 }
grey = { 128, 128, 128, 255 }
red = { 255, 0, 0, 255 }
green = { 0, 255, 0, 255 }
blue = { 0, 0, 255, 255 }
gold = { 255, 215, 0, 255 }

-- Defines the dimensions of the world. When possible, we refer only to grid
-- numbers, but love2d's calls require pixel values. base_size refers to the
-- width and height of one grid cell.
x_grid_max = 130
y_grid_max = 99
base_size = 8
width  = base_size*(x_grid_max+1)
height = base_size*(y_grid_max+1)
love.graphics.setMode( width, height )

-- All cells are one of three types, only OPEN is passable by the player and the
-- maze carving algorithm. The maze is surrounded by a 'moat' of water, which is
-- really just a hack to make the mathematics of this simplistic. No edge cases.
WATER = 2
WALL = 1
OPEN = 0

--
-- Framework Functions
--

-- love.load, well, loads all of the preliminary data for the program. 'player'
-- is what you might expect, 'maze' the object (is that the right lua term) that
-- holds the position of the 'start' and 'exit' squares and 'map' which is the
-- world in which the player will move.
--
-- I also set the random seed based on current time. Bit of a gripe: os.time
-- returns in millisecond range, meaning the random seed isn't going to be that
-- great.
--
function love.load()
   player = {
      grid_x = 1,
      grid_y = 1,
   }
   maze = {
      ["exit"] = {
         grid_x = x_grid_max - 1,
         grid_y = y_grid_max - 1
      },
      ["start"] = {
         grid_x = 1,
         grid_y = 1
      }
   }

   time = os.time()
   math.randomseed( time )

   map = generate_maze()
end

-- love.draw updates the screen for every tick. We first layer in the maze
-- itself from 'map', then drop in the exit, start and player squares. The
-- player is a tasteful gold, but not dangerous like Midas.
function love.draw()
   -- the maze
   for x=0, x_grid_max do
      for y=0, y_grid_max do
         if map[y][x] == OPEN then
            love.graphics.setColor( white )
            love.graphics.rectangle("fill", x * base_size, y * base_size, base_size, base_size)
         elseif map[y][x] == WALL then
            love.graphics.setColor( grey )
            love.graphics.rectangle("line", x * base_size, y * base_size, base_size, base_size)
         elseif map[y][x] == WATER then
            love.graphics.setColor( blue )
            love.graphics.rectangle("fill", x * base_size, y * base_size, base_size, base_size)
         end
      end
   end

   -- the exit
   love.graphics.setColor( red )
   love.graphics.rectangle("fill", maze.exit.grid_x*base_size, maze.exit.grid_y*base_size, base_size, base_size)

   -- the start
   love.graphics.setColor( green )
   love.graphics.rectangle("fill", maze.start.grid_x*base_size, maze.start.grid_y*base_size, base_size, base_size)

   -- the player
   love.graphics.setColor( gold )
   love.graphics.rectangle("fill", player.grid_x*base_size, player.grid_y*base_size, base_size, base_size)
end

-- love.keypressed handles inputs per tick; I handle only movement and
-- escaping. That lua doesn't have a switch statement is somewhat irking to me,
-- but I suppose love2d is meant for prototypes? I'm certainly inexperienced
-- with both the language and the library. I _think_ love.draw consumes a
-- powerful amount of CPU in re-drawing the maze per tick.
function love.keypressed(key)
   if key == "up" then
      if collide(-1, 0) then
         player.grid_y = player.grid_y - 1
      end
   elseif key == "down" then
      if collide(1, 0) then
         player.grid_y = player.grid_y + 1
      end
   elseif key == "left" then
      if collide(0, -1) then
         player.grid_x = player.grid_x - 1
      end
   elseif key == "right" then
      if collide(0, 1) then
         player.grid_x = player.grid_x + 1
      end
   elseif key == 'escape' then
      love.event.push('quit')
   end
end

--
-- Internal Functions
--

-- generate_maze does what you might think. The algorithm is something like
-- Prim's.
function generate_maze()
   -- fill map entirely
   map = {}
   for i=0, y_grid_max do
      map[i] = {}
      for j=0, x_grid_max do
         map[i][j] = WALL
      end
   end

   -- build the moat
   for i=0, y_grid_max do
      for j=0, x_grid_max do
         map[0][j] = WATER
         map[y_grid_max][j] = WATER
      end
      map[i][x_grid_max] = WATER
      map[i][0] = WATER
   end

   -- craft the maze
   map[maze.start.grid_y][maze.start.grid_x] = OPEN --mark the entrance

   ---- walls contains those positions known to be walls. The function index is
   ---- a hash of the (y,x) coordinates. The algorithm will strip one wall
   ---- randomly out of walls, possibly mark it as open space and, possibly, add
   ---- the neighbor cells into 'walls'.
   walls = {
      ["0102"] = { y=1, x=2 },
      ["0201"] = { y=2, x=1 }
   }
   seen =  { ["0101"] = { x=1, y=1 } }
   while next(walls) ~= nil do
      key = rand_key(walls)

      wall = walls[key]
      walls[key] = nil
      seen[key] = wall

      y = wall.y
      x = wall.x


      north      = is_open(map, y-1, x)
      south      = is_open(map, y+1, x)
      west       = is_open(map, y,   x-1)
      east       = is_open(map, y,   x+1)

      -- Directions are named in terms of the cardinal directions. Up and down
      -- the Y-axis is N/S, left and right on the X-axis is W/E. is_center
      -- asserts that any new OPEN space cannot join some tunnels, although it
      -- does not disallow diagonal OPEN cells, which I dislike the look of.
      is_center = (north and south) or (north and west) or (north and east) or
                  (south and west) or (south and east) or (east and west)

      if not is_center then
         map[y][x] = OPEN
         add_wall(walls, seen, map, y-1, x) -- north
         add_wall(walls, seen, map, y+1, x) -- south
         add_wall(walls, seen, map, y, x-1) -- east
         add_wall(walls, seen, map, y, x+1) -- west
      end
   end

   -- search for the exit
   for i=1, y_grid_max-1 do
      if map[i][x_grid_max-1] == OPEN then
         maze.exit.grid_y = i
      end
   end
   return map
end

-- Possibly add the wall at (y,x) into 'walls', unless it is not a WALL or is in
-- 'seen', meaning we've already ruled it out as a candidate to go OPEN.
function add_wall(walls, seen, map, y, x)
   key = string.format("%.2d%.2d", y, x)

   if (map[y][x] == WALL) and (seen[key] == nil) then
      walls[key] = { y=y, x=x }
   end
end

-- This seems like a bitterly ugly hack: pull a random key out of a given table.
function rand_key(hash)
   ks = {}
   for k,v in pairs(hash) do table.insert(ks, k) end
   return ks[math.random(1, #ks)]
end

-- Determines if a given cell is, indeed, OPEN.
function is_open(map, y, x)
   if map[y][x] == OPEN then
      return true
   else
      return false
   end
end

-- Has the player hit somthing? This function performs the check.
function collide(y, x)
   if map[player.grid_y + y][player.grid_x + x] ~= OPEN then
      return false
   end
   return true
end
&lt;/code&gt;
&lt;/pre&gt;</content>
  </entry>
  <entry>
    <title>Wrangling Servers -- A Proper Foundation</title>
    <link rel="alternate" href="/2012/02/22/wrangling_servers_a_proper_foundation.html"/>
    <id>/2012/02/22/wrangling_servers_a_proper_foundation.html</id>
    <published>2012-02-22T00:00:00-06:00</published>
    <updated>2012-02-22T00:00:00-06:00</updated>
    <author>
      <name>Brian L. Troutwine</name>
    </author>
    <summary type="html">&lt;p&gt;This is the second in a series of articles that walk-through setting up and maintaining a kit sufficient to run a tech business on. Please make yourself familiar with the &lt;a href='../../01/22/wrangling_servers_introduction_and_preliminaries.html'&gt;first article&lt;/a&gt;, in which a base image box was setup and a puppet central master box was derived from the base.&lt;/p&gt;

&lt;p&gt;In this article we&amp;#8217;ll make puppet self-hosting, properly version-control our puppet configuration and put together a push-style deployment system for every manner of source code.&lt;/p&gt;

&lt;p&gt;As the deployment system is best put in place in the context of a reasonable puppet setup we&amp;#8217;ll be chicken-egging it here for a short bit, but, don&amp;#8217;t worry, there&amp;#8217;s much less work in this second piece than the first.&lt;/p&gt;</summary>
    <content type="html">&lt;p&gt;This is the second in a series of articles that walk-through setting up and maintaining a kit sufficient to run a tech business on. Please make yourself familiar with the &lt;a href='../../01/22/wrangling_servers_introduction_and_preliminaries.html'&gt;first article&lt;/a&gt;, in which a base image box was setup and a puppet central master box was derived from the base.&lt;/p&gt;

&lt;p&gt;In this article we&amp;#8217;ll make puppet self-hosting, properly version-control our puppet configuration and put together a push-style deployment system for every manner of source code.&lt;/p&gt;

&lt;p&gt;As the deployment system is best put in place in the context of a reasonable puppet setup we&amp;#8217;ll be chicken-egging it here for a short bit, but, don&amp;#8217;t worry, there&amp;#8217;s much less work in this second piece than the first.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;h1 id='puppet_hosting_puppet'&gt;Puppet hosting Puppet&lt;/h1&gt;

&lt;p&gt;You&amp;#8217;ll recall that in our last article our two files in /etc/puppet were not version controlled. Make sure you aren&amp;#8217;t working on the puppet master and create a new git repository:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ mkdir -p ~/projects/us/troutwine/ops/etcpuppet
$ cd ~/projects/us/troutwine/ops/etcpuppet&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I like to keep my projects namespaced by domain, then by departmental affiliation&amp;#8211;even if I&amp;#8217;m the only one working on that domain&amp;#8217;s IP. The exact path you supply is probably going to differ. Once in &lt;code&gt;etcpuppet/&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ git init
Initialized empty Git repository in ~/projects/us/troutwine/ops/etcpuppet/.git/&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Create in this directory three files. The first file is &lt;code&gt;config.ru&lt;/code&gt; and serves as the instruction set for the thin web-server that powers puppet master.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ cat config.ru
# a config.ru, for use with every rack-compatible webserver.
# SSL needs to be handled outside this, though.

# if puppet is not in your RUBYLIB:
# $:.unshift(&amp;#39;/opt/puppet/lib&amp;#39;)

$0 = &amp;quot;master&amp;quot;

# if you want debugging:
# ARGV &amp;lt;&amp;lt; &amp;quot;--debug&amp;quot;

ARGV &amp;lt;&amp;lt; &amp;quot;--rack&amp;quot;
require &amp;#39;puppet/application/master&amp;#39;
# we&amp;#39;re usually running inside a Rack::Builder.new {} block,
# therefore we need to call run *here*.
run Puppet::Application[:master].run&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Our second file acts as configuration for the puppet master itself.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ cat puppet.conf
[main]
ssldir=$vardir/ssl

[master]
certname=puppet&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;These files you should recall from the first article.&lt;/p&gt;

&lt;p&gt;With no deployment rig in place, we&amp;#8217;ll begin by muddling through and relay rsyncing code into place by hand, gradually elaborating on the process up to the final method. I&amp;#8217;m aware of some folks that rsync their configuration directly into place&amp;#8211;from developer machine to production system&amp;#8211;but I resist doing that for a few reasons. Firstly, if /etc/puppet is to be the location of puppet configuration &lt;em&gt;and&lt;/em&gt; user/group puppet is to own this directory the user puppet must be granted remote login access. The puppet user &lt;em&gt;should not&lt;/em&gt; be granted any manner of login access because the data it owns is used, without suspicion, to manipulate all the systems in your cluster: any possible breach of the puppet user account by a remote party would be disastrous. Secondly, to mitigate any possible breeches of the puppet user, our puppet configuration will be deployed on a read-only filesystem. Any attacker that &lt;em&gt;might&lt;/em&gt; gain access to the puppet user will be unable to alter data owned by that user: the R/O filesystem will be mounted by root and unalterable except by the root user. (Other attack vectors exist; we&amp;#8217;ll address these later through network design.)&lt;/p&gt;

&lt;p&gt;First, rsync the configuration codebase to your puppet box. Recall that I&amp;#8217;m running a virtual box instance on a host-only network; I have the localhost puppet IP alias as localpuppet in &lt;code&gt;/etc/hosts&lt;/code&gt; and I&amp;#8217;ve an ssh-key accessible account on the puppet master box.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ rsync -vzr --delete --exclude=&amp;#39;.git&amp;#39; -e ssh . localpuppet:etcpuppet
sending incremental file list
created directory /home/blt/etcpuppet
./
config.ru
puppet.conf
manifests/
manifests/.gitkeepme

sent 595 bytes  received 76 bytes  1342.00 bytes/sec
total size is 479  speedup is 0.71&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;On the puppet host:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;puppet:~$ sudo rsync -vzr --delete --exclude=&amp;#39;.git&amp;#39; etcpuppet/ /etc/puppet/
sending incremental file list

sent 599 bytes  received 76 bytes  1350.00 bytes/sec
total size is 479  speedup is 0.71&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Nothing should be synced into &lt;code&gt;/etc/puppet&lt;/code&gt; as there are no files modified from the previous article. What we&amp;#8217;re going to need now are puppet modules to host puppet itself. This very task is the over-saturated &amp;#8216;Hello World&amp;#8217; of puppet, to the exclusion of other, more interesting, works. Here&amp;#8217;s what I&amp;#8217;m going to do: in this article we&amp;#8217;ll plug submodules into the repository we&amp;#8217;ve created and edit a file or two. Bam: self-hosting puppet. If you&amp;#8217;re interested in the details or if you need the tutorial, dive into the source of the submodules introduced. Those I&amp;#8217;ve written are documented sufficiently to act as a tutorial for the determined.&lt;/p&gt;

&lt;p&gt;Before we begin adding modules we&amp;#8217;re going to need some boilerplating. The &lt;code&gt;manifests/site.pp&lt;/code&gt; is puppet&amp;#8217;s main method, so to speak: everything included in this file is all that is available to a puppet agent.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;import &amp;#39;nodes.pp&amp;#39;

Exec {
  path =&amp;gt; &amp;quot;/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin&amp;quot;,
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The first line pulls in all of our node definitions. In puppet, a &amp;#8216;node&amp;#8217; is a machine type. Exact demarcation is left up to the user but determined by machine hostname. We&amp;#8217;ll use a mixture of &amp;#8216;type&amp;#8217; and &amp;#8216;typeNumber&amp;#8217;, &amp;#8216;puppet&amp;#8217; and &amp;#8216;db0&amp;#8217; being exemplars of both methods, respectively. More of that shortly. The second block sets the shell path for all command invocations. It&amp;#8217;s not strictly necessary to set this&amp;#8211;puppet can sometimes figure out your path from the host system&amp;#8211;but it &lt;em&gt;is&lt;/em&gt; better to be explicit when possible, else all subsequent Execs will require a &lt;code&gt;path&lt;/code&gt; line. That&amp;#8217;s annoying and bug prone. Read more &lt;a href='http://puppetcookbook.com/posts/set-global-exec-path.html'&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Our next plate for the boiler is &lt;code&gt;manifests/nodes.pp&lt;/code&gt;; the import at the top of &lt;code&gt;site.pp&lt;/code&gt; is a relative one. You&amp;#8217;ll find that puppet&amp;#8217;s inclusion rules are a little strange. There&amp;#8217;s a difference, oh yes, between the keywword &lt;a href='http://docs.puppetlabs.com/guides/language_guide.html#importing-manifests'&gt;&amp;#8216;import&amp;#8217;&lt;/a&gt;, which you should almost never use, and &lt;a href='http://docs.puppetlabs.com/guides/modules.html#module-autoloading'&gt;&amp;#8216;include&amp;#8217;&lt;/a&gt;, which you&amp;#8217;ll use quite a bit. &lt;em&gt;Please&lt;/em&gt; read the two documents I&amp;#8217;ve just linked and make sure you understand them; while the puppet language has some warts they are, at least, &lt;em&gt;well documented&lt;/em&gt; warts. &lt;code&gt;manifests/nodes.pp&lt;/code&gt; will act as a base of and import all specific node definitions.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;import &amp;#39;nodes/*.pp&amp;#39;

# The base class acts as a repository for configuration common to all the more
# specific node definintions imported above.
class base {
  include supervisor, puppet

  # Ensure machine time is always synced to external reference. All system
  # defaults are acceptable.
  package { ntp: ensure =&amp;gt; present, }
  # Include for libssl-dev for native rubygems that need SSL (EventMachine).
  package { &amp;#39;libssl-dev&amp;#39;: ensure =&amp;gt; present, }
  # Require all machines to have rsync installed for various purposes.
  package { rsync: ensure =&amp;gt; present, }

}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The only node definition we&amp;#8217;ve got at the moment is for the puppet node, &lt;code&gt;manifests/nodes/puppet.pp&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;node &amp;#39;puppet&amp;#39; {
  include base, puppet::master
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That&amp;#8217;s everything. All that&amp;#8217;s needed now is to install the submodules referenced and their dependencies. From the root of your repository:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ git submodule add git://github.com/blt/puppet-module-supervisor.git modules/supervisor
$ git submodule add git://github.com/blt/puppet-apt.git modules/apt
$ git submodule add git://github.com/blt/puppet-nginx.git modules/nginx
$ git submodule add git://github.com/blt/puppet-puppet.git modules/puppet&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Deploy as above, execute &lt;code&gt;puppet agent --test&lt;/code&gt; and commit. That&amp;#8217;s it. I urge you to have a look through the modules&amp;#8217; source; I&amp;#8217;ve worked to comment the modules well and you really do need to have a handle on everything. Note especially the nginx module: you&amp;#8217;ll find many that attempt to use puppet&amp;#8217;s limited language to compile vhost definitions into nginx&amp;#8217;s more, hmm, &lt;em&gt;vigorous&lt;/em&gt; configuration language. I believe this is an exercise in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;complication&lt;/li&gt;

&lt;li&gt;frustration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;neither of which you want when there&amp;#8217;s something needs fixing, the site&amp;#8217;s offline and you&amp;#8217;re caught in the lurch between the dainty needs of puppet and unhappy folks. The vhost resource in the nginx module will take care of the hidden details of the nginx module but require that you pass in a template as content for the vhost. That way you get the full acrobatics of nginx without any of the heartache of mimicking that in puppet.&lt;/p&gt;

&lt;p&gt;Now that puppet is self-hosting, you should commit all of this to version control, finally.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ git add * &amp;amp;&amp;amp; git commit -m &amp;quot;Initial commit&amp;quot;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Congratulations: your puppet configuration is now checked into version control. Add any remote repositories you might care to and push your code up and away to Github or some other reasonably disaster-proof repository host. I tend to use the remote branch name &amp;#8216;backup&amp;#8217; for this need and reserve &amp;#8216;production&amp;#8217; for the fork that will be deployed to live puppet master.&lt;/p&gt;

&lt;h2 id='deployment'&gt;Deployment&lt;/h2&gt;

&lt;p&gt;Using rsync is passable when we&amp;#8217;re developing but it&amp;#8217;s not going to cut it long-term. (You can get pretty elaborate with rsync, though. I&amp;#8217;m fond of daisy-chaining rsyncs across machines, spinning up as needed with inotifywait.) The deployment strategy we&amp;#8217;re going to build requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A central git repository for &amp;#8216;blessed&amp;#8217; production / staging / testing repos.&lt;/li&gt;

&lt;li&gt;A message bus.&lt;/li&gt;

&lt;li&gt;A message to command the invocation server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The two options that I&amp;#8217;d like to make you aware of for this last point are &lt;a href='http://puppetlabs.com/mcollective/introduction/'&gt;mcollective&lt;/a&gt; and my &lt;a href='https://github.com/blt/traut'&gt;traut&lt;/a&gt;. Mcollective has company backing, scales way up and is a pseudo-interactive shell to all nodes in the collective. You can do &lt;em&gt;very&lt;/em&gt; cool stuff with mcollective. Alternatively, traut is a small open-source project, has no company backing and is really just a cron-like for messaging. You write your commands in any language that will take stdin, unlike mcollective which is a ruby only party. Traut is also &lt;em&gt;stupid&lt;/em&gt; simple to get running and keep that way.&lt;/p&gt;

&lt;p&gt;If you know cron, you know traut. Mcollective is &lt;em&gt;much&lt;/em&gt; larger project that you should &lt;em&gt;probably&lt;/em&gt; transition to when your business takes off, your ops team is funded or you have a bit of time on your hands. Once you have more than twenty computers in your cluster&amp;#8211;or if you find yourself ssh&amp;#8217;ing into your machines constantly&amp;#8211;put some time into learning mcollective.&lt;/p&gt;

&lt;p&gt;Till then, deployment works like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A user pushes code into a blessed code repository, the post-receive hook is invoked and sends a specially formed message through the bus.&lt;/li&gt;

&lt;li&gt;The post-receive hook&amp;#8217;s message is caught by a traut script which builds a deployable slug, placing it in a remotely accessible directory on the filesystem. This slug building script then fires off a notification message through the bus.&lt;/li&gt;

&lt;li&gt;Traut clients, pre-configured to invoke a command for the notification message, download the new slug and deploy it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The slug building hook that I&amp;#8217;ll introduce here is primitive: it simple-mindedly strips out git metadata and bundles all the source-code into a single squashfs file. This is great for puppet configurations, less so for complex applications. Adding heuristic running of a project&amp;#8217;s Makefile, Rakefile, ant or another script is not at all difficult; I just didn&amp;#8217;t get around to it. If this becomes a problem for you and I&amp;#8217;ve not fixed it up between writing this and your involvement, &lt;a href='https://github.com/blt/puppet-slugbuild/issues'&gt;take out an issue&lt;/a&gt;?&lt;/p&gt;

&lt;h3 id='a_central_git_repository'&gt;A central git repository&lt;/h3&gt;

&lt;p&gt;Go ahead and fire up another machine, call it &amp;#8216;git&amp;#8217;. This machine will host the &amp;#8216;blessed&amp;#8217; repositories using &lt;a href='https://github.com/sitaramc/gitolite'&gt;gitolite&lt;/a&gt; to provide access control. A word of warning, the manner in which gitolite is configured makes it difficult to fully automate: doing commits into gitolites&amp;#8217; &lt;code&gt;gitolite-admin&lt;/code&gt; repository is pretty slick, but I&amp;#8217;m not clever enough to use puppet with that. (If you are, email me!)&lt;/p&gt;

&lt;p&gt;Once the machine is ready, the first thing you&amp;#8217;re going to need to do is get the git box&amp;#8217;s puppet agent keyed into puppet master. To this point I&amp;#8217;ve ignored networking. Perhaps you&amp;#8217;re in an environment with DNS pre-baked. If not, I&amp;#8217;ll cover setting up DNS in a later article. For now, and this is not a &lt;em&gt;bad&lt;/em&gt; solution, consider setting IPs statically in /etc/hosts. Until I actually write the DNS article, that&amp;#8217;s what I&amp;#8217;ll be doing. Be sure that the hostname &lt;code&gt;puppet&lt;/code&gt; points to your puppet master box.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;git:~# puppet agent --test  --waitforcert 30 --server puppet&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now, switch back to the puppet master&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;puppet:~# puppet cert list
  git.troutwine.us (8E:A8:C0:03:9D:53:1A:CA:FC:25:16:82:88:F8:3F:B4)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The FQDN will be different for you (most likely) but you should see the git box waiting to have its certificate signed.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;puppet:~# puppet cert sign git.troutwine.us
notice: Signed certificate request for git.troutwine.us
notice: Removing file Puppet::SSL::CertificateRequest git.troutwine.us at &amp;#39;/var/lib/puppet/ssl/ca/requests/git.troutwine.us.pem&amp;#39;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Substituting, of course, for your domain.&lt;/p&gt;

&lt;p&gt;We&amp;#8217;re going to use a &lt;a href='http://d-i.alioth.debian.org/manual/en.i386/apb.html'&gt;preseed&lt;/a&gt; to automate the package configuration of gitolite for two reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the default user in the package is &amp;#8216;gitolite&amp;#8217; rather than &amp;#8216;git&amp;#8217; (I &lt;em&gt;hate&lt;/em&gt; that) and&lt;/li&gt;

&lt;li&gt;an admin ssh key must be supplied for the package to finish its installation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The second point is the most pressing but the first is important too: there is no shame in being meticulous if such behaviors simplify setup or remove surprises for your users.&lt;/p&gt;

&lt;p&gt;From the root of your puppet configuration, install the gitolite module:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ git submodule add git://github.com/blt/puppet-gitolite.git modules/gitolite&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The node definition for &amp;#8216;git&amp;#8217; is very short. In &lt;code&gt;manifests/nodes/git.pp&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;node &amp;#39;git.troutwine.us&amp;#39; {
  include base

  # Install the gitolite daemon and provide the admin key.
  class { &amp;#39;gitolite&amp;#39;:
    gituser =&amp;gt; &amp;#39;git&amp;#39;,
    admin_key =&amp;gt; &amp;#39;ssh-rsa AAAAB3NSKIPAFEW&amp;#39;,
    path =&amp;gt; &amp;#39;/var/lib/git&amp;#39;,
  }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The installation of the gitolite daemons and user is interfaced through a class: you may not, therefore, install multiple copies of gitolite on a single system. This is counter to the full capability of gitolite&amp;#8211;multiple installed copies, one per user&amp;#8211;but I find it much less error-prone to divide gitolite installations per domain.&lt;/p&gt;

&lt;h3 id='installing_traut_and_the_rabbitmq_message_bus'&gt;Installing traut and the RabbitMQ message bus&lt;/h3&gt;

&lt;p&gt;Before we fuss about with post-receive hooks we&amp;#8217;ll get the remaining kit for deployment humming. Spin up a new machine to host the message bus, call it &amp;#8216;mq0&amp;#8217;. Introduce the node mq0 to puppet and add the RabbitMQ configuration module to your root configuration:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ git submodule add git://github.com/blt/puppet-rabbitmq.git modules/rabbitmq&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Unless you have a certificate authority in place&amp;#8211;which I&amp;#8217;m assuming you don&amp;#8217;t, at this point&amp;#8211;add my openssl module as well:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ git submodule add git://github.com/blt/puppet-openssl.git modules/openssl&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A better name for the openssl module might be &amp;#8216;poor-mans-ca&amp;#8217;, but I thought that a bit long. The module, inspired by &lt;a href='http://projects.reductivelabs.com/projects/puppet/wiki/Module_Ssh_Auth_Patterns'&gt;ssh-auth&lt;/a&gt;, will build, sign and distribute certificates sufficient to run an encrypted internal network. It is &lt;em&gt;not&lt;/em&gt; a secure certificate authority for the wider internet&amp;#8211;don&amp;#8217;t issue these things to hosts that have to rove or, God forbid, to a client. However, so long as you&amp;#8217;re able to keep the machine hosting the private keys secure&amp;#8211;and we&amp;#8217;re going to install the keys to the puppet master, so you should&amp;#8211;this will be just dandy. To be clear: &lt;strong&gt;if an attacker gets access to the filesystem of your puppet master, they will be able to decrypt any communication over channels signed with the keys generated by this module.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Why go to all the trouble of generating certificates for all clients and servers? Hopefully the benefit of running communications to a server daemon over an encrypted channel is obvious to you, but generating keys for a client, as well? Control, simply. The message bus will be used to cause sensitive tasks to kick off; mere encrypted channels do not deny unknown parties from establishing connections and pumping messages through. The RabbitMQ setup advocated here will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;require clients to supply a known username and password and&lt;/li&gt;

&lt;li&gt;supply a certificate co-signed with the server&amp;#8217;s own.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In &lt;code&gt;manifests/nodes.pp&lt;/code&gt; add the following:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# Until there&amp;#39;s a pressing need to construct a more traditional CA for
# internal services, the puppet-openssl module can construct a primitive
# one. The master will be placed on puppet, making that box _extremely_
# sensitive to tampering. It was, of course, already _extremely_ sensitive to
# tampering.
if $hostname == &amp;#39;puppet&amp;#39; {
  class { &amp;#39;openssl::certmaster&amp;#39;:
    ca_name =&amp;gt; &amp;#39;rabbitmq&amp;#39;,
    ensure =&amp;gt; present,
  }
}
Openssl::Server {
  ca_name =&amp;gt; &amp;#39;rabbitmq&amp;#39;,
}
openssl::server {
  &amp;#39;mq0&amp;#39; : ensure =&amp;gt; present;
}
Openssl::Client {
  ca_name =&amp;gt; &amp;#39;rabbitmq&amp;#39;,
}
openssl::client {
  &amp;#39;puppet&amp;#39;: ensure =&amp;gt; present;
  &amp;#39;git&amp;#39;   : ensure =&amp;gt; present;
  &amp;#39;mq0&amp;#39;   : ensure =&amp;gt; present;
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;All three nodes will be issued client certificates for RabbitMQ, placed in &lt;code&gt;/etc/rabbitmq/ssl/client&lt;/code&gt;. The location is configurable, more nodes can be added as can more services.&lt;/p&gt;

&lt;p&gt;Installing the RabbitMQ daemon is a relatively simple matter. Create an mq node definition, &lt;code&gt;manifests/nodes/mq.pp&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;node /^mq\d+/ {
  include base, rabbitmq
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The installation of the traut daemon should be unsurprising&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ git submodule add git://github.com/blt/puppet-traut.git modules/traut&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;except that, instead of creating a new node, we&amp;#8217;ll add &amp;#8216;include traut&amp;#8217; to the base node class in &lt;code&gt;manifests/nodes.pp&lt;/code&gt;. The traut module comes with a few optional goodies, one of which is &lt;a href='https://github.com/blt/hare'&gt;hare&lt;/a&gt;. In &lt;code&gt;manifests/nodes.pp&lt;/code&gt; add:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# The traut daemon which allows cron-like action in response to AMQP
# messages. Here we install cron on all systems and enable a &amp;#39;puppet agent
# --test&amp;#39; event.
$traut_vhost = &amp;#39;/traut&amp;#39;
$traut_user  = &amp;#39;traut&amp;#39;
$traut_pass  = &amp;#39;264l8uSlCeZSGZiCQHns&amp;#39;
$traut_key   = &amp;#39;/etc/rabbitmq/ssl/client/key.pem&amp;#39;
$traut_chain = &amp;#39;/etc/rabbitmq/ssl/client/cert.pem&amp;#39;

class { &amp;#39;traut&amp;#39;:
  ensure =&amp;gt; present,
  vhost =&amp;gt; $traut_vhost,
  host =&amp;gt; &amp;#39;mq0&amp;#39;,
  username =&amp;gt; $traut_user,
  password =&amp;gt; $traut_pass,
  exchange =&amp;gt; &amp;#39;traut&amp;#39;,
  debugging =&amp;gt; true,
  version =&amp;gt; &amp;#39;1.0.1&amp;#39;,
  private_key =&amp;gt; $traut_key,
  cert_chain =&amp;gt; $traut_chain,
  require =&amp;gt; File[$traut_key, $traut_chain],
  subscribe =&amp;gt; File[$traut_key, $traut_chain],
}
include traut::hare&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is a bit long and I urge you to read the documentation provided with the puppet-traut module. Suffice it to say that all nodes will have traut installed, traut will connect to the RabbitMQ daemon on &lt;code&gt;mq0&lt;/code&gt; over SSL (with a client certificate) and using the supplied password and username. &lt;strong&gt;Be sure to change, at least, the value of &lt;code&gt;$traut_pass&lt;/code&gt; in your setup.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To enable specially coded messages on every push for the &amp;#8216;puppet repository, in &lt;code&gt;manifests/nodes/git.pp&lt;/code&gt; add:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# Ensure that on pushes into the &amp;#39;puppet&amp;#39; git repository traut notifications
# are sent out via the post-hook.
gitolite::resource::posthook { &amp;#39;puppet&amp;#39;:
  mqpass =&amp;gt; &amp;quot;${base::gitolite_posthook_password}&amp;quot;,
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;With that in mind, go ahead and &lt;em&gt;setup&lt;/em&gt; the &amp;#8216;puppet&amp;#8217; repository by cloning gitolite-admin and adding the appropriate entries. Be sure to commit and push back your changes.&lt;/p&gt;

&lt;p&gt;That done, in &lt;code&gt;manifests/nodes/mq.pp&lt;/code&gt; add:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# The traut vhost RabbitMQ user and /traut vhost are are used by the traut
# system daemon. /traut should be shared among multiple RabbitMQ users, where
# the traut user _must_ be exclusive to the similarly named daemon.
rabbitmq::resource::vhost { &amp;quot;${base::traut_vhost}&amp;quot;:
  ensure =&amp;gt; present,
}
rabbitmq::resource::user { &amp;quot;${base::traut_user}&amp;quot;:
  require =&amp;gt; Rabbitmq::Resource::Vhost[&amp;quot;${base::traut_vhost}&amp;quot;],
  password =&amp;gt; &amp;quot;${base::traut_pass}&amp;quot;,
  ensure =&amp;gt; present,
}
rabbitmq::resource::user::permissions { &amp;quot;${base::traut_user}&amp;quot;:
  require =&amp;gt; Rabbitmq::Resource::User[&amp;quot;${base::traut_user}&amp;quot;],
  vhost =&amp;gt; &amp;quot;${base::traut_vhost}&amp;quot;,
  ensure =&amp;gt; present,
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This creates the RabbitMQ traut user with the password and the traut vhost, all specified in &lt;code&gt;nodes.pp&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Redeploy and re-run puppet agent on all hosts. You should see traut installed to each system, as well as RabbitMQ on mq0. Depending on the order that &lt;code&gt;puppet
agent&lt;/code&gt; fires on your nodes you may need to run it several times so that ssl keys distribute properly. See the puppet-openssl documentation for more details.&lt;/p&gt;

&lt;p&gt;Before moving on, ensure that all nodes have &amp;#8216;mq0&amp;#8217; resolves in DNS or are set in /etc/hosts; you can manage &lt;em&gt;that&lt;/em&gt; with puppet if you&amp;#8217;d like. Add the following to &lt;code&gt;manifests/nodes.pp&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# Without an internal DNS system, nor a pressing need for one until the
# cluster grows substantially, set hostnames manually through /etc/hosts.
host {
  &amp;#39;puppet.troutwine.us&amp;#39;:
    ensure =&amp;gt; present,
    ip =&amp;gt; &amp;#39;192.168.56.11&amp;#39;,
    host_aliases =&amp;gt; &amp;#39;puppet&amp;#39;;
  &amp;#39;git.troutwine.us&amp;#39;:
    ensure =&amp;gt; present,
    ip =&amp;gt; &amp;#39;192.168.56.12&amp;#39;,
    host_aliases =&amp;gt; &amp;#39;git&amp;#39;;
  &amp;#39;mq0.troutwine.us&amp;#39;:
    ensure =&amp;gt; present,
    ip =&amp;gt; &amp;#39;192.168.56.13&amp;#39;,
    host_aliases =&amp;gt; &amp;#39;mq0&amp;#39;;
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;substituting, of course, for your setup&amp;#8217;s IP addresses and domain name.&lt;/p&gt;

&lt;h3 id='at_last_deploying'&gt;At last: deploying&lt;/h3&gt;

&lt;p&gt;The final piece to this article are a series of shell-scripts that turn traut events into deployable code. I&amp;#8217;ve rolled this into a puppet module as well:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ git submodule add git://github.com/blt/puppet-slugbuild.git modules/slugbuild&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In &lt;code&gt;manifests/git.pp&lt;/code&gt; add the following:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class { &amp;#39;slugbuild&amp;#39;:
  ensure =&amp;gt; present,
  githosts =&amp;gt; &amp;#39;git.troutwine.us,github.com&amp;#39;,
  gitcentral =&amp;gt; &amp;#39;git.troutwine.us&amp;#39;,
  mqpass =&amp;gt; &amp;quot;${base::gitolite_posthook_password}&amp;quot;,
}
slugbuild::resource::traut { &amp;#39;puppet&amp;#39;:
  ensure =&amp;gt; present,
}
slugbuild::resource::authorized_key {
  &amp;#39;puppet root&amp;#39;:
    ensure =&amp;gt; present,
    key =&amp;gt; &amp;#39;AAAABSKIPAFEW&amp;#39;;
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;the exact happenings are documented in the module. Hopefully you noticed that &lt;code&gt;slugbuild::resource::authorized_key&lt;/code&gt; requires you specify an ssh public key. This key, specifically, is for the root puppet master user to the &amp;#8216;slugbuild&amp;#8217; user on the git node. Using this access, the puppet node&amp;#8217;s root will sync slugs. In &lt;code&gt;manifests/nodes/puppet.pp&lt;/code&gt; add:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# This key is generated so that the root user can pull source slugs from the
# slugbuilder. Note, however, that the public key is _not_ automatically
# placed into the slugbuilder&amp;#39;s authorized_keys and must be done so manually
# with slugbuild::resource::authorized_key on the slugbuild node.
user { &amp;#39;root&amp;#39;:
  ensure =&amp;gt; present,
}
ssh::resource::key { &amp;#39;id_rsa&amp;#39;:
  root =&amp;gt; &amp;#39;/root/.ssh/&amp;#39;,
  ensure =&amp;gt; present,
  user =&amp;gt; &amp;#39;root&amp;#39;,
}
ssh::resource::known_hosts { &amp;#39;root&amp;#39;:
  root =&amp;gt; &amp;#39;/root/.ssh/&amp;#39;,
  hosts =&amp;gt; &amp;#39;git.troutwine.us&amp;#39;,
  user =&amp;gt; &amp;#39;root&amp;#39;,
}

# The slugbuild sync will, post-build, sync all available slugs to the local
# machine using the credentials generated above.
class { &amp;#39;slugbuild::slugclient&amp;#39;:
  ensure =&amp;gt; present,
  mqpass =&amp;gt; &amp;quot;${base::gitolite_posthook_password}&amp;quot;,
  slughost =&amp;gt; &amp;#39;git.troutwine.us&amp;#39;,
}
slugbuild::resource::sync { &amp;#39;puppet-sync&amp;#39;:
  project =&amp;gt; &amp;#39;puppet&amp;#39;,
  ensure =&amp;gt; present,
}

# Ensure that newly available puppet configuration slugs are mounted and
# linked as /etc/puppet
class { &amp;#39;puppet::resource::redeploy&amp;#39;:
  ensure =&amp;gt; present,
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The first half constructs ssh keys for the root user of the puppet master, the latter half sets up puppet master as a slugbuild client to &lt;code&gt;git.troutwine.us&lt;/code&gt;. Be sure to use the contents of &lt;code&gt;/root/.ssh/id_rsa.pub&lt;/code&gt; as data to the key parameter of &lt;code&gt;slubuild::resource::authorized_key&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Redeploy your puppet configuration through the rsync daisy chains. Run puppet-agent on all your machines. Commit all of your changes. With everything in place remove &lt;code&gt;/etc/puppet&lt;/code&gt; on the puppet master and push to the gitolite repository you&amp;#8217;ve created. After a few moments, you &lt;em&gt;should&lt;/em&gt; find that slugbuild on the git node has created slugs, these have been transferred over to the puppet node and the latest has been mounted as &lt;code&gt;/etc/puppet&lt;/code&gt;. Subsequent commits will likewise be handled, with the mount point being swapped atomically.&lt;/p&gt;

&lt;h1 id='where_to_next'&gt;Where to next?&lt;/h1&gt;
&lt;div class='poetry'&gt;
There ain't nothing more to write about, and I am rotten
glad of it, because if I'd a knowed what a trouble it was to make a book I
wouldn't a tackled it, and ain't a-going to no more.
&lt;br /&gt;
&lt;small&gt;Huckleberry Finn&lt;/small&gt;
&lt;/div&gt;
&lt;p&gt;I figured that this article would take a week, tops, to write. Instead, it took nearly a month! See my &lt;a href='https://github.com/blt'&gt;Github&lt;/a&gt; for all the &lt;code&gt;puppet-*&lt;/code&gt; activity. Find the repository I&amp;#8217;ve created using the above instructions &lt;a href='https://github.com/blt/troutwineus-puppet-example'&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That said, there&amp;#8217;s still plenty to do but I feel remiss in not taking more time to document the modules already used. You &lt;em&gt;should&lt;/em&gt;, at this point, have a pretty decent base on which to build. I&amp;#8217;ll keep fleshing this out, but go ahead and email me or get in touch with the &lt;a href='http://groups.google.com/group/puppet-users'&gt;Puppet Users&lt;/a&gt; mailing list if you&amp;#8217;re eager to get moving. I&amp;#8217;m also available for hire, as it happens.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;These last few articles have been similar in scope to the puppet books I&amp;#8217;m aware of on the market&amp;#8211;a fact which didn&amp;#8217;t occur to me at the outset. Turnbull&amp;#8217;s books were an excellent reference when I started out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href='http://www.amazon.com/gp/product/1590599780?ie=UTF8&amp;amp;ref_=sr_1_2&amp;amp;qid=1329859043&amp;amp;sr=8-2'&gt;Pulling Strings with Puppet&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='http://www.amazon.com/Pro-Puppet-James-Turnbull/dp/1430230576/ref=sr_1_1?ie=UTF8&amp;amp;qid=1329859043&amp;amp;sr=8-1'&gt;Pro Puppet&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Puppet project also has very decent documentation. I keep the following bookmarked:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href='http://docs.puppetlabs.com/index.html'&gt;Puppet Labs Documentation &amp;#8211; Index&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='http://docs.puppetlabs.com/references/stable/type.html'&gt;Docs: Type Reference&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='http://docs.puppetlabs.com/guides/configuring.html'&gt;Docs: Configuring Puppet&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='http://docs.puppetlabs.com/references/stable/function.html'&gt;Docs: Function Reference&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='http://docs.puppetlabs.com/references/stable/metaparameter.html'&gt;Docs: Metaparameter Reference&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='http://docs.puppetlabs.com/guides/language_guide.html'&gt;Docs: Language Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next time we&amp;#8217;ll start digging back through some modules and examining them in detail, examining several simple modules in a bundle and, later, a single module per article. The &lt;a href='https://github.com/blt/puppet-openssl'&gt;puppet-openssl&lt;/a&gt; will certainly be a long article just to itself.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Wrangling Servers -- Introduction and Preliminaries</title>
    <link rel="alternate" href="/2012/01/22/wrangling_servers_introduction_and_preliminaries.html"/>
    <id>/2012/01/22/wrangling_servers_introduction_and_preliminaries.html</id>
    <published>2012-01-20T00:00:00-06:00</published>
    <updated>2012-01-20T00:00:00-06:00</updated>
    <author>
      <name>Brian L. Troutwine</name>
    </author>
    <summary type="html">&lt;p&gt;When I was brought on as &lt;a href='https://www.carepilot.com'&gt;CarePilot&lt;/a&gt; as Systems Administrator / Operations Developer we ran on a single(!) box in Amazon&amp;#8217;s EC2, no redundancy and nothing in the way of configuration control. I kept nothing of that box&amp;#8211;even moving away from EC2 to Rackspace. CarePilot runs on kit built up from scratch, all the way up and down from the DB replication and backups, to the application deployment process to high-level monitoring and notifications. It was my goal at CarePilot to automate, within a reasonable degree, the maintenance and repair of the machines. Some things I&amp;#8217;ve invented, others I&amp;#8217;ve picked up and made use of.&lt;/p&gt;

&lt;p&gt;While the CarePilot kit was custom crafted, I believe that it&amp;#8217;s component pieces&amp;#8211;released under commercial-venture friendly open-source licenses&amp;#8211;could be used as a base for most any tech-startup. This is the first article in a series that will document the kit I&amp;#8217;ve produced, introducing some of the open-source bits of tech I&amp;#8217;ve created and server as a bit of a tutorial for those I merely make use of.&lt;/p&gt;

&lt;p&gt;If at the end of these articles you can&amp;#8217;t piece together a stable base for a new business let me know: &lt;em&gt;I wrote something wrong&lt;/em&gt;.&lt;/p&gt;</summary>
    <content type="html">&lt;p&gt;When I was brought on as &lt;a href='https://www.carepilot.com'&gt;CarePilot&lt;/a&gt; as Systems Administrator / Operations Developer we ran on a single(!) box in Amazon&amp;#8217;s EC2, no redundancy and nothing in the way of configuration control. I kept nothing of that box&amp;#8211;even moving away from EC2 to Rackspace. CarePilot runs on kit built up from scratch, all the way up and down from the DB replication and backups, to the application deployment process to high-level monitoring and notifications. It was my goal at CarePilot to automate, within a reasonable degree, the maintenance and repair of the machines. Some things I&amp;#8217;ve invented, others I&amp;#8217;ve picked up and made use of.&lt;/p&gt;

&lt;p&gt;While the CarePilot kit was custom crafted, I believe that it&amp;#8217;s component pieces&amp;#8211;released under commercial-venture friendly open-source licenses&amp;#8211;could be used as a base for most any tech-startup. This is the first article in a series that will document the kit I&amp;#8217;ve produced, introducing some of the open-source bits of tech I&amp;#8217;ve created and server as a bit of a tutorial for those I merely make use of.&lt;/p&gt;

&lt;p&gt;If at the end of these articles you can&amp;#8217;t piece together a stable base for a new business let me know: &lt;em&gt;I wrote something wrong&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;h2 id='what_are_you_getting_yourself_into'&gt;What are you getting yourself into?&lt;/h2&gt;
&lt;hr /&gt;&lt;i&gt;Kindly note, I don't speak for CarePilot. The opinions and preferences I
express here are not necessarily representative of the views of CarePilot. I'm
just a guy talking on his own.&lt;/i&gt;&lt;hr /&gt;
&lt;p&gt;Imagine that you&amp;#8217;re employee #3 of a startup and are getting pulled into more Ops work as the load on your few EC2 boxes gets high: all day you fiddle with this and that, then &lt;em&gt;bam&lt;/em&gt; one of the servers goes offline and you have to piece a new image up by hand. The site is offline, meanwhile, and employees #1 and #2 can&amp;#8217;t help but give you the stink-eye. Or, imagine that you&amp;#8217;re the kind of person to release small profit generating web-apps every few months and one has finally taken off. Success! But the load on the $36 Heroku instance you&amp;#8217;re running on is too high and the site&amp;#8217;s suffering. You can crank up the Heroku toggles to meet the load, but you&amp;#8217;re going to lose profitability that way. Time to move to virtual hosting, keeping in mind that you &lt;em&gt;have&lt;/em&gt; to keep your Ops work to a minimum.&lt;/p&gt;

&lt;p&gt;What do you do, in either case? Use Puppet. Puppet is a relatively easy to use state management tool&amp;#8211;that makes an excellent sideline into acting as a configuration tool&amp;#8211;is well documented and backed by a company of &lt;a href='http://puppetlabs.com/'&gt;nice folks&lt;/a&gt; and &lt;a href='http://groups.google.com/group/puppet-users'&gt;nice users&lt;/a&gt;. Puppet is &lt;em&gt;not&lt;/em&gt; an easy thing to bootstrap, however. This article, and the one that follows it, will walk you through getting a production-ready puppet setup bootstrapped, along with a few extra goodies that I &lt;em&gt;think&lt;/em&gt; you&amp;#8217;ll find very helpful.&lt;/p&gt;

&lt;p&gt;Should take a few hours. In this article I&amp;#8217;ll walk you through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;setting up a base box image,&lt;/li&gt;

&lt;li&gt;version controlling puppet configuration right off and&lt;/li&gt;

&lt;li&gt;bootstrapping a central puppet master from a base box.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The lack of configuration management makes this a much larger task than it would be &lt;em&gt;with&lt;/em&gt; configuration management: take heart, this is the &lt;em&gt;hard&lt;/em&gt; part.&lt;/p&gt;

&lt;h2 id='bootstrapping_puppet'&gt;Bootstrapping Puppet&lt;/h2&gt;

&lt;p&gt;The absolute heart of a managed server cluster, as conceived here, is Puppet: without it configuration is a one-off affair, down boxes are not easily replaced and there exists no central repository for understanding the arrangement of the whole system. There are alternatives&amp;#8211;Chef being the most common&amp;#8211;but the kit I&amp;#8217;m going to outline here uses Puppet for two reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I went to &lt;a href='http://pdx.edu'&gt;University&lt;/a&gt; and did some projects with a fellow completely enamored of it&amp;#8211;I believe the good &lt;a href='http://cat.pdx.edu'&gt;IT department&lt;/a&gt; makes heavy use of it.&lt;/li&gt;

&lt;li&gt;I lurked in the Chef and Puppet communities for a time and found the conversation in Puppet&amp;#8217;s more helpful to the almost-hopelessly ignorant. Posting a puppet related question to &lt;a href='http://serverfault.com/questions/tagged/puppet'&gt;ServerFault&lt;/a&gt; and having a detailed answer within the half-hour is a fine thing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The base OS used for this kit is Debian Squeeze. With Puppet being written in &lt;a href='http://ruby-lang.org'&gt;ruby&lt;/a&gt; and Debian support for ruby being somewhat crummy&amp;#8211;the Debian Ruby Team, as I understand it, is understaffed and the ruby community has values somewhat hostile to Debian&amp;#8217;s own&amp;#8211;there&amp;#8217;s a hassle here. Namely: do I install puppet from &lt;a href='http://en.wikipedia.org/wiki/Rubygems'&gt;rubygems&lt;/a&gt; or from Debian&amp;#8217;s packages? Considerations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Debian&amp;#8217;s puppet requires ruby 1.8.7 where mainstream ruby development largely targets 1.9.&lt;/li&gt;

&lt;li&gt;Puppet&amp;#8217;s central master has had memory-leak issues with ruby 1.8.7.&lt;/li&gt;

&lt;li&gt;We&amp;#8217;ll be installing system utilities through rubygems later in this series.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rather than suffer with ruby 1.8.7 puppet will be installed as a gem, the process of which will prime us for quite a bit of work later on.&lt;/p&gt;
&lt;hr /&gt;&lt;i&gt;Before we go further, I suggest you get a virtual-machine setup
going--or rent some servers in a cloud--and follow along. I've used Virtualbox
on my personal machine to spot-check the writing of this series, though I won't
provide instructions on its use. To mimic standard VPS system setup, configure
your systems to have two network interfaces, one for NAT the other for host-only
networking. Make `eth0` the NAT interface. Make sure that `eth1` has a static
address.&lt;/i&gt;&lt;hr /&gt;
&lt;p&gt;I like to have a template base box which forms the basis of all other box types. By that I mean most cloud VPS companies allow you to store an &amp;#8216;image&amp;#8217;, a pre-configured OS alongside their offered fresh-install OS images. I like to keep a single base image&amp;#8211;carepilot-base, say&amp;#8211;which all newly spun-up boxes are elaborated from, through the use of Puppet. This causes a cyclical dependency issue which must be resolved by hand: namely, the puppet-master machine will necessarily require a human to coax it into being. As the puppet-master is not a production critical piece of infrastructure&amp;#8211;its being offline does not stop customers interacting with the website or other product&amp;#8211;I find this acceptable. If you &lt;em&gt;do not&lt;/em&gt; and &lt;em&gt;do&lt;/em&gt; come up with an alternative to breaking the dependency cycle drop me a line!&lt;/p&gt;

&lt;p&gt;On the base box we&amp;#8217;ll install:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;our desired ruby version,&lt;/li&gt;

&lt;li&gt;the puppet gem and&lt;/li&gt;

&lt;li&gt;supervisord to run the puppet client.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Yes, supervisord. Writing init scripts is a bummer&amp;#8211;not to mention largely non-portable&amp;#8211;and adding daemonizing code to any system tools you might create is, likewise, a bummer. In the perfect Unix spirit, why not delegate daemonization to a special-purpose tool?&lt;/p&gt;

&lt;h3 id='installing_ruby'&gt;Installing Ruby&lt;/h3&gt;

&lt;p&gt;It&amp;#8217;s the ill-named &lt;code&gt;ruby1.9.1&lt;/code&gt; and &lt;code&gt;rubygems1.9.1&lt;/code&gt; we want. In actuality, these install, as of this writing, the &lt;em&gt;1.9.2&lt;/em&gt; series of interpreter. I&amp;#8217;m sure there&amp;#8217;s an interesting story there.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;base:~# aptitude install ruby1.9.1 rubygems1.9.1&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Some gems we&amp;#8217;ll need to install have so-called &amp;#8216;native extensions&amp;#8217;&amp;#8211;C code&amp;#8211;so we&amp;#8217;re going to need a compiler on our production systems. Possibly a bummer, depending on your environment. Being certified to handle credit card processing or to handle some gambling related matters&amp;#8211;I&amp;#8217;m vague on gambling, sorry&amp;#8211;puts a C compiler or make facility on a production system right out. In general, sure, it&amp;#8217;s important to make your default system as secure as possible. Consider, though, that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the modern Debian system has, in its base install, several Turing-complete interpreters able to produce machine code,&lt;/li&gt;

&lt;li&gt;large portions of GCC&amp;#8217;s support libraries and tools are already installed in the base system, an interpreted language could be made to use those tools,&lt;/li&gt;

&lt;li&gt;once a Turing-complete language with FFI ability is already present on a box you&amp;#8217;ve installed the equivalent of a C compiler and make system onto a production system and&lt;/li&gt;

&lt;li&gt;&lt;em&gt;real&lt;/em&gt; system security is about restricting remote access, user access controls and segregation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You &lt;em&gt;can&lt;/em&gt; build a box to host your own apt repository for gems and other odds and ends that need a compiler&amp;#8211;keeping those production systems perfectly clear of this one machine-code production vector&amp;#8211;but I&amp;#8217;m not convinced that the effort involved in that is rational given the slight nature of the hazard. It&amp;#8217;s unpleasant work and you&amp;#8217;ll spend a fair bit of time on it, continuously, but it can be done. I won&amp;#8217;t do it here and in this series I&amp;#8217;ll assume that you&amp;#8217;ll have installed the following on the base system:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;base:~# aptitude install ruby1.9.1-dev build-essential libssl-dev&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To my knowledge there&amp;#8217;s no automatic alternatives system for ruby. Fun thing about Debian, though, is that it&amp;#8217;s:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;always got some tool that will meet your needs and&lt;/li&gt;

&lt;li&gt;isn&amp;#8217;t as thoroughly documented as you might hope, commensurate to your possible needs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Folks on the various mailing lists are &lt;em&gt;super&lt;/em&gt; helpful, however.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;base:~# update-alternatives --install /usr/bin/ruby ruby /usr/bin/ruby1.9.1 400 \
  --slave /usr/share/man/man1/ruby.1.gz ruby.1.gz /usr/share/man/man1/ruby1.9.1.1.gz \
  --slave /usr/bin/irb irb /usr/bin/irb1.9.1 \
  --slave /usr/bin/gem gem /usr/bin/gem1.9.1&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You &lt;em&gt;could&lt;/em&gt; do something similar for 1.8 as well, but I&amp;#8217;m not going to bother. You will need to have git and openssl present on all of your systems:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;base:~# aptitude install git openssl&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id='installing_puppet'&gt;Installing Puppet&lt;/h3&gt;

&lt;p&gt;Compared to getting ruby installed, this is going to be a breeze:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;base:~# gem install facter --version &amp;#39;1.6.4&amp;#39; --no-ri --no-rdoc
Successfully installed facter-1.6.4
1 gem installed

base:~# gem install puppet --version &amp;#39;2.7.9&amp;#39; --no-ri --no-rdoc
Successfully installed puppet-2.7.9
1 gem installed&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;(I hate wasting disk space and don&amp;#8217;t reference the installed documentation on my production servers &lt;em&gt;ever&lt;/em&gt;, so I&amp;#8217;ve skipped building rdocs and the ruby index.) Note those version numbers; they&amp;#8217;ll be enshrined in puppet configuration later, but for now I&amp;#8217;d like to make sure, if you&amp;#8217;re following along, that we&amp;#8217;re on the same footing.&lt;/p&gt;

&lt;p&gt;One fun &lt;em&gt;problem&lt;/em&gt; with Debian and rubygems is that gems&amp;#8217; binaries are &lt;em&gt;not&lt;/em&gt; installed in the default system &lt;code&gt;$PATH&lt;/code&gt;. &amp;#8220;Wait a second,&amp;#8221; I can hear my year-ago self saying to a chilly room and two napping dogs, &amp;#8220;they &lt;em&gt;meant&lt;/em&gt; to dump gem binaries into /var/lib?&amp;#8221; Yep: so the argument went, /var/lib is state data and the &lt;em&gt;state&lt;/em&gt; for rubygems is all the code plus the executables, never mind the fact that it&amp;#8217;s not uncommon to mount /var on it&amp;#8217;s own partition and set it noexec in fstab. Anyway, I&amp;#8217;ve heard the Debian team&amp;#8217;s going to fix this in a later version of the OS. For now, it&amp;#8217;s up to us:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;make sure that your &lt;code&gt;/var&lt;/code&gt; partition is mounted exec (it probably will be) and&lt;/li&gt;

&lt;li&gt;add &lt;code&gt;[ -d /var/lib/gems/1.9.1/bin/ ] &amp;amp;&amp;amp; export PATH=&amp;quot;/var/lib/gems/1.9.1/bin/:$PATH&amp;quot;&lt;/code&gt; to the top of /etc/bash.bashrc&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There must be a &lt;code&gt;puppet&lt;/code&gt; group and user available on the box:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;base:~# adduser --group --system --home /etc/puppet --disabled-password puppet&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id='installing_supervisord'&gt;Installing Supervisord&lt;/h3&gt;

&lt;p&gt;Finally, supervisord:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;base:~# aptitude install supervisor&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;What we don&amp;#8217;t want right now is to start the puppet client, either in daemon form or foreground. It&amp;#8217;s a right pain dealing with puppet if it mis-creates an SSL key and on the base box we&amp;#8217;ll be forced to destroy that key for each new box derived from the base. It&amp;#8217;s okay, though: we&amp;#8217;ll write puppet configuration such that the first, necessarily manual, run of each puppet client will secure the box in its intended role.&lt;/p&gt;

&lt;h2 id='setting_up_the_puppetd_box'&gt;Setting up the Puppetd Box&lt;/h2&gt;

&lt;p&gt;Clone the base image and make a box with hostname &lt;code&gt;puppet&lt;/code&gt;. This machine will host the daemonized puppet-master daemon from which all puppet clients will pull configuration directives. By default, puppet master uses webrick to serve content&amp;#8211;a tremendously slow and wasteful web-server fit only for development purposes. In my experience, even past a few clients, webrick is woefully under-powered. Happily, it&amp;#8217;s easy enough to get a production-ready puppet master installation going.&lt;/p&gt;

&lt;h3 id='first_step_nginx_a_proper_webserver'&gt;First step, Nginx: a proper web-server.&lt;/h3&gt;

&lt;p&gt;The nginx in Squeeze&amp;#8217;s default package list is far too old: just over two years at this point. Enable the backports repository by creating &lt;code&gt;/etc/apt/sources.list.d/backports.list&lt;/code&gt; with the content:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;deb http://backports.debian.org/debian-backports squeeze-backports main&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# aptitude update &amp;amp;&amp;amp; aptitude install -t squeeze-backports nginx&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This will ensure that the backports package list is available in your cached apt index and that nginx is installed from the backports repository. By default &lt;a href='http://packages.debian.org/squeeze-backports/nginx-full'&gt;nginx-full&lt;/a&gt; is installed, though since we&amp;#8217;re installing the dummy you can opt to use nginx-light by installing that package after installing the dummy; the dummy will remain installed and update nginx-light as needed.&lt;/p&gt;

&lt;h3 id='second_step_thin_a_rubyrack_webserver'&gt;Second step, Thin: a ruby-rack web-server&lt;/h3&gt;

&lt;p&gt;If you&amp;#8217;re not so familiar with ruby-land, &amp;#8216;rack&amp;#8217; is a ruby glue layer between many web-servers and, well, whatever you want to build on top of that layer. Rails3 is a rack library and puppet, too. What we&amp;#8217;re going to do is use the rackup file&amp;#8211;&amp;#8216;.ru&amp;#8217; extension, by convention, which contains instructions for a rack aware web-server, like thin&amp;#8211;shipped with Puppet. Then we&amp;#8217;ll create several thin / puppet master processes, load-balancing over them with nginx and managed by supervisord.&lt;/p&gt;

&lt;p&gt;Installing thin is a breeze:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;puppet:~# gem install thin --no-ri --no-rdoc
Successfully installed eventmachine-0.12.10
Successfully installed daemons-1.1.6
Successfully installed thin-1.3.1
3 gems installed&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Again, I&amp;#8217;ve elected to &lt;em&gt;not&lt;/em&gt; install documentation. You should notice that there is now a &lt;code&gt;thin&lt;/code&gt; executable in your &lt;code&gt;$PATH&lt;/code&gt;, but not in a really pleasing place to type out. The Debian alternatives system again:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;puppet:~# update-alternatives --install /usr/bin/thin thin /var/lib/gems/1.9.1/bin/thin 400&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The rackup file for puppet is &lt;code&gt;/var/lib/gems/1.9.1/gems/puppet-2.7.9/ext/rack/files/config.ru&lt;/code&gt; and should be copied into /etc/puppet:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;puppet:~# cp /var/lib/gems/1.9.1/gems/puppet-2.7.9/ext/rack/files/config.ru /etc/puppet/&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In &lt;code&gt;/etc/supervisor/conf.d/puppetmaster.conf&lt;/code&gt; create a file with this content:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[program:puppetmaster]
numprocs=3
command=/usr/bin/thin start -e development --socket /var/run/puppet/master.%(process_num)02d.sock --user puppet --group puppet --chdir /etc/puppet -R /etc/puppet/config.ru
process_name=%(program_name)s_%(process_num)02d
startsecs=5&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Thin is run in development mode so that it will not daemonize. The supervisord option &lt;code&gt;numprocs&lt;/code&gt; is used to cluster instances, at small cost of RAM and initial complexity. Be sure &lt;em&gt;not&lt;/em&gt; to spin these processes up as we do not yet have puppet configuration in place.&lt;/p&gt;

&lt;h3 id='third_step_nginxthin_servicing_puppet_clients'&gt;Third step, Nginx+Thin: servicing puppet clients&lt;/h3&gt;

&lt;p&gt;By default, puppet master stores its SSL certificates in &lt;code&gt;/etc/puppet&lt;/code&gt;. This is less than ideal: we&amp;#8217;re going to version the manifests and configuration in this directory with git&amp;#8211;and we don&amp;#8217;t want to check-in private certificates, just in case. Not to mention that there &lt;em&gt;are&lt;/em&gt; better places on disk for SSL certs. Create &lt;code&gt;/etc/puppet/puppet.conf&lt;/code&gt; like so:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[main]
ssldir=$vardir/ssl

[master]
certname=puppet&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Hopefully the &lt;a href='http://docs.puppetlabs.com/references/stable/configuration.html#ssldir'&gt;&amp;#8216;ssldir&amp;#8217;&lt;/a&gt; is sensible enough. That last line &lt;code&gt;certname=puppet&lt;/code&gt; changes the default name of the puppet master pem from the FQDN to, well, just &amp;#8216;puppet&amp;#8217;. The FQDN pem makes it difficult to have a generic and relatively simple bootstrapping process because we&amp;#8217;d be forced to tweaking our actions slightly for every new domain; boot-strapping should be as quick and as simple as possible. Else, you&amp;#8217;ll probably forget a step and spend forty-five frustrating minutes wondering why nothing works when I&amp;#8217;ve been so &lt;em&gt;blase&lt;/em&gt; in declaring the straight-forward nature of this work.&lt;/p&gt;
&lt;hr /&gt;&lt;i&gt;You might wonder why there have been two files placed in /etc/puppet but no
versioning having been applied. The deployment process will assume that your
puppet configuration is held in a git repository. I'll walk through this in the
next article, introducing an interim deployment strategy as we work to get the
kit online sufficient to support the final process.&lt;/i&gt;&lt;i&gt;There won't be any more edits to files in /etc/puppet for the remainder of
this article.&lt;/i&gt;&lt;hr /&gt;
&lt;p&gt;You can execute the puppetmasterd script in non-daemon, debug mode and determine if your installation has gone well. You should see a &amp;#8216;Finishing transaction&amp;#8217; message as the final output of this command:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;puppet:~# puppetmasterd --no-daemonize --debug&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Send the process SIGINT to return your terminal; you won&amp;#8217;t hurt anything. For restricted, privileged writing of pidfiles, domain sockets and logs:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;puppet:~# mkdir /var/run/puppet &amp;amp;&amp;amp; chown puppet:puppet /var/run/puppet
puppet:~# mkdir /var/log/puppet &amp;amp;&amp;amp; chown puppet:puppet /var/log/puppet&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;With thin running all that remains is getting nginx to front for it. Edit &lt;code&gt;/etc/nginx/sites-enabled/default&lt;/code&gt; to read&lt;/p&gt;
&lt;pre&gt;
server {
  # I'm assuming that 'puppet' resolves to a private IP. Don't run Puppet on the public internet!
  listen puppet:8140;

  ssl on;
  ssl_certificate /var/lib/puppet/ssl/certs/puppet.pem;
  ssl_certificate_key /var/lib/puppet/ssl/private_keys/puppet.pem;
  ssl_ciphers ALL:-ADH:+HIGH:+MEDIUM:-LOW:-SSLv2:-EXP;
  ssl_client_certificate  /var/lib/puppet/ssl/ca/ca_crt.pem;
  ssl_verify_client optional;

  # Force all filetypes to be sent raw. (Ouch!)
  types { }
  default_type application/x-raw;

  # serving static files from mount point
  location /production/file_content/files/ {
    ## problem here is that puppet factor doesn't give compact CIDR for network
    # allow   192.168.56.0/16;
    # deny    all;

    alias /etc/puppet/files/;
  }

  # serving static files from modules mounts
  location ~ /production/file_content/[^/]+/files/ {
    ## see above
    # allow   192.168.56.0/16;
    # deny    all;

    root /etc/puppet/modules;

    # rewrite /production/file_content/module/files/file.txt
    # to /module/file.text
    rewrite ^/production/file_content/([^/]+)/files/(.+)$  $1/$2 break;
  }

  location / {
    proxy_pass http://puppet-production;
    proxy_redirect   off;
    proxy_set_header Host             $host;
    proxy_set_header X-Real-IP        $remote_addr;
    proxy_set_header X-Forwarded-For  $proxy_add_x_forwarded_for;
    proxy_set_header X-Client-Verify  $ssl_client_verify;
    proxy_set_header X-Client-Verify  SUCCESS;
    proxy_set_header X-Client-DN      $ssl_client_s_dn;
    proxy_set_header X-SSL-Subject    $ssl_client_s_dn;
    proxy_set_header X-SSL-Issuer     $ssl_client_i_dn;
  }
}
&lt;/pre&gt;
&lt;p&gt;and &lt;code&gt;/etc/nginx/conf.d/puppet-production-upstream.conf&lt;/code&gt; to read:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;upstream puppet-production {
  server unix:/var/run/puppet/master.00.sock;
  server unix:/var/run/puppet/master.01.sock;
  server unix:/var/run/puppet/master.02.sock;
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We&amp;#8217;re causing the local nginx server to respond to hostname &amp;#8216;puppet&amp;#8217; requests by passing them on to the puppet master backends responding over the domain sockets we defined in the last section. Nginx handles the SSL mongering for puppet and sets proxy values, as defined in the &amp;#8216;default&amp;#8217; vhost.&lt;/p&gt;

&lt;p&gt;Note that, thanks to our use of &lt;code&gt;certname&lt;/code&gt; in puppet configuration we can refer to the generic &amp;#8216;puppet.pem&amp;#8217; in the ssl configuration. I won&amp;#8217;t elaborate on the hows and the whys&amp;#8211;mostly because they&amp;#8217;re uninteresting&amp;#8211;but if you&amp;#8217;d like to read up on the Nginx bits do so &lt;a href='http://wiki.nginx.org/Modules'&gt;here&lt;/a&gt; and search &lt;a href='http://docs.puppetlabs.com/references/stable/configuration.html'&gt;this document&lt;/a&gt;. If I&amp;#8217;ve skipped over something unrealistically fast drop me a line. You&amp;#8217;ll find my email address in the footer of this page. Search for &amp;#8216;Contact me.&amp;#8217;&lt;/p&gt;

&lt;p&gt;You should now be able to start the puppet master. First, restart nginx:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;puppet:~# /etc/init.d/nginx restart&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now cause supervisord to re-read its configuration and start up the thin process hosting puppet master.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;puppet:~# supervisorctl reread
puppet:~# supervisorctl update
puppet:~# supervisorctl start puppetmaster:*&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Before you attempt to run the puppet agent, be aware that puppet master running on ruby 1.9.2 has an interesting &lt;a href='http://projects.puppetlabs.com/issues/9084'&gt;issue&lt;/a&gt;, which, since we&amp;#8217;re using a base system, is going to be easy enough to correct. Do all of the following, taking careful note of the &lt;em&gt;systems&lt;/em&gt; the commands are run on.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;puppet:~# ln -s /var/lib/puppet/ssl/certs/ca.pem $(openssl version -d|cut -d\&amp;quot; -f2)/certs/$(openssl x509 -hash -noout -in /var/lib/puppet/ssl/certs/ca.pem).0&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You should be able to restart nginx and issue&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;puppet:~# puppet agent --test --noop&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;with no problems to report. The final message output will be:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Exiting; no certificate found and waitforcert is disabled&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This means that the puppet agent did make a connection to the puppet master server but rejected sending any catalogs down as the key presented by the client was unknown to the server: you must sign the certificate.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;puppet:~# puppet cert --sign puppet.troutwine.us
notice: Signed certificate request for puppet.troutwine.us
notice: Removing file Puppet::SSL::CertificateRequest puppet.troutwine.us at &amp;#39;/var/lib/puppet/ssl/ca/requests/puppet.troutwine.us.pem&amp;#39;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;(Your fully qualified domain will vary. See the help text of &lt;code&gt;puppet cert&lt;/code&gt; for more details.) To bring the base system up to speed, note the output of&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;puppet:~# cat /var/lib/puppet/ssl/certs/ca.pem&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;then, on the base system,&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;base:~# mkdir -p /var/lib/puppet/ssl/certs
base:~# chown puppet:puppet /var/lib/puppet/ssl/certs/
base:~# cat &amp;lt;&amp;lt; EOF &amp;gt;&amp;gt; /var/lib/puppet/ssl/certs/ca.pem
YOUR CERTIFICATE KEY TEXT GOES HERE
EOF
base:~# chmod 644 /var/lib/puppet/ssl/certs/ca.pem
base:~# ln -s /var/lib/puppet/ssl/certs/ca.pem $(openssl version -d|cut -d\&amp;quot; -f2)/certs/$(openssl x509 -hash -noout -in /var/lib/puppet/ssl/certs/ca.pem).0&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Every system cloned from the base &lt;em&gt;from this point forward&lt;/em&gt; will be keyed to the created puppet master key. That means, should the master key change you&amp;#8217;ll need to manually swap them on every live host and update the base image.&lt;/p&gt;

&lt;h2 id='what_have_we_got_and_where_to_next'&gt;What have we got and where to next?&lt;/h2&gt;
&lt;div class='poetry'&gt;
&lt;pre&gt;
Venerable Gon'yo asked Joshu,
  "How is it when a person does not have a single thing?"
Joshu said,
  "Throw it away."
Gon'yo said,
  "I say I don't have a single thing. What could I ever throw away?"
Joshu said,
  "If so, carry it around with you."
&lt;/pre&gt;
&lt;small&gt;Gon'yo's One "Thing"&lt;/small&gt;
&lt;/div&gt;
&lt;p&gt;We have a little more than nothing to carry around, happily. What we do have is a pre-configured base system coupled to a central puppet master, though with no version control of puppet manifests, no puppet manifests and nothing to ensure that puppet is &lt;em&gt;actually&lt;/em&gt; doing the job we set for it to do. That&amp;#8217;s all fixable.&lt;/p&gt;

&lt;p&gt;In the next article we&amp;#8217;ll accomplish three things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;version controlled manifests,&lt;/li&gt;

&lt;li&gt;the self-hosting of puppet and&lt;/li&gt;

&lt;li&gt;the tech needed to get a more convenient deployment model going.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Articles thereafter will cover the inclusion of ops management tools, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href='http://docs.puppetlabs.com/dashboard/manual/1.2/bootstrapping.html'&gt;Puppet Dashboard&lt;/a&gt; and&lt;/li&gt;

&lt;li&gt;Redmine.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy hacking!&lt;/p&gt;</content>
  </entry>
</feed>

