<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.masterzen.fr/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>Masterzen's Blog » Puppet</title>
	
	<link>http://www.masterzen.fr</link>
	<description>Journey in a software world...</description>
	<lastBuildDate>Sat, 31 Jul 2010 15:48:27 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.masterzen.fr/MasterzenPuppet" /><feedburner:info uri="masterzenpuppet" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>More Puppet Offloading</title>
		<link>http://feeds.masterzen.fr/~r/MasterzenPuppet/~3/x71qXOlDfyU/</link>
		<comments>http://www.masterzen.fr/2010/03/21/more-puppet-offloading/#comments</comments>
		<pubDate>Sun, 21 Mar 2010 15:56:58 +0000</pubDate>
		<dc:creator>masterzen</dc:creator>
				<category><![CDATA[Nginx]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Puppet]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[System Administration]]></category>
		<category><![CDATA[cache]]></category>
		<category><![CDATA[offloading]]></category>
		<category><![CDATA[puppet]]></category>
		<category><![CDATA[puppetmaster]]></category>

		<guid isPermaLink="false">http://www.masterzen.fr/?p=186</guid>
		<description><![CDATA[Puppet really shines at configuration management, but there are some things it is not good at, for instance file sourcing of large files, or managing deep hierarchies.
Fortunately, most of this efficiency issues will be addressed in a subsequent major version (thanks to some of my patches and other refactorings).
Meanwhile it is interesting to work-around those [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://reductivelabs.com/products/puppet/" onclick="javascript:pageTracker._trackPageview('/outbound/article/reductivelabs.com');">Puppet</a> really shines at configuration management, but there are some things it is not good at, for instance file sourcing of large files, or managing deep hierarchies.</p>
<p>Fortunately, most of this efficiency issues will be addressed in a subsequent major version (thanks to <a href="http://projects.reductivelabs.com/issues/3396" onclick="javascript:pageTracker._trackPageview('/outbound/article/projects.reductivelabs.com');">some of</a> <a href="http://projects.reductivelabs.com/issues/2929" onclick="javascript:pageTracker._trackPageview('/outbound/article/projects.reductivelabs.com');">my patches</a> and other refactorings).</p>
<p>Meanwhile it is interesting to work-around those bugs. Since most of us are running our masters as part of a more complete stack and not isolated, we can leverage the power of this stack to address some of the issues.</p>
<p>In this article, I&#8217;ll expose two techniques to help your overloaded masters to serve more and more clients.</p>
<h2>Offloading file sourcing</h2>
<p>I already talked about offloading file sourcing in a <a href="http://www.masterzen.fr/2010/01/28/puppet-memory-usage-not-a-fatality/" >previous blog post about puppet memory consumption</a>. Here the idea is to prevent our puppetmasters to read the whole content of files in memory at once to serve them. Most of the installation of puppetmasterd out there are behind an http reverse proxy of some sort (ie Apache or Nginx).</p>
<p>The idea is that file serving is an activity that a small static server is better placed to do than puppet itself (that might change when <a href="http://projects.reductivelabs.com/issues/3373" onclick="javascript:pageTracker._trackPageview('/outbound/article/projects.reductivelabs.com');">#3373</a> will be fully addressed). Note: I produced <a href="http://groups.google.com/group/puppet-dev/t/f9ffe87357c2ba38" onclick="javascript:pageTracker._trackPageview('/outbound/article/groups.google.com');">an experimental patch pending review</a> to stream puppet file sourcing on the client side, which this tip doesn&#8217;t address.</p>
<p>So I did implement this in <a href="http://www.nginx.org" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.nginx.org');">Nginx</a> (which is my favorite http server of course, but that can be ported to any other webserver quite easily, which is an exercise left to the reader):</p>
<script src="http://gist.github.com/339342.js"></script><noscript><code class="gist"><pre></p>
<pre>server {
    listen 8140;

    ssl                     on;
    ssl_session_timeout     5m;
    ssl_certificate         /var/lib/puppet/ssl/certs/master.pem;
    ssl_certificate_key     /var/lib/puppet/ssl/private_keys/master.pem;
    ssl_client_certificate  /var/lib/puppet/ssl/ca/ca_crt.pem;
    ssl_crl                 /var/lib/puppet/ssl/ca/ca_crl.pem;
    ssl_verify_client       optional;

    root                    /etc/puppet;

    # make sure we serve everything
    # as raw
    types { }
    default_type application/x-raw;

    # those locations are for the "production" environment
    # update according to your configuration

    # serve static file for the [files] mountpoint
    location /production/file_content/files/ {
        # it is advisable to have some access rules here
        allow   172.16.0.0/16;
        deny    all;

        alias /etc/puppet/files/;
    }

    # serve modules files sections
    location ~ /production/file_content/[^/]+/files/ {
        # it is advisable to have some access rules here
        allow   172.16.0.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;
    }

    # ask the puppetmaster for everything else
    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-SSL-Subject    $ssl_client_s_dn;
        proxy_set_header    X-SSL-Issuer     $ssl_client_i_dn;
        proxy_buffer_size   16k;
        proxy_buffers       8 32k;
        proxy_busy_buffers_size    64k;
        proxy_temp_file_write_size 64k;
        proxy_read_timeout  65;
    }
}</pre>
<p></pre></code></noscript>
<p>And if you use multiple module paths (for instance to separate common modules to other modules), it is still possible to use this trick with some use of <a href="http://wiki.nginx.org/NginxHttpCoreModule#try_files" onclick="javascript:pageTracker._trackPageview('/outbound/article/wiki.nginx.org');">nginx try_files</a> directive.</p>
<p>The try_files directive allows puppet to try several physical path (the first matching one will be served), and if none match you can use the generic location that proxies to the master which certainly will know what to do.</p>
<p>Something that can be useful would be to create a small script to generate the nginx config from your fileserver.conf and puppet.conf. Since mine is pretty easy, I did it manually.</p>
<h2>Optimize Catalog Compilation</h2>
<p>The normal process of puppet is to contact the <em>puppetmaster</em> at some time interval asking for a catalog. The catalog is a byproduct of the compilation of the parsed manifests in which are injected the node facts. This operation takes some times depending on the manifest complexity and the server capacity or current load.</p>
<p>Most of the time an host requires a catalog while the <em>manifests didn&#8217;t change at all</em>. In my own infrastructure I rarely change my manifests once a kind of host become stable (I might do a change every week at most when in production).</p>
<p>Since 0.25, puppet is now fully <a href="http://en.wikipedia.org/wiki/Representational_State_Transfer" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');">RESTful</a>, that means to get a catalog <em>puppetd</em> contacts the master under its SSL protected links and asks for this url:</p>
<script src="http://gist.github.com/339348.js"></script><noscript><code class="gist"><pre></p>
<pre>/production/catalog/node.daysofwonder.com?facts=&lt;encodedfacts&gt;&amp;facts_format=b64_zlib_yaml</pre>
<p></pre></code></noscript>
<p>In return the puppetmaster responds by a json-encoded catalog.<br />
The actual compilation of a catalog for one of my largest host takes about 4s (excluding storeconfigs). During this 4s one ruby thread inside the master is using the CPU. And this is done once every 30 minutes, even if the manifests don&#8217;t change.</p>
<p>What if we could compile only when something changes? This would really free our masters!</p>
<p>Since puppet uses HTTP, it is easy to add a front-most HTTP cache in front of our master to actually cache the catalog the first time it is compiled and serve this one on the subsequent requests.</p>
<p>Although we can do it with any HTTP Cache (ie Varnish), this is really easy to add this with Nginx (which is already running in my own stack):</p>
<script src="http://gist.github.com/339353.js"></script><noscript><code class="gist"><pre></p>
<pre># define a proxy cache called 'puppetcache'
# with an in-memory zone of 10MiB (increase this number if you want to be able to cache
# more keys)
# the cache disk path should be on the same filesystem as the proxy_temp_path
proxy_cache_path  /var/cache/nginx/cache  levels=1:2   keys_zone=puppetcache:10m;

server {

    ... normal nginx for puppet config...

    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-SSL-Subject    $ssl_client_s_dn;
    proxy_set_header    X-SSL-Issuer     $ssl_client_i_dn;
    proxy_buffer_size   16k;
    proxy_buffers       8 32k;
    proxy_busy_buffers_size    64k;
    proxy_temp_file_write_size 64k;
    proxy_read_timeout  65;

    # we handle catalog differently
    # because we want to cache them
    location /production/catalog {
        proxy_pass          http://puppet-production;
        proxy_redirect      off;

        # it is a good thing to actually restrict who
        # can ask for a catalog (especially for cached
        # catalogs)
        allow 172.16.10.0/24;
        allow 127.0.0.0/8;
        deny all;

        # where to cache contents
        proxy_cache         puppetcache;

        # we cache content by catalog host
        # we could also use $args to take into account request
        # facts, but those change too often (ie uptime or memory)
        # to be really usefull
        proxy_cache_key     $uri;

        # define how long to cache response

        # normal catalogs will be cached 2 weeks
        proxy_cache_valid  200 302 301 2w;

        # errors are not cached long
        proxy_cache_valid  500 403 1m;

        # the rest is cached a little bit
        proxy_cache_valid  any 30m;
    }

    # catch all location for other terminii
    location / {
        proxy_pass          http://puppet-production;
        proxy_redirect      off;
    }
}</pre>
<p></pre></code></noscript>
<p>Puppet currently doesn&#8217;t return any http caching headers (ie Cache-Control or Expires), so we use nginx ability to cache despite it (see proxy_cache_valid). Of course I have a <a href="http://github.com/masterzen/puppet/tree/features/http-catalog-cache" onclick="javascript:pageTracker._trackPageview('/outbound/article/github.com');">custom puppet branch</a> that introduces a new parameter called <em>&#8211;catalog_ttl</em> which allows puppet to set those cache headers.</p>
<p>One thing to note is that the <em>cache expiration won&#8217;t coincide with when you change your manifests</em>. So we need some ways to purge the cache when you deploy new manifests.</p>
<p>With Nginx this can be done with:</p>
<ul>
<li>removing the nginx cache directory: rm -rf /var/cache/nginx/cache &amp;&amp; killall -HUP nginx</li>
<li>selectively purge with: the <a href="http://github.com/FRiCKLE/ngx_cache_purge" onclick="javascript:pageTracker._trackPageview('/outbound/article/github.com');">Nginx proxy cache purge module</a>.</li>
</ul>
<p>It&#8217;s easy to actually add one of those methods to any <em>svn hook</em> or <em>git post-receive hook</em> so that deploying manifests actually purge the cache.</p>
<p>Note: I think that ReductiveLabs has some plan to add catalog compilation caching directly to Puppet (which would make sense). This method is the way to go before this features gets added to Puppet. I have no doubt that caching inside Puppet will be much better than outside caching, mainly because Puppet would be able to expire the cache when the manifests change.</p>
<p>There a few caveats to note:</p>
<ul>
<li>any host with a valid certificate can request another cached catalog, unlike with the normal puppetmaster which makes sure to serve catalogs only to the correct host. It&#8217;s something that can be a problem for some configurations</li>
<li>if your manifests rely on &#8220;dynamic&#8221; facts (like uptime or free memory), obviously you shouldn&#8217;t cache the catalog at all.</li>
<li>the above nginx configuration doesn&#8217;t include the facts as part of the cache key. That means the catalog won&#8217;t be re-generated when any facts change and the cached catalog will always be served. If that&#8217;s an issue, you need to purge the cache when the host itself change.</li>
</ul>
<p>I should also mention that caching is certainly not the panacea of reducing the master load.</p>
<p>Some other people are using clever methods to smooth out master load. One notable example is the <a href="http://www.devco.net/archives/2010/03/17/scheduling_puppet_with_mcollective.php" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.devco.net');">MCollective puppet scheduler</a>, <a href="http://twitter.com/ripienaar" onclick="javascript:pageTracker._trackPageview('/outbound/article/twitter.com');">R.I Pienaar</a> has written. In essence he wrote a <em>puppet run scheduler</em> running on top of <a href="http://code.google.com/p/mcollective/" onclick="javascript:pageTracker._trackPageview('/outbound/article/code.google.com');">MCollective</a> that schedule puppet runs (triggered through MCollective) when the master load is appropriate. This allows for the best use of the host running the master.</p>
<p>If you also have some tricks or tips for running puppet, do not hesitate to contact me (I&#8217;m masterzen on freenode&#8217;s #puppet or <a href="http://twitter.com/_masterzen_" onclick="javascript:pageTracker._trackPageview('/outbound/article/twitter.com');">@_masterzen_</a> on twitter).</p>
<img src="http://feeds.feedburner.com/~r/MasterzenPuppet/~4/x71qXOlDfyU" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.masterzen.fr/2010/03/21/more-puppet-offloading/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://www.masterzen.fr/2010/03/21/more-puppet-offloading/</feedburner:origLink></item>
		<item>
		<title>Puppet Memory Usage – not a fatality</title>
		<link>http://feeds.masterzen.fr/~r/MasterzenPuppet/~3/tVVnJ3c2cc0/</link>
		<comments>http://www.masterzen.fr/2010/01/28/puppet-memory-usage-not-a-fatality/#comments</comments>
		<pubDate>Thu, 28 Jan 2010 21:43:33 +0000</pubDate>
		<dc:creator>masterzen</dc:creator>
				<category><![CDATA[Nginx]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Puppet]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[System Administration]]></category>
		<category><![CDATA[allocator]]></category>
		<category><![CDATA[file serving]]></category>
		<category><![CDATA[heap]]></category>
		<category><![CDATA[jruby]]></category>
		<category><![CDATA[json]]></category>
		<category><![CDATA[memory]]></category>
		<category><![CDATA[memory consumption]]></category>
		<category><![CDATA[memory hog]]></category>
		<category><![CDATA[puppet]]></category>
		<category><![CDATA[REST]]></category>
		<category><![CDATA[YAML]]></category>

		<guid isPermaLink="false">http://www.masterzen.fr/?p=168</guid>
		<description><![CDATA[As every reader of this blog certainly know, I&#8217;m a big fan of Puppet, using it in production on Days of Wonder servers, up to the point I used to contribute regularly bug fixes and new features (not that I stopped, it&#8217;s just that my spare time is a scarce resource nowadays).
Still, I think there [...]]]></description>
			<content:encoded><![CDATA[<p>As every reader of this blog certainly know, I&#8217;m a big fan of <a href="http://reductivelabs.com/products/" onclick="javascript:pageTracker._trackPageview('/outbound/article/reductivelabs.com');">Puppet,</a> using it in production on <a href="http://www.daysofwonder.com" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.daysofwonder.com');">Days of Wonder</a> servers, up to the point I used to contribute regularly bug fixes and new features (not that I stopped, it&#8217;s just that my spare time is a scarce resource nowadays).</p>
<p>Still, I think there are some issues in term of scalability or resource consumption (CPU or memory), for which we can find some workarounds or even fixes. Those issues are not a symptom bad programming or bad design. No, most of the issues come either from ruby itself or some random library issues.</p>
<p>Let&#8217;s review the things I have been thinking about lately.</p>
<h2>Memory consumption</h2>
<p>This is by far one of the most seen issues both on the client side and the server side. I&#8217;ve mainly seen this problem on the client side, up to the point that most people recommend running puppetd as cronjobs, instead of being a long lived process.</p>
<h3>Ruby allocator</h3>
<p>All boils down to the ruby (at least the the MRI 1.8.x version) allocator. This is the part in the ruby interpreter that deals with memory allocations. Like in many dynamic languages, the allocator manages a memory pool that is called a<a href="http://en.wikipedia.org/wiki/Dynamic_memory_allocation" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"> heap</a>. And like some other languages (among them Java), this heap can <strong>never shrink and always grows</strong> when more memory is needed. This is done this way because it is simpler and way faster. Usually applications ends using their nominal part of memory and no more memory has to be allocated by the kernel to the process, which gives faster applications.</p>
<p>The problem is that if the application needs transiently a high amount of memory that will be trashed a couple of millisecond after, the process will pay this penalty all its life, even though say 80% of the memory used by the process is free but not reclaimed by the OS.</p>
<p><em>And it&#8217;s even worst</em>. The ruby interpreter when it grows the heap, instead of allocating bytes per bytes (which would be really slow) does this by chunk. The whole question is what is the proper size of a chunk?</p>
<p>In the default implementation of MRI 1.8.x, a chunk is the size of the previous heap times 1.8. That means at worst a ruby process might end up allocating 1.8 times more than what it really needs at a given time. (This is a gross simplification, read the code if you want to know more).</p>
<h3>Yes but what happens in Puppet?</h3>
<p>So how does it apply to <em>puppetd</em>?</p>
<p>It&#8217;s easy, <em>puppetd</em> uses memory for two things (beside maintaining some core data to be able to run):</p>
<ol>
<li>the <strong>catalog</strong> (which contains all resources, along with all templates) as shipped by the <em>puppetmaster</em> (i.e. serialized) and live as ruby objects.</li>
<li>the <strong>content of the sourced</strong> files (one at a time, so it&#8217;s the biggest transmitted file that imposes it&#8217;s high watermark for <em>puppetd</em>). Of course this is still better than in 0.24 where the content was transmitted encoded in XMLRPC adding the penalty of escaping everything&#8230;</li>
</ol>
<p>Hopefully, <strong>nobody distributes large files with Puppet <img src='http://www.masterzen.fr/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </strong> If you&#8217;re tempted to do so, see below&#8230;</p>
<p>But again there&#8217;s more, as <em>Peter Meier</em> (known as duritong in the community) <a href="http://groups.google.com/group/puppet-dev/browse_thread/thread/17e901f2613b9c27/552469109dac1f91?lnk=gst&amp;q=+Possible+workaround+for+%232824#552469109dac1f91" onclick="javascript:pageTracker._trackPageview('/outbound/article/groups.google.com');">discovered a couple of month ago</a>: when <em>puppetd</em> gets its <em>catalog</em> (which by the way is transmitted in <a href="http://www.json.org/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.json.org');">json</a> nowadays), it also stores it as a local cache to be able to run if it can&#8217;t contact the master for a subsequent run. This operation is done by unserializing the catalog from json to ruby live objects, and then serializing the laters to <a href="http://www.yaml.org/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.yaml.org');">YAML</a>. Beside the <strong>evident loss of time</strong> to do that on large catalog, YAML is a real memory hog. Peter&#8217;s experience showed that about 200MB of live memory his <em>puppetd</em> process was using came from this final serialization!</p>
<p>So I had the following idea: why not store the serialized version of the catalog (the json one) since we already have it in a serialized form when we receive it from the master (it&#8217;s a little bit more complex than that of course). This way no need to serialize it again in YAML. This is what <a href="http://projects.reductivelabs.com/issues/2892" onclick="javascript:pageTracker._trackPageview('/outbound/article/projects.reductivelabs.com');">ticket #2892 is all about.</a> <a href="http://www.madstop.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.madstop.com');">Luke</a> is committed to have this enhancement in Rowlf, so there&#8217;s good hope!</p>
<h3>Some puppet solutions?</h3>
<p>So what can we do to help puppet not consume that many memory?</p>
<p>In <em>theory we could play on several factors</em>:</p>
<ul>
<li><strong>Transmit smaller catalogs</strong>. For instance get rid of all those templates you love (ok that&#8217;s not a solution)</li>
<li>Stream the serialization/deserialization with something like <a href="http://github.com/brianmario/yajl-ruby" onclick="javascript:pageTracker._trackPageview('/outbound/article/github.com');">Yajl-Ruby</a></li>
<li>Use another <strong>ruby interpreter with a better allocator</strong> (like for instance JRuby)</li>
<li>Use a <strong>different constant for resizing the heap</strong> (ie replace this 1.8 by 1.0 or less on line 410 of gc.c). This can be done easily when using Rails machine GC patches or Ruby Enterprise Edition, in which case setting the environment variable  <strong><tt>RUBY_HEAP_SLOTS_GROWTH_FACTOR</tt></strong> is enough. Check the <a href="http://www.rubyenterpriseedition.com/documentation.html#_garbage_collector_performance_tuning" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.rubyenterpriseedition.com');">documentation for more information</a>.</li>
<li><strong>Stream the sourced file on the server and the client</strong> (this way only a small buffer is used, and the total size of the file is never allocated). This one is hard.</li>
</ul>
<p>Note that the same issues apply to the master too (especially for the file serving part). But it&#8217;s usually easier to run a different ruby interpreter (like REE) on the master than on all your clients.</p>
<p>Streaming HTTP requests is promising but unfortunately would require large change to how Puppet deals with HTTP. Maybe it can be done only for file content requests&#8230; This is something I&#8217;ll definitely explore.</p>
<p>This file serving thing let me think about the following which I already discussed several time with Peter&#8230;</p>
<h2>File serving offloading</h2>
<p>One of the mission of the <em>puppetmaster</em> is to serve sourced file to its clients. We saw in the previous section that to do that the master has to read the file in memory. That&#8217;s <a href="http://reductivelabs.com/trac/puppet/wiki/PuppetScalability." onclick="javascript:pageTracker._trackPageview('/outbound/article/reductivelabs.com');">one reason it is recommended</a> to use a dedicated puppetmaster server to act as a <strong>pure fileserver</strong>.</p>
<p>But <strong>there&#8217;s a better way</strong>, provided you run puppet behind <a href="http://nginx.org/en/" onclick="javascript:pageTracker._trackPageview('/outbound/article/nginx.org');">nginx</a> or <a href="http://httpd.apache.org/" onclick="javascript:pageTracker._trackPageview('/outbound/article/httpd.apache.org');">apache</a>. Those two proxies are also static file servers: why not leverage what they do best to serve the sourced files and thus offload our puppetmaster?</p>
<p>This has some advantages:</p>
<ul>
<li>it frees lots of resources on the puppetmaster, so that they can serve more catalogs by unit time</li>
<li>the job will be done faster and by using less resources. Those static servers have been created to spoon-feed our puppet clients&#8230;</li>
</ul>
<p>In fact it was impossible in 0.24.x, but now that file content serving is <a href="http://en.wikipedia.org/wiki/Representational_State_Transfer" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');">RESTful</a> it becomes trivial.</p>
<p>Of course offloading would give its best if your clients requires lots of sourced files that change often, or if you provision lots of new hosts at the same time because we&#8217;re offloading only content, not file metadata. File content is served only if the client hasn&#8217;t the file or the file checksum on the client is different.</p>
<h3>An example is better than thousand words</h3>
<p>Imagine we have a standard manifest layout with:</p>
<ul>
<li> some globally sourced files under /etc/puppet/files and</li>
<li>some modules files under /etc/puppet/modules/&lt;modulename&gt;/files.</li>
</ul>
<p>Here is what would be the <em>nginx configuration</em> for such scheme:</p>
<pre class="syntax-highlight:bash">
server {
    listen 8140;

    ssl                     on;
    ssl_session_timeout     5m;
    ssl_certificate         /var/lib/puppet/ssl/certs/master.pem;
    ssl_certificate_key     /var/lib/puppet/ssl/private_keys/master.pem;
    ssl_client_certificate  /var/lib/puppet/ssl/ca/ca_crt.pem;
    ssl_crl                 /var/lib/puppet/ssl/ca/ca_crl.pem;
    ssl_verify_client       optional;

    root                    /etc/puppet;

    # those locations are for the &quot;production&quot; environment
    # update according to your configuration

    # serve static file for the [files] mountpoint
    location /production/file_content/files/ {
        # it is advisable to have some access rules here
        allow   172.16.0.0/16;
        deny    all;

        # make sure we serve everything
        # as raw
        types { }
        default_type application/x-raw;

        alias /etc/puppet/files/;
    }

    # serve modules files sections
    location ~ /production/file_content/[^/]+/files/ {
        # it is advisable to have some access rules here
        allow   172.16.0.0/16;
        deny    all;

        # make sure we serve everything
        # as raw
        types { }
        default_type application/x-raw;

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

    # ask the puppetmaster for everything else
    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-SSL-Subject    $ssl_client_s_dn;
        proxy_set_header    X-SSL-Issuer     $ssl_client_i_dn;
        proxy_buffer_size   16k;
        proxy_buffers       8 32k;
        proxy_busy_buffers_size    64k;
        proxy_temp_file_write_size 64k;
        proxy_read_timeout  65;
    }
}
</pre>
<p><strong>EDIT:</strong> the above configuration was missing the only content-type that nginx can return for Puppet to be able to actually receive the file content (that is raw).</p>
<p>I leave as an exercise to the reader the apache configuration.</p>
<p>It would also be possible to write some ruby/sh/whatever to generate the nginx configuration from the puppet fileserver.conf file.</p>
<p>And that&#8217;s all folks, stay tuned for more Puppet (or even different) content.</p>
<img src="http://feeds.feedburner.com/~r/MasterzenPuppet/~4/tVVnJ3c2cc0" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.masterzen.fr/2010/01/28/puppet-memory-usage-not-a-fatality/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		<feedburner:origLink>http://www.masterzen.fr/2010/01/28/puppet-memory-usage-not-a-fatality/</feedburner:origLink></item>
		<item>
		<title>My Puppet Camp slides appearing on the slideshare homepage!</title>
		<link>http://feeds.masterzen.fr/~r/MasterzenPuppet/~3/j9bJ3QMweoY/</link>
		<comments>http://www.masterzen.fr/2009/10/13/my-puppet-camp-slides-appearing-on-the-slideshare-homepage/#comments</comments>
		<pubDate>Tue, 13 Oct 2009 21:48:53 +0000</pubDate>
		<dc:creator>masterzen</dc:creator>
				<category><![CDATA[Puppet]]></category>
		<category><![CDATA[System Administration]]></category>
		<category><![CDATA[pictures]]></category>
		<category><![CDATA[puppet]]></category>
		<category><![CDATA[puppetcamp]]></category>
		<category><![CDATA[puppetcamp09]]></category>
		<category><![CDATA[slideshare]]></category>

		<guid isPermaLink="false">http://www.masterzen.fr/?p=110</guid>
		<description><![CDATA[This morning I got the joy to see that my Puppet Camp 2009 slides had been selected by Slideshare to appear on their home page:
Waouh. For a surprise, that&#8217;s a surprise. I guess those stock photos I used are the underlying reason for this.
Still now that I talk about Puppet Camp again, I forgot to [...]]]></description>
			<content:encoded><![CDATA[<p>This morning I got the joy to see that my Puppet Camp 2009 slides had been selected by Slideshare to appear on their <a href="http://www.slideshare.net" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.slideshare.net');">home page</a>:</p>
<p>Waouh. For a surprise, that&#8217;s a surprise. I guess those stock photos I used are the underlying reason for this.</p>
<p>Still now that I talk about Puppet Camp again, I forgot to give the links to some pictures taken during the event:</p>
<ul>
<li><a href="http://www.flickr.com/photos/43103276@N07/sets/72157622370691217/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.flickr.com');">the ones taken by Glarizza</a> (sorry I don&#8217;t know your real name)</li>
</ul>
<p>and</p>
<ul>
<li><a href="http://www.flickr.com/photos/halliganfamily/tags/puppetcamp/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.flickr.com');">those from Michael Halligan</a></li>
</ul>
<img src="http://feeds.feedburner.com/~r/MasterzenPuppet/~4/j9bJ3QMweoY" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.masterzen.fr/2009/10/13/my-puppet-camp-slides-appearing-on-the-slideshare-homepage/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.masterzen.fr/2009/10/13/my-puppet-camp-slides-appearing-on-the-slideshare-homepage/</feedburner:origLink></item>
		<item>
		<title>Puppet Camp 2009 debriefing time!</title>
		<link>http://feeds.masterzen.fr/~r/MasterzenPuppet/~3/aw1M-QeleYM/</link>
		<comments>http://www.masterzen.fr/2009/10/05/puppet-camp-2009-debriefing-time/#comments</comments>
		<pubDate>Mon, 05 Oct 2009 01:42:52 +0000</pubDate>
		<dc:creator>masterzen</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Puppet]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[System Administration]]></category>
		<category><![CDATA[presentation]]></category>
		<category><![CDATA[puppet]]></category>
		<category><![CDATA[puppetcamp]]></category>
		<category><![CDATA[puppetcamp09]]></category>
		<category><![CDATA[storeconfigs]]></category>

		<guid isPermaLink="false">http://www.masterzen.fr/?p=105</guid>
		<description><![CDATA[I attended Puppet Camp 2009 in San Francisco last week. It was a wonderful event and I could meet a lot of really smart developers and sysadmins from a lot of different countries (US, Australia, Europe and even Singapore).
The format of the event (an unconference with some scheduled talks in the morning) was really great. [...]]]></description>
			<content:encoded><![CDATA[<p>I attended <a href="http://reductivelabs.com/home/community/puppetcamp/" onclick="javascript:pageTracker._trackPageview('/outbound/article/reductivelabs.com');">Puppet Camp 2009</a> in San Francisco last week. It was a wonderful event and I could meet a lot of really smart developers and sysadmins from a lot of different countries (US, Australia, Europe and even Singapore).</p>
<p>The format of the event (an unconference with some scheduled talks in the morning) was really great. Everybody got a chance to enter or propose a discussion topic they care about. I could attend some development sessions about the Ruby DSL vs Parser DSL, Code smells, Puppet Provider/Type developments, <a href="http://augeas.net/" onclick="javascript:pageTracker._trackPageview('/outbound/article/augeas.net');">Augeas</a>, and so on&#8230;</p>
<p>Morning talks were awesome. I was presenting a talk about storeconfigs, called &#8220;All About Storeconfigs&#8221;. Puppet Storeconfigs is a feature where you can store nodes configuration and export/collect resources between nodes with the help of a database. I already talked about this in a couple of posts:</p>
<ul>
<li><a title="Permanent Link to Storeconfigs (advanced) use cases" rel="bookmark" href="../2009/08/08/storeconfigs-use-cases/">Storeconfigs (advanced) use cases</a></li>
<li><a title="Permanent Link to OMG!! storedconfigs killed my database!" rel="bookmark" href="../2009/03/18/omg-storedconfigs-killed-my-database/">OMG!! storedconfigs killed my database!</a></li>
<li><a title="Permanent Link to All about Puppet storeconfigs" rel="bookmark" href="../2009/03/08/all-about-puppet-storeconfigs/">All about Puppet storeconfigs</a></li>
</ul>
<p>You can enjoy <a href="http://coursestream.sfsu.edu/ess/feed?id=e723afa9-1748-43c7-8231-180d2a7f7d3e&amp;type=MP3" onclick="javascript:pageTracker._trackPageview('/outbound/article/coursestream.sfsu.edu');">the recording of the session</a> (event though they cut the first part which is not that good), and have closer look to my slides here:</p>
<div id="__ss_2123814" style="width: 425px; text-align: left;"><a style="font:14px Helvetica,Arial,Sans-serif;display:block;margin:12px 0 3px 0;text-decoration:underline;" title="All About Storeconfigs" href="http://www.slideshare.net/masterzen/all-about-storeconfigs-2123814" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.slideshare.net');">All About Storeconfigs</a><object width="425" height="355" data="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=storeconfigs-091004202239-phpapp02&amp;stripped_title=all-about-storeconfigs-2123814" type="application/x-shockwave-flash"><param name="allowFullScreen" value="true" /><param name="allowScriptAccess" value="always" /><param name="src" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=storeconfigs-091004202239-phpapp02&amp;stripped_title=all-about-storeconfigs-2123814" /><param name="allowfullscreen" value="true" /></object></p>
<div style="font-size: 11px; font-family: tahoma,arial; height: 26px; padding-top: 2px;">View more <a style="text-decoration:underline;" href="http://www.slideshare.net/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.slideshare.net');">documents</a> from <a style="text-decoration:underline;" href="http://www.slideshare.net/masterzen" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.slideshare.net');">Brice Figureau</a>.</div>
</div>
<p>What&#8217;s great with those conferences in foreign countries is that you usually finish at the pub with some local people to continue to share Puppet (or not) experiences. Those parties were plenty of fun, so thank you everybody for this.</p>
<p>So thanks everybody and Reductive Labs team (especially Andrew who organized everything) for this event, and thanks to <a href="http://www.daysofwonder.com" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.daysofwonder.com');">Days of Wonder</a> for funding my trip!</p>
<img src="http://feeds.feedburner.com/~r/MasterzenPuppet/~4/aw1M-QeleYM" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.masterzen.fr/2009/10/05/puppet-camp-2009-debriefing-time/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		<feedburner:origLink>http://www.masterzen.fr/2009/10/05/puppet-camp-2009-debriefing-time/</feedburner:origLink></item>
		<item>
		<title>Storeconfigs (advanced) use cases</title>
		<link>http://feeds.masterzen.fr/~r/MasterzenPuppet/~3/vHjz9yidH4s/</link>
		<comments>http://www.masterzen.fr/2009/08/08/storeconfigs-use-cases/#comments</comments>
		<pubDate>Sat, 08 Aug 2009 11:49:59 +0000</pubDate>
		<dc:creator>masterzen</dc:creator>
				<category><![CDATA[Puppet]]></category>
		<category><![CDATA[System Administration]]></category>
		<category><![CDATA[puppet]]></category>
		<category><![CDATA[storeconfigs]]></category>
		<category><![CDATA[storedconfigs]]></category>

		<guid isPermaLink="false">http://www.masterzen.fr/?p=85</guid>
		<description><![CDATA[This week on #puppet, Nico asked for a storeconfigs live example. So I thought, a blog post would be perfect to post an example of a storeconfigs use case and its full explanation. Of course if you&#8217;re interested in some discussions around storeconfigs, please report to the following blog posts:

All about storeconfigs
OMG!!! Storeconfigs killed my [...]]]></description>
			<content:encoded><![CDATA[<p>This week on <a href="http://reductivelabs.com/trac/puppet/wiki/IrcChannel" onclick="javascript:pageTracker._trackPageview('/outbound/article/reductivelabs.com');">#puppet</a>, <a href="http://www.rottenbytes.info/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.rottenbytes.info');">Nico</a> asked for a <a href="http://reductivelabs.com/trac/puppet/wiki/UsingStoredConfiguration" onclick="javascript:pageTracker._trackPageview('/outbound/article/reductivelabs.com');">storeconfigs</a> live example. So I thought, a blog post would be perfect to post an example of a <em>storeconfigs</em> use case and its full explanation. Of course if you&#8217;re interested in some discussions around storeconfigs, please report to the following blog posts:</p>
<ul>
<li><a href="http://www.masterzen.fr/2009/03/08/all-about-puppet-storeconfigs/" >All about storeconfigs</a></li>
<li><a href="http://www.masterzen.fr/2009/03/18/omg-storedconfigs-killed-my-database/" >OMG!!! Storeconfigs killed my database</a></li>
</ul>
<p>At <a href="http://www.daysofwonder.com" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.daysofwonder.com');">Days of Wonder</a>, I use <em>storeconfigs</em> for only one type of use: <strong>exchanging information between nodes</strong>. But I know some other people use this feature as an <strong>inventory system</strong> (to know what node gets what configuration).</p>
<h2>Use case 1: website document root replication</h2>
<p>Let&#8217;s start with a simple example, easily understandable.</p>
<p>At Days of Wonder we have a bunch of webservers arranged in a kind of cluster. All these webservers document root (where reside the various php and image files) should be always in sync. So we rsync to all webservers, from a central build server each time the developpers commit a change.</p>
<p>The tedious part with this scheme is that you have to make sure all the webservers have the correct ssh authorized_keys and ssh authorization for the build server to contact them successfully.</p>
<h4>The manifest</h4>
<pre class="syntax-highlight:ruby">

# Class:: devl
# This class is implemented on the build server
#
# Usage:
# Generate a ssh key and store the private key and public key
# on the puppetmaster files mount as keys/buildkey and keys/buildkey.pub
#
#   node build {
#       include devl
#       devl::pushkey{
#           &quot;build&quot;:
#               keyfile =&gt; &quot;files/keys/buildkey&quot;
#       }
#   }
#
#
class devl {
    ...
    define pushkey($keyfile) {
        @@ssh_authorized_key {
            &quot;push-${name}@${fqdn}&quot;:
                user =&gt; &quot;push&quot;,
                type =&gt; &quot;ssh-rsa&quot;,
                tag =&gt; &quot;push&quot;,
                # this is to remove the ssh-rsa prefix, the suffix and trim any \n
                key =&gt; gsub(gsub(file(&quot;/etc/puppet/${keyfile}.pub&quot;), &#039;^ssh-rsa (.*) .*$&#039;, &#039;\1&#039;), &quot;\n&quot;, &quot;&quot;),
                options =&gt; [&#039;command=&quot;rsync --server -vlgDtpr --delete . /path/to/docroot/&quot;&#039;, &#039;no-port-forwarding&#039;,&#039;no-X11-forwarding&#039;,&#039;no-agent-forwarding&#039;,&#039;no-pty&#039;],
        }

        # store the private key locally, for our rsync build
        file {
            &quot;/home/build/.ssh/id_${name}&quot;:
                ensure =&gt; file, owner =&gt; &quot;build&quot;, group =&gt; &quot;build&quot;,
                source =&gt; &quot;puppet:///${keyfile}&quot;, mode =&gt; 0400,
                alias =&gt; &quot;pkey-${name}&quot;,
                require =&gt; [User[&quot;build&quot;], File[&quot;/home/build/.ssh&quot;]]
        }
    }
    ...
}

# Class: www::push
# This class is implemented on webservers
#
class www::push {
    ... create here the push user and so on...
    Ssh_authorized_key &lt;&lt;| tag == &quot;push&quot; |&gt;&gt;
    ...
}
</pre>
<h4>Inner workings<em><br />
</em></h4>
<p>It&#8217;s easy when the build server applies its configuration, it creates an exported ssh_authorized_key (notice the double @), which is not applied locally. Instead it is stored in the storeconfigs database.</p>
<p>We also create locally a file containing the ssh private key pair.</p>
<p>When one of the webserver comes to check out its configuration, it implements the www::push class which collects all ssh_authorized_key resources tagged with &#8220;push&#8221;.</p>
<p>That is all the authorized keys we created with the pushkey definition in the build configuration. The collection means that this resource is created as if we defined it in the node that collects it. That means the webserver will have a new ssh authorized key whose action, options and keys are the one defined in the build server configuration.</p>
<p>Of course this manifest doesn&#8217;t show everything, it also drops a handful of shell scripts to do the rsync using the local private keys, along with more configuration files for some other parts of the build.</p>
<p>Note: the gsub function is a custom parser function I borrowed from <a href="http://dasz.at/" onclick="javascript:pageTracker._trackPageview('/outbound/article/dasz.at');">David Schmidtt</a> repository. In 0.25 it would be replaced by regsubst.</p>
<h2>Use case 2: tinydns master and slaves</h2>
<p>Once again at Days of Wonder, we run <a href="http://cr.yp.to/djbdns.html" onclick="javascript:pageTracker._trackPageview('/outbound/article/cr.yp.to');">tinydns</a> as our DNS server. <em>Tinydns</em> doesn&#8217;t have a fancy full of security holes zone transfer system, so we emulate this functionality by rsync&#8217;ing the zone files from the master to the slaves each time the zones are changed (the zones are managed by Puppet of course).</p>
<p>This is somehow the exact same system as the one we saw in the <strong>use case 1</strong>, except there is one key for all the slaves, and more important <strong>each slave registers itself to the master</strong> to be part of the replication.</p>
<h4>The manifest</h4>
<pre class="syntax-highlight:ruby">
class djbdns {
    ...

    # Define: tinydns::master
    # define a master with its listening +ip+, +keyfile+, and zonefile.
    # Usage:
    #     djbdns::tinydns::master {
    #         &quot;root&quot;:
    #             keyfile =&gt; &quot;files/keys/tinydns&quot;,
    #             content =&gt; &quot;files/dow/zone&quot;
    #     }
    #
    define tinydns::master($ip, $keyfile, $content=&#039;&#039;) {
        $root = &quot;/var/lib/service/${name}&quot;
        tinydns::common { $name: ip =&gt; $ip, content=&gt;$content }

        # send our public key to our slaves
        @@ssh_authorized_key {
            &quot;dns-${name}@${fqdn}&quot;:
                user =&gt; &quot;root&quot;,
                type =&gt; &quot;ssh-rsa&quot;,
                tag =&gt; &quot;djbdns-master&quot;,
                key =&gt; file(&quot;/etc/puppet/${keyfile}.pub&quot;),
                options =&gt; [&quot;command=\&quot;rsync --server -logDtprz . ${root}/root/data.cdb\&quot;&quot;, &quot;from=\&quot;${fqdn}\&quot;&quot;, &#039;no-port-forwarding&#039;,&#039;no-X11-forwarding&#039;,&#039;no-agent-forwarding&#039;,&#039;no-pty&#039;]
        }

        # store our private key locally
        file {
            &quot;/root/.ssh/${name}_identity&quot;:
            ensure =&gt; file,
            source =&gt; &quot;puppet://${keyfile}&quot;, mode =&gt; 0600,
            alias =&gt; &quot;master-pkey-${name}&quot;
        }

        # replicate with the help of the propagate-key script
        # this exec subscribe to the zone file and the slaves
        # which means each time we add a slave it is rsynced
        # or each time the zone file changes.
        exec {
            &quot;propagate-data-${name}&quot;:
                command =&gt; &quot;/usr/local/bin/propagate-key ${name} /var/lib/puppet/modules/djbdns/slaves.d /root/.ssh/${name}_identity&quot;,
                subscribe =&gt; [File[&quot;/var/lib/puppet/modules/djbdns/slaves.d&quot;] , File[&quot;${root}/root/data&quot;], Exec[&quot;data-${name}&quot;]],
                require =&gt; [File[&quot;/usr/local/bin/propagate-key&quot;], Exec[&quot;data-${name}&quot;]],
                refreshonly =&gt; true
        }

        # collect slaves address
        File&lt;&lt;| tag == &#039;djbdns&#039; |&gt;&gt;
    }

    # Define:: tinydns::slave
    # this define is implemented on each tinydns slaves
    define tinydns::slave($ip) {
        $root = &quot;/var/lib/service/${name}&quot;

        tinydns::common { $name: ip =&gt; $ip }

        # publish our addresses back to the master
        # our ip address ends up being in a file name in the slaves.d directory
        # where the propagate-key shell script will get it.
        @@file {
            &quot;/var/lib/puppet/modules/djbdns/slaves.d/${name}-${ipaddress}&quot;:
            ensure =&gt; file, content =&gt; &quot;\n&quot;,
            alias =&gt; &quot;slave-address-${name}&quot;,
            tag =&gt; &#039;djbdns&#039;
        }

        # collect the ssh public keys of our master
        Ssh_authorized_key &lt;&lt;| tag == &#039;djbdns-master&#039; |&gt;&gt;
    }
}</pre>
<h4>Inner workings</h4>
<p>This time we have a double exchange system:</p>
<ol>
<li> The master exports its public key to be collected by the slaves</li>
<li>and the slaves are exporting back their IP addresses to the master, under the form a of an empty file. Their IP address is encoded in those file names.</li>
</ol>
<p>When the zone file has to be propagated, the propagate-key shell script is executed. This script lists all the file in the /var/lib/puppet/djbdns/slaves.d folder where the slaves exports their ip addresses, extract the ip address from the file names and calls rsync with the correct private key. Simple and elegant, isn&#8217;t it?</p>
<h2>Other ideas</h2>
<p>There&#8217;s simply no limitation to what we can do with storeconfigs, because you can export any kind of resources, not only files or ssh authorized keys.</p>
<p>I&#8217;m giving here some ideas (some that we are implementing here):</p>
<ul>
<li>Centralized backups. Using rdiff-backup for instance, we could propagate the central backup server key to all servers, and get back the list of files to backup.</li>
<li>Resolv.conf building. This is something we&#8217;re doing at Days of Wonder. Each dnscache server exports their IP address, and we build resolv.conf on each host from those addresses.</li>
<li>Ntp automated configuration: each NTP server (of a high stratum) exports their ip address (or ntp.conf configuration fragments) that can be used for all the other NTP server to be pointed to those to form lower stratum servers.</li>
<li>Automated monitoring configurations: each service and node exports configuration fragments that are collected on the NMS host to build the NMS configuration. People running nagios or munin seems to do that.</li>
</ul>
<p>If you have some creative uses of storeconfigs, do not hesitate to publish them, either on the Puppet-user list, the Puppet wiki or elsewhere (and why not in a blog post that could be aggregated by <a href="http://www.planetpuppet.org/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.planetpuppet.org');">Planet Puppet</a>).</p>
<pre></pre>
<img src="http://feeds.feedburner.com/~r/MasterzenPuppet/~4/vHjz9yidH4s" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.masterzen.fr/2009/08/08/storeconfigs-use-cases/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.masterzen.fr/2009/08/08/storeconfigs-use-cases/</feedburner:origLink></item>
		<item>
		<title>Planet Puppet is born!</title>
		<link>http://feeds.masterzen.fr/~r/MasterzenPuppet/~3/5PxTtivJLWs/</link>
		<comments>http://www.masterzen.fr/2009/08/08/planet-puppet-is-born/#comments</comments>
		<pubDate>Sat, 08 Aug 2009 10:13:43 +0000</pubDate>
		<dc:creator>masterzen</dc:creator>
				<category><![CDATA[Puppet]]></category>
		<category><![CDATA[blog]]></category>
		<category><![CDATA[moonmoon]]></category>
		<category><![CDATA[planet]]></category>
		<category><![CDATA[planetpuppet]]></category>
		<category><![CDATA[puppet]]></category>

		<guid isPermaLink="false">http://www.masterzen.fr/?p=83</guid>
		<description><![CDATA[As usual, I&#8217;m faster to create things than to talk about them.
Last week, after talking with several member of #puppet, I decided to register planetpuppet.org, and to install moonmoon to aggregate the few Puppet blogs out there in the blogosphere.
The whole aim of this attempt is to provide more exposure to our own blogs (we [...]]]></description>
			<content:encoded><![CDATA[<p>As usual, I&#8217;m faster to create things than to talk about them.</p>
<p>Last week, after talking with several member of <a href="http://reductivelabs.com/trac/puppet/wiki/IrcChannel" onclick="javascript:pageTracker._trackPageview('/outbound/article/reductivelabs.com');">#puppet</a>, I decided to register <a href="http://www.planetpuppet.org/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.planetpuppet.org');">planetpuppet.org</a>, and to install <a href="http://moonmoon.org/" onclick="javascript:pageTracker._trackPageview('/outbound/article/moonmoon.org');">moonmoon</a> to aggregate the few Puppet blogs out there in the blogosphere.</p>
<p>The whole aim of this attempt is to provide more exposure to our own blogs (we have a sentence in France which basically says: &#8220;union makes the force&#8221;). This is not to be confused with <a href="http://store.puppeteers.org/index.php?main_page=product_info&amp;products_id=57" onclick="javascript:pageTracker._trackPageview('/outbound/article/store.puppeteers.org');">Puppet Planet</a></p>
<p>If you run a blog with a Puppet tag or category from which we can extract a RSS or Atom Feed, then please contact me or drop a comment here, and I&#8217;ll happily add it to the <a href="http://www.planetpuppet.org/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.planetpuppet.org');">Planet Puppet.</a></p>
<p>There are still some work to do for the site. For instance it looks ugly, has no logo, and there&#8217;s no explanation of what it is. My plan is to add this incrementally; I wanted to have first the site up and running. And since I plain suck at graphic design, I&#8217;ll wait some Days of Wonder co-worker vacation return to ask them for some help on this area <img src='http://www.masterzen.fr/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>Meanwhile, do not forget to visit Planet Puppet from time to time (once a day would be good!). It is also possible to subscribe to the <a href="http://www.planetpuppet.org/?type=atom10" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.planetpuppet.org');">Planet Puppet feed</a>.</p>
<img src="http://feeds.feedburner.com/~r/MasterzenPuppet/~4/5PxTtivJLWs" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.masterzen.fr/2009/08/08/planet-puppet-is-born/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.masterzen.fr/2009/08/08/planet-puppet-is-born/</feedburner:origLink></item>
		<item>
		<title>New SSL features for Nginx</title>
		<link>http://feeds.masterzen.fr/~r/MasterzenPuppet/~3/5PhZAlGA0ko/</link>
		<comments>http://www.masterzen.fr/2009/07/21/new-ssl-features-for-nginx/#comments</comments>
		<pubDate>Tue, 21 Jul 2009 17:54:16 +0000</pubDate>
		<dc:creator>masterzen</dc:creator>
				<category><![CDATA[Nginx]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Puppet]]></category>
		<category><![CDATA[System Administration]]></category>
		<category><![CDATA[crl]]></category>
		<category><![CDATA[patch]]></category>
		<category><![CDATA[ssl]]></category>

		<guid isPermaLink="false">http://www.masterzen.fr/?p=74</guid>
		<description><![CDATA[As a Puppet Mongrel Nginx user, I&#8217;m really ashamed about the convoluted nginx configuration needed (two server blocks listening on different ports, you need to direct your clients CA interactions to the second port with &#8211;ca_port), and the lack of support of proper CRL verification.
If you are like me, then there is some hope in [...]]]></description>
			<content:encoded><![CDATA[<p>As a <a href="http://reductivelabs.com/trac/puppet/wiki/UsingMongrelNginx" onclick="javascript:pageTracker._trackPageview('/outbound/article/reductivelabs.com');">Puppet Mongrel Nginx</a> user, I&#8217;m really ashamed about the convoluted nginx configuration needed (two server blocks listening on different ports, you need to direct your clients CA interactions to the second port with &#8211;ca_port), and the lack of support of proper CRL verification.</p>
<p>If you are like me, then there is some hope in this blog post.</p>
<p>Last week-end, I did some intense Puppet hacking (certainly more news about this soon), and part of this work is two Nginx patch:</p>
<ul>
<li>The first one adds support for ssl_client_verify optional. In this mode nginx accepts a client without a certificate, and of course accepts a client as long as it verifies against the CA certificate.</li>
<li>The second patch adds support for CRL PEM files (the one we usually deploy in Puppet).</li>
</ul>
<h2>Installation</h2>
<p>First, download both patches:</p>
<ul>
<li><a href="http://www.masterzen.fr/patches/nginx/0001-Support-ssl_client_verify-optional-and-ssl_client_v.patch" >Support ssl_client_verify optional and $ssl_client_verify</a></li>
<li><a href="http://www.masterzen.fr/patches/nginx/0002-Add-SSL-CRL-verifications.patch" >Add SSL CRL verifications</a></li>
</ul>
<p>Then apply them to Nginx (tested on 0.7.59):</p>
<pre class="syntax-highlight:sh">
$ cd nginx-0.7.59
$ patch -p1 &lt; ../0001-Support-ssl_client_verify-optional-and-ssl_client_v.patch
$ patch -p1 &lt; ../0002-Add-SSL-CRL-verifications.patch
</pre>
<p>Then build Nginx as usual.</p>
<h2>Usage</h2>
<p>Here is a revised Puppet Nginx Mongrel configuration:</p>
<pre class="syntax-highlight:sh">
upstream puppet-production {
  server 127.0.0.1:18140;
  server 127.0.0.1:18141;
}

server {
  listen 8140;

  ssl                     on;
  ssl_session_timeout     5m;
  ssl_certificate         /var/lib/puppet/ssl/certs/puppetmaster.pem;
  ssl_certificate_key     /var/lib/puppet/ssl/private_keys/puppetmaster.pem;
  ssl_client_certificate  /var/lib/puppet/ssl/ca/ca_crt.pem;
  ssl_ciphers             SSLv2:-LOW:-EXPORT:RC4+RSA;
  # allow authenticated and client without certs
  ssl_verify_client       optional;
  # obey to the Puppet CRL
  ssl_crl /var/lib/puppet/ssl/ca/ca_crl.pem;

  root                    /var/tmp;

  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-SSL-Subject    $ssl_client_s_dn;
    proxy_set_header    X-SSL-Issuer     $ssl_client_i_dn;
    proxy_read_timeout  65;
  }
}
</pre>
<p>Reload nginx, and enjoy <img src='http://www.masterzen.fr/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<img src="http://feeds.feedburner.com/~r/MasterzenPuppet/~4/5PhZAlGA0ko" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.masterzen.fr/2009/07/21/new-ssl-features-for-nginx/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://www.masterzen.fr/2009/07/21/new-ssl-features-for-nginx/</feedburner:origLink></item>
		<item>
		<title>Puppet and JRuby a love story!</title>
		<link>http://feeds.masterzen.fr/~r/MasterzenPuppet/~3/q2Lp0VAac6c/</link>
		<comments>http://www.masterzen.fr/2009/05/24/puppet-and-jruby-a-love-story/#comments</comments>
		<pubDate>Sun, 24 May 2009 21:55:54 +0000</pubDate>
		<dc:creator>masterzen</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Puppet]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[System Administration]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[certificates]]></category>
		<category><![CDATA[jruby]]></category>
		<category><![CDATA[puppet]]></category>
		<category><![CDATA[ssl]]></category>

		<guid isPermaLink="false">http://www.masterzen.fr/?p=52</guid>
		<description><![CDATA[As announced in my last edit of my yesterday post Puppet and JRuby a love and hate story, I finally managed to run a webrick puppetmaster under JRuby with a MRI client connecting and fetching it&#8217;s config.
The Recipe
Puppet side
Unfortunately Puppet creates its first certificate with a serial number of 0, which JRuby-OpenSSL finds invalid (in [...]]]></description>
			<content:encoded><![CDATA[<p>As announced in my last edit of my yesterday post <a href="http://www.masterzen.fr/2009/05/23/puppet-and-jruby-a-love-and-hate-story/" >Puppet and JRuby a love and hate story</a>, I finally managed to run a <a href="http://www.webrick.org" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.webrick.org');">webrick</a> puppetmaster under <a href="http://www.jruby.org" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.jruby.org');">JRuby</a> with a MRI client connecting and fetching it&#8217;s config.</p>
<h2>The Recipe</h2>
<h3>Puppet side</h3>
<p>Unfortunately Puppet creates its first certificate with a <strong>serial number of 0</strong>, which <a href="http://github.com/jruby/jruby-openssl/tree/master" onclick="javascript:pageTracker._trackPageview('/outbound/article/github.com');">JRuby-OpenSSL</a> finds invalid (in fact that&#8217;s <a href="http://www.bouncycastle.org/java.html" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.bouncycastle.org');">Bouncy Castle JCE Provider</a>). So the first thing is to check if you already have some certificate generated with a serial of 0. If you have none, then everything is great you can skip this.</p>
<p>You can see a certificate content with openssl:</p>
<pre class="syntax-highlight:sh">

% openssl x509 -text -in /path/to/my/puppet/ssl/ca/ca_cert.pem

Certificate:
Data:
Version: 3 (0x2)
Serial Number: 1 (0x1)
Signature Algorithm: sha1WithRSAEncryption
Issuer: CN=ca
Validity
Not Before: May 23 18:38:19 2009 GMT
Not After : May 22 18:38:19 2014 GMT
Subject: CN=ca
...
</pre>
<p>If no certificate has a serial of 0, then it&#8217;s OK, otherwise I&#8217;m afraid you&#8217;ll have to start the PKI from scratch (which means rm -rf $vardir/ssl and authenticate clients again), after applying the following Puppet patch:</p>
<pre class="syntax-highlight:ruby">

JRuby fix: make sure certificate serial &gt; 0

JRuby OpenSSL implementation is more strict than real ruby one and
requires certificate serial number to be strictly positive.

Signed-off-by: Brice Figureau &lt;brice-puppet@daysofwonder.com&gt;

diff --git a/lib/puppet/ssl/certificate_authority.rb b/lib/puppet/ssl/certificate_authority.rb
index 08feff0..4a7d461 100644
--- a/lib/puppet/ssl/certificate_authority.rb
+++ b/lib/puppet/ssl/certificate_authority.rb
@@ -184,7 +184,7 @@ class Puppet::SSL::CertificateAuthority
# it, but with a mode we can&#039;t actually read in some cases.  So, use
# a default before the lock.
unless FileTest.exist?(Puppet[:serial])
-            serial = 0x0
+            serial = 0x1
end

Puppet.settings.readwritelock(:serial) { |f|
</pre>
<p>I&#8217;ll post this patch to <a href="http://groups.google.com/group/puppet-dev" onclick="javascript:pageTracker._trackPageview('/outbound/article/groups.google.com');">puppet-dev</a> soon, so I hope it&#8217;ll eventually get merged soon in mainline.</p>
<h3>JRuby</h3>
<p>You need the freshest JRuby available at this time. My test were conducted with latest JRuby as of commit &#8220;3aadd8a&#8221;. The best is to clone the <a href="http://github.com/jruby/jruby/tree/master" onclick="javascript:pageTracker._trackPageview('/outbound/article/github.com');">github jruby repository</a>, and build it (it requires of course a JDK and Ant, but that&#8217;s pretty much all).</p>
<p>Then install jruby in your path (if you need assistance for this, I&#8217;m not sure this blog post is for you <img src='http://www.masterzen.fr/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> )</p>
<h3>JRuby-OpenSSL</h3>
<p>As I explained in my previous blog post about the same subject, Puppet exercises a lot the Ruby OpenSSL subsystem. During this experiment, I found a few shortcomings in the current JRuby-OpenSSL 0.5, including missing methods, or missing behaviors needed by Puppet to run fine.</p>
<p>So to get a fully Puppet enabled JRuby-OpenSSL you need either to get the very latest <a href="http://github.com/jruby/jruby-openssl/tree/master" onclick="javascript:pageTracker._trackPageview('/outbound/article/github.com');">JRuby-OpenSSL from its own github repository </a>(or checkout the <a href="http://github.com/masterzen/jruby-openssl/tree/puppet-fixes" onclick="javascript:pageTracker._trackPageview('/outbound/article/github.com');">puppet-fixes branch of my fork of said repository on github</a>) and or apply manually the following patches on top of the 0.5 source tarballs:</p>
<ul>
<li><a href="http://jira.codehaus.org/browse/JRUBY-3689" onclick="javascript:pageTracker._trackPageview('/outbound/article/jira.codehaus.org');">JRUBY-3689</a>: OpenSSL::X509::CRL can&#8217;t be created with PEM content</li>
<li><a href="http://jira.codehaus.org/browse/JRUBY-3690" onclick="javascript:pageTracker._trackPageview('/outbound/article/jira.codehaus.org');">JRUBY-3690</a>: OpenSSL::X509::Request can&#8217;t be created from PEM content</li>
<li><a href="http://jira.codehaus.org/browse/JRUBY-3691" onclick="javascript:pageTracker._trackPageview('/outbound/article/jira.codehaus.org');">JRUBY-3691</a>: Implement OpenSSL::X509::Request#to_pem</li>
<li><a href="http://jira.codehaus.org/browse/JRUBY-3692" onclick="javascript:pageTracker._trackPageview('/outbound/article/jira.codehaus.org');">JRUBY-3692</a>: Implement OpenSSL::X509::Store#add_file</li>
<li><a href="http://jira.codehaus.org/browse/JRUBY-3693" onclick="javascript:pageTracker._trackPageview('/outbound/article/jira.codehaus.org');">JRUBY-3693</a>: OpenSSL::X509::Certificate#check_private_key is not implemented</li>
<li><a href="http://jira.codehaus.org/browse/JRUBY-3556" onclick="javascript:pageTracker._trackPageview('/outbound/article/jira.codehaus.org');">JRUBY-3556</a>: Webrick doesn&#8217;t start in https</li>
<li><a href="http://jira.codehaus.org/browse/JRUBY-3694" onclick="javascript:pageTracker._trackPageview('/outbound/article/jira.codehaus.org');">JRUBY-3694</a>: Webrick HTTPS produces some SSL stack trace</li>
</ul>
<p>Then rebuild JRuby-OpenSSL which is a straightforward process (copy build.properties.SAMPLE to build.properties, adjust jruby.jar path, and then issue ant jar to build the jopenssl.jar).</p>
<p>Once done, install the 0.5 JRuby-OpenSSL gem in your jruby install, and copy other the built jar in lib/ruby/gems/1.8/gems/jruby-openssl-0.5/lib.</p>
<h2>Let&#8217;s try it!</h2>
<p>Then it&#8217;s time to run your puppetmaster, just start it with <em>jruby</em> instead of ruby. Of course you need the puppet dependencies installed (Facter).</p>
<p>My next try will be to run Puppet on Jruby and mongrel (or what replaces it in JRuby world), then try with storeconfig on&#8230;</p>
<p>Hope that helps, and for any question, please post in the <a href="http://groups.google.com/group/puppet-dev" onclick="javascript:pageTracker._trackPageview('/outbound/article/groups.google.com');">puppet-dev</a> list.</p>
<img src="http://feeds.feedburner.com/~r/MasterzenPuppet/~4/q2Lp0VAac6c" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.masterzen.fr/2009/05/24/puppet-and-jruby-a-love-story/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		<feedburner:origLink>http://www.masterzen.fr/2009/05/24/puppet-and-jruby-a-love-story/</feedburner:origLink></item>
		<item>
		<title>Puppet and JRuby, a love and hate story</title>
		<link>http://feeds.masterzen.fr/~r/MasterzenPuppet/~3/FdAVlGb1HN8/</link>
		<comments>http://www.masterzen.fr/2009/05/23/puppet-and-jruby-a-love-and-hate-story/#comments</comments>
		<pubDate>Sat, 23 May 2009 17:15:38 +0000</pubDate>
		<dc:creator>masterzen</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Puppet]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[System Administration]]></category>
		<category><![CDATA[jruby]]></category>
		<category><![CDATA[openssl]]></category>
		<category><![CDATA[puppet]]></category>
		<category><![CDATA[puppetmaster]]></category>
		<category><![CDATA[ssl]]></category>

		<guid isPermaLink="false">http://www.masterzen.fr/?p=46</guid>
		<description><![CDATA[Since I heard about JRuby about a year ago, I wanted to try to run my favorite ruby program on it. I&#8217;m working with Java almost all day long, so I know for sure that the Sun JVM is a precious tool for running long-lived server. It is pretty fast, and has a very good [...]]]></description>
			<content:encoded><![CDATA[<p>Since I heard about <a href="http://www.jruby.org" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.jruby.org');">JRuby</a> about a year ago, I wanted to try to run <a href="http://www.reductivelabs.com/puppet" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.reductivelabs.com');">my favorite ruby program</a> on it. I&#8217;m working with Java almost all day long, so I know for sure that the Sun JVM is a precious tool for running long-lived server. It is <em>pretty fast</em>, and has a very good (and tunable) <a href="http://en.wikipedia.org/wiki/Garbage_collection_(computer_science)" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');">garbage collector</a>.</p>
<p>In a word: the <em>perfect system</em> to run a long-lived puppetmaster!</p>
<p>The first time I tried, back in February 2009, I unfortunately encountered the bug <a title="Fcntl constants not available" href="http://jira.codehaus.org/browse/JRUBY-3349" onclick="javascript:pageTracker._trackPageview('/outbound/article/jira.codehaus.org');">JRUBY-3349</a> which prevented Puppet to run quite early, because the Fcntl constants weren&#8217;t defined. Since my understanding of JRuby internal is near zero, I left there.</p>
<p>But thanks to <a href="http://madstop.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/madstop.com');">Luke Kanies</a> (Puppet creator), one of the JRuby main developers <a href="http://blog.headius.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/blog.headius.com');">Charles Oliver Nutter</a> fixed the issue a couple of weeks ago (thanks to him, and they even fixed another issue at about the same time about fcntl which didn&#8217;t support SET_FD).</p>
<p>That was just in time for another test&#8230;</p>
<p>But what I forgot was that Puppet is not every ruby app on the block. It uses lots of cryptography behind the scene. Remember that Puppet manages its own PKI, including:</p>
<ul>
<li>a full Certification Authority.</li>
<li>a CRL.</li>
<li>authenticated clients connections, through SSL.</li>
</ul>
<p>That just means Puppet exercise a lot the Ruby OpenSSL extension.</p>
<p>The main issue is that <a href="http://www.ruby-lang.org/en/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.ruby-lang.org');">MRI</a> uses <a href="http://www.openssl.org/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.openssl.org');">OpenSSL</a> for all the cryptographic stuff, and JRuby uses <a href="http://github.com/jruby/jruby-openssl/tree/master" onclick="javascript:pageTracker._trackPageview('/outbound/article/github.com');">a specific Java version of this extension</a>. Of course this later is still young (presently at v 0.5) and doesn&#8217;t contain yet everything needed to be able to run Puppet.</p>
<p>In another life I wrote a proprietary cryptographic Java library, so I&#8217;m not a complete cryptography newcomer (OK, I forgot almost everything, but I still have some good books to refer to). So I decided to implement what is missing in JRuby-openssl to allow a webrick Puppetmaster to run.</p>
<p>You can find my contributions in the various <a href="http://jira.codehaus.org/browse/JRUBY-3689" onclick="javascript:pageTracker._trackPageview('/outbound/article/jira.codehaus.org');">JRUBY-3689</a>, <a href="http://jira.codehaus.org/browse/JRUBY-3690" onclick="javascript:pageTracker._trackPageview('/outbound/article/jira.codehaus.org');">JRUBY-3690</a>, <a href="http://jira.codehaus.org/browse/JRUBY-3691" onclick="javascript:pageTracker._trackPageview('/outbound/article/jira.codehaus.org');">JRUBY-3691</a>, <a href="http://jira.codehaus.org/browse/JRUBY-3692" onclick="javascript:pageTracker._trackPageview('/outbound/article/jira.codehaus.org');">JRUBY-3692</a>, <a href="http://jira.codehaus.org/browse/JRUBY-3693" onclick="javascript:pageTracker._trackPageview('/outbound/article/jira.codehaus.org');">JRUBY-3693</a> bugs.</p>
<p>I still have another a minor patch to submit (OpenSSL::X509::Certificate#to_text implementation).</p>
<p>So the question is: with <em>all that patches</em> applied, did I get a puppetmaster running?</p>
<p>And the answer is <strong>unfortunately no</strong>.</p>
<p>I can get the puppetmaster to start on a fresh configuration (ie it creates everything SSL related and such), but it fails as soon a client connects (hey that&#8217;s way better than before I started <img src='http://www.masterzen.fr/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> ).</p>
<p>All comes <em>from SSL</em>. The issue is that  with the C OpenSSL implementation it is possible to get the peer certificate anytime, but the java SSL implementation (which is provided by the Sun virtual machine) requires the client to be authenticated before anyone get access to the peer certificate.</p>
<p>That&#8217;s unfortunate because to be able to authenticate a not-yet-registered client, we must have access to its certificate. I couldn&#8217;t find any easy code fix, so I stopped my investigations there.</p>
<p>There is still some possible workarounds, like running in mongrel mode (provided JRuby supports mongrel which I didn&#8217;t check) and let <a href="http://nginx.net/" onclick="javascript:pageTracker._trackPageview('/outbound/article/nginx.net');">Nginx</a> (or Apache) handle the SSL stuff, but still it would be great to be able to run a full-fledged puppetmaster on JRuby.</p>
<p>I tried with a known client and get the same issue, so maybe that&#8217;s a whole different issue, I guess I&#8217;ll have to dig deeper in the Java SSL code, which unfortunately is not available <img src='http://www.masterzen.fr/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>Stay tuned for more info about this. I hope to be able to have a full puppetmaster running on JRuby soon!</p>
<p>EDIT: I could run a full puppetmaster on webrick from scratch under JRuby with a normal ruby client. I&#8217;ll post the recipe in a subsequent article soon.</p>
<img src="http://feeds.feedburner.com/~r/MasterzenPuppet/~4/FdAVlGb1HN8" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.masterzen.fr/2009/05/23/puppet-and-jruby-a-love-and-hate-story/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		<feedburner:origLink>http://www.masterzen.fr/2009/05/23/puppet-and-jruby-a-love-and-hate-story/</feedburner:origLink></item>
		<item>
		<title>OMG!! storedconfigs killed my database!</title>
		<link>http://feeds.masterzen.fr/~r/MasterzenPuppet/~3/RzXeTmS_2rg/</link>
		<comments>http://www.masterzen.fr/2009/03/18/omg-storedconfigs-killed-my-database/#comments</comments>
		<pubDate>Wed, 18 Mar 2009 22:42:21 +0000</pubDate>
		<dc:creator>masterzen</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Puppet]]></category>
		<category><![CDATA[System Administration]]></category>
		<category><![CDATA[InnoDB]]></category>
		<category><![CDATA[puppet]]></category>
		<category><![CDATA[storeconfigs]]></category>
		<category><![CDATA[storedconfigs]]></category>
		<category><![CDATA[tuning]]></category>

		<guid isPermaLink="false">http://www.masterzen.fr/?p=22</guid>
		<description><![CDATA[When I wrote my previous post titled all about storedconfigs, I was pretty confident I explained everything I could about storedconfigs&#8230; I was wrong of course  
A couple of days ago, I was helping some USG admins who were facing an interesting issue. Interesting for me, but I don&#8217;t think they&#8217;d share my views [...]]]></description>
			<content:encoded><![CDATA[<p>When I wrote <a href="http://www.masterzen.fr/2009/03/08/all-about-puppet-storeconfigs/" >my previous post titled all about storedconfigs</a>, I was pretty confident I explained everything I could about <a href="http://reductivelabs.com/trac/puppet/wiki/UsingStoredConfiguration" onclick="javascript:pageTracker._trackPageview('/outbound/article/reductivelabs.com');">storedconfigs</a>&#8230; I was <em>wrong</em> of course <img src='http://www.masterzen.fr/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>A couple of days ago, I was helping some <a href="http://www.usg.edu/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.usg.edu');">USG admins</a> who were facing an interesting issue. Interesting for me, but I don&#8217;t think they&#8217;d share my views on this, as their servers were melting down under the database load.</p>
<p>But first let me explain the issue.</p>
<h2>The issue</h2>
<p>The thing is that when a client checks in to get its configuration, the <em>puppetmaster</em> compiles its configuration to a digestible format and returns it. This operation is the process of transforming the AST built by parsing the manifests to what is called the <em>catalog</em> in Puppet. This is this catalog (which in fact is a graph of resources) which is later played by the client.</p>
<p>When the compilation process is over, and <em>if storedconfigs</em> is enabled on the master, the master connects to the RDBMS, and retrieves all the resources, parameters, tags and facts. Those, if any, are compared to what has just been compiled, and if some resources differs (by value/content, or if there are some missing or new ones), they get written to the database.</p>
<p>Pretty straightforward, isn&#8217;t it?</p>
<p>As you can see, this process is synchronous and while the master processes the <em>storedconfigs</em> operations, it doesn&#8217;t serve anybody else.</p>
<p>Now, imagine you have a large site (ie hundreds of puppetd clients), and you decide to turn on storedconfigs. All the clients checking in will see their current configuration stored in the database.</p>
<p>Unfortunately the first run of <em>storedconfigs</em> for a client, the database is empty, so the puppetmaster has to send all the information to the RDBMS which in turns as to write it to the disks. Of course on subsequent runs only what is modified needs to reach the RDBMS which is much less than the first time (provided you are running <a href="http://projects.reductivelabs.com/versions/show/27" onclick="javascript:pageTracker._trackPageview('/outbound/article/projects.reductivelabs.com');">0.24.8 </a>or applied my <a href="http://projects.reductivelabs.com/issues/1930" onclick="javascript:pageTracker._trackPageview('/outbound/article/projects.reductivelabs.com');">patch</a>).</p>
<p>But if your RDBMS is not correctly setup or not sized for so much <em>concurrent write load</em>, the <em>storedconfigs</em> process will take time. During this time this master is pinned to the database and can&#8217;t serve clients. So the immediate effect is that new clients checking in will see timeouts, load will rise, and so on.</p>
<h2>The database</h2>
<p>If you are in the aforementioned scenario you must be sure your RDBMS hardware is properly sized for this peak load, and that your database is properly tuned.</p>
<p>I&#8217;ll soon give some generic MySQL tuning advices to let MySQL handle the load, but remember those are generic so YMMV.</p>
<h3>Size the I/O subsystem</h3>
<p>What people usually forget is that disk (ie those with rotating plates, not SSDs) have a maximum number of I/O operations per seconds. This value is for professional high-end disks about 250 IOP/s.</p>
<p>Now, to simplify, let&#8217;s say your average puppet client has 500 resources with an average of 4 parameters each. That means the master will have to perform at least 500 * 4 + 500 = 2500 writes to the database (that&#8217;s naive since there are indices to modify, and transactions can be grouped, etc.. but you see the point).</p>
<p>Add to this the tags, hmm let&#8217;s say an average of 4 tags per resources, and we have 500 * 4 + 500 + 500 * 4 = 4500 writes to perform to store the configuration of a given host.</p>
<p>Now remember our 250 IOP/s, how many seconds does the disk need to performs 4500 writes?</p>
<p>The answer is <strong>18s</strong>!! Which is a high value. During this time you can&#8217;t do anything else. Now add concurrency to the mix, and imagine what that means.</p>
<p>Of course this supposes we have to wait for the disk to have finished (ie synchronous writing), but  in fact that&#8217;s pretty how RDBMS are working if you really want to trust your data.</p>
<p>So the result is that if you want a fast RDBMS you must be ready to pay for an expensive I/O subsystem.</p>
<h3>Size the I/O subsystem</h3>
<p>That&#8217;s certainly the most important part of your server.</p>
<p>You need:</p>
<ul>
<li><strong>fast disks</strong> (15k RPM, because they is a real latency benefit compared to 10k )</li>
<li>the <strong>more spindle possible</strong> grouped in a sane RAID array like <strong>RAID10</strong>. Please forget RAID5 if you want your data to be safe (and fast writes). I saw too much horror stories with RAID5. I should really join the <a href="http://www.baarf.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.baarf.com');">BAARF</a>.</li>
<li>a <strong>Battery Backed RAID Cache unit</strong> (that will absorb the fsyncs gracefully).</li>
<li>Tune the RAID for the largest stripe size. Remove the RAID read cache if possible (innodb will take care of the READ cache with the innodb buffer pool).</li>
</ul>
<p>If you don&#8217;t have this, <em>do not even think turning on storedconfigs</em> for a large site.</p>
<h3>Size the RDBMS server</h3>
<p>Of course other things matters. If the database can fit in RAM (the best if you don&#8217;t want to be I/O bound),  then you obviously need RAM. Preferably <a href="http://en.wikipedia.org/wiki/Dynamic_random_access_memory#Errors_and_error_correction" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');">ECC</a> <a href="http://en.wikipedia.org/wiki/Registered_memory" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');">Registered</a> RAM. Use 64 bits hardware with a 64 bits OS.</p>
<p>Then you need some CPU. Nowadays they&#8217;re cheap, but beware of InnoDB scaling issues on multi-core/multi-CPU systems (see below).</p>
<h3>Tune the database configuration</h3>
<p>Here is a checklist on how to tune MySQL for a mostly write load:</p>
<h4>InnoDB of course</h4>
<p>For concurrency, stability and durability reasons <a href="http://en.wikipedia.org/wiki/InnoDB" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');">InnoDB</a> is mandatory. MyISAM is at best usable for READ workload but suffers concurrency issues so it is a no-no for our topic</p>
<h4>Tuned InnoDB</h4>
<p>The default InnoDB settings are tailored to very small 10 years old servers&#8230;</p>
<p>Things to look to:</p>
<ul>
<li><strong>innodb_buffer_pool_size</strong>. Usual advice says 70% to 80% of physical RAM of the server if MySQL is the only running application. I&#8217;d say that it depends on the size of the database. If you know you&#8217;ll store only a few MiB, no need to allocate 2 GiB <img src='http://www.masterzen.fr/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> . More information with this<a href="http://www.mysqlperformanceblog.com/2006/09/29/what-to-tune-in-mysql-server-after-installation/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.mysqlperformanceblog.com');"> useful and intersting blog post from Percona guys</a>.</li>
<li><strong>innodb_log_file_size</strong>. We want those to be the largest we can to ease the mostly write log we have. Once all the clients will be stored in the database we&#8217;ll reduce this to a something lower. The trade-off with large logs is the recovery time in case of crash. It isn&#8217;t uncommon to see several hundreds of MiB, or even GiB.</li>
<li><strong>innodb_flush_method = O_DIRECT</strong> on Linux. This is to prevent the OS to cache the innodb_buffer_pool content (thus ending with a double cache).<strong></strong></li>
<li><strong>innodb_flush_log_at_trx_commit=2</strong>. If your MySQL server doesn&#8217;t have any other use than for storedconfigs or you don&#8217;t care about the D in ACID. Otherwise use 0. It is also possible to temporarily change it to 2, and then move back to 0 when all clients have their configs stored.</li>
<li><strong>transaction-isolation=READ-COMMITTED.</strong> This one can help also, although I never tested it myself</li>
</ul>
<h4>Patch MySQL</h4>
<p>The fine people at Percona or Ourdelta produces some patched builds of MySQL that removes some of the MySQL InnoDB scalability issues. This is more important on high concurrency workload on multi-core/multi-cpu systems.</p>
<p>It can also be good to run MySQL with <a href="http://goog-perftools.sourceforge.net/doc/tcmalloc.html" onclick="javascript:pageTracker._trackPageview('/outbound/article/goog-perftools.sourceforge.net');">Google&#8217;s perftools TCMalloc</a>. TCMalloc is a memory allocator which scales way better than the Glibc one.</p>
<h2>On the Puppet side</h2>
<p>The immediate and most straightforward idea is to<strong> l</strong><strong>imit the number of clients</strong> that can check in at the same time. This can be done by disabling puppetd on each client (<em>puppetd &#8211;disable</em>), blocking network access, or any other creative mean&#8230;</p>
<p>When all the active hosts have checked in, you can then enable the other ones. This can be done hundreds of hosts at a time, until all hosts have a configuration stored.</p>
<p>Another solution is to direct some hosts to a special <em>puppetmaster</em> with <em>storedconfigs</em> on (the regular one still has storedconfigs disabled), by playing with DNS or by configuration, whatever is simplest in your environment. Once those hosts have their config stored, move them back to their regular puppetmaster and move newer hosts there.</p>
<p>Since that&#8217;s completely manual, it might be unpractical for you, but that&#8217;s the simplest method.</p>
<h2>And after that?</h2>
<p>As long as your manifests are only slightly changing, subsequent runs will see only a really limited database activity (if you run a puppetmaster &gt;= 0.24.8). That means the tuning we did earlier can be undone (for instance you can lower the innodb_log_file_size for instance, and adjust the innodb_buffer_pool_size to the size of the hot set).</p>
<p>But still storedconfigs can double your compilation time. If you are already at the limit compared to the number of hosts, you might see some client timeouts.</p>
<h2>The Future</h2>
<p>Today <a href="http://madstop.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/madstop.com');">Luke</a> announced on the <a href="http://groups.google.com/group/puppet-dev" onclick="javascript:pageTracker._trackPageview('/outbound/article/groups.google.com');">puppet-dev list</a> that they were working on a <em>queuing system</em> to defer storedconfigs and smooth out the load by spreading it on a longer time. But still, tuning the database is important.</p>
<p>The idea is to offload the <em>storedconfigs</em> to another daemon which is hooked behind a queuing system. After the compilation the <em>puppetmaster</em> queues the catalog, where it will be unqueued by the puppet queue daemon which will in turn execute the <em>storedconfigs</em> process.</p>
<p>I don&#8217;t know the ETA for this interesting feature, but meanwhile I hope the tips I provided here can be of any help to anyone <img src='http://www.masterzen.fr/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>Stay tuned for more puppet stories!</p>
<img src="http://feeds.feedburner.com/~r/MasterzenPuppet/~4/RzXeTmS_2rg" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.masterzen.fr/2009/03/18/omg-storedconfigs-killed-my-database/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		<feedburner:origLink>http://www.masterzen.fr/2009/03/18/omg-storedconfigs-killed-my-database/</feedburner:origLink></item>
	</channel>
</rss><!-- Dynamic Page Served (once) in 0.776 seconds --><!-- Cached page served by WP-Cache -->
