<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	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/"
	>

<channel>
	<title>thinking sysadmin</title>
	<atom:link href="http://andyleonard.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://andyleonard.com</link>
	<description>qstat -u aleonard -s z</description>
	<lastBuildDate>Sun, 24 Feb 2013 14:40:27 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
		<item>
		<title>Presentation: When ACLs Attack &#8211; Cross-Platform File Permissions</title>
		<link>http://andyleonard.com/2012/07/13/presentation-when-acls-attack-cross-platform-file-permissions/</link>
		<comments>http://andyleonard.com/2012/07/13/presentation-when-acls-attack-cross-platform-file-permissions/#comments</comments>
		<pubDate>Fri, 13 Jul 2012 19:45:10 +0000</pubDate>
		<dc:creator>Andy</dc:creator>
				<category><![CDATA[presentations]]></category>

		<guid isPermaLink="false">http://andyleonard.com/?p=769</guid>
		<description><![CDATA[(Note that this presentation has my employer&#8217;s logo on it, but this site certainly does not speak for them.) When ACLs Attack View more presentations from andy_leonard.]]></description>
				<content:encoded><![CDATA[<p>(Note that this presentation has my employer&#8217;s logo on it, but this site certainly does not speak for them.)</p>
<div style="width:425px" id="__ss_13633722"><strong style="display:block;margin:12px 0 4px"><a href="http://www.slideshare.net/andy_leonard/when-acls-attack" title="When ACLs Attack">When ACLs Attack</a></strong><object id="__sse13633722" width="425" height="355"><param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=whenaclsattack-120713142210-phpapp01&#038;stripped_title=when-acls-attack&#038;userName=andy_leonard" /><param name="allowFullScreen" value="true"/><param name="allowScriptAccess" value="always"/><param name="wmode" value="transparent"/><embed name="__sse13633722" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=whenaclsattack-120713142210-phpapp01&#038;stripped_title=when-acls-attack&#038;userName=andy_leonard" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" wmode="transparent" width="425" height="355"></embed></object>
<div style="padding:5px 0 12px">View more <a href="http://www.slideshare.net/">presentations</a> from <a href="http://www.slideshare.net/andy_leonard">andy_leonard</a>.</div>
</div>
]]></content:encoded>
			<wfw:commentRss>http://andyleonard.com/2012/07/13/presentation-when-acls-attack-cross-platform-file-permissions/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Filling in the Missing Parts of NetApp&#8217;s API</title>
		<link>http://andyleonard.com/2012/02/27/filling-in-the-missing-parts-of-netapps-api/</link>
		<comments>http://andyleonard.com/2012/02/27/filling-in-the-missing-parts-of-netapps-api/#comments</comments>
		<pubDate>Tue, 28 Feb 2012 04:47:09 +0000</pubDate>
		<dc:creator>Andy</dc:creator>
				<category><![CDATA[storage]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[ontap]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[sdk]]></category>

		<guid isPermaLink="false">http://andyleonard.com/?p=758</guid>
		<description><![CDATA[Late last year, NetApp released long-overdue Python and Ruby support in their SDK, officially known as the NetApp Manageability SDK. The SDK download is &#8211; oddly and unfortunately &#8211; still buried behind a paywall, and you have to submit a web form about how you plan to use it to get access to the download; [...]]]></description>
				<content:encoded><![CDATA[<p>Late last year, NetApp released long-overdue Python and Ruby support in their SDK, officially known as the <a href="https://now.netapp.com/NOW/cgi-bin/software/?product=NetApp+Manageability+SDK&#038;platform=All+Platforms">NetApp Manageability SDK</a>.  The SDK download is &#8211; oddly and unfortunately &#8211; still buried behind a paywall, and you have to submit a web form about how you plan to use it to get access to the download; otherwise it&#8217;s available to all.</p>
<p>But perhaps there&#8217;s good reason for hiding the download away: There are still large gaps in the API.  For instance, say you want to change the security mode of a qtree?  You&#8217;re out of luck.  (Makes one wonder how NetApp implements this functionality in OnCommand System Manager &#8211; are they eating their own dogfood?)</p>
<p>That said, if you&#8217;re willing to venture off the beaten (and supported) path, you can use the undocumented <a href="https://communities.netapp.com/message/74370#74400">system-cli API call</a>.  Here&#8217;s how I&#8217;m using it in a <a href="https://github.com/seattle-biomed/ontap-api-wrapper">Python wrapper</a> I&#8217;m working on that makes the SDK feel a little bit less like handling thinly-varnished XML:<br />
<span id="more-758"></span></p>
<pre class="brush: python; title: ; notranslate">
    def invoke_cli(self, *cli_args):
        &quot;&quot;&quot;
        Call the unsupported/undocumented system-cli API.

        cli_args, joined with spaces, would represent the command line 
        if executing in the CLI.

        Return the NaElement result of executing the command.
        &quot;&quot;&quot;

        args = NaElement('args')
        for arg in cli_args:
            args.child_add(NaElement('arg', arg))

        cli = NaElement('system-cli')
        cli.child_add(args)
        out = self.api.invoke_elem(cli)
        if out.results_status() == 'failed':
            raise OntapApiException(out.results_errno(), out.results_reason())
        return out
</pre>
<p>On lines 11-13, I&#8217;m building up an NaElement (itself imported from the eponymous NaElement module) representation of the command line arguments; this is then added as a child to a &#8220;system-cli&#8221; NaElement.  On line 17, I call &#8220;NaServer.invoke_elem()&#8221; on the &#8220;system-cli&#8221; NaElement &#8211; and from here on out, it&#8217;s just like using the ordinary (documented) API.  The &#8220;cli-output&#8221; element in &#8220;out&#8221; contains the output for you to parse, and &#8220;cli-result-value&#8221; is a return code.</p>
<p>Here&#8217;s hoping newer versions of the API will do away with the need for this workaround.</p>
]]></content:encoded>
			<wfw:commentRss>http://andyleonard.com/2012/02/27/filling-in-the-missing-parts-of-netapps-api/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Git pre-commit hook for DNS zone data</title>
		<link>http://andyleonard.com/2012/01/21/git-pre-commit-hook-for-dns-zone-data/</link>
		<comments>http://andyleonard.com/2012/01/21/git-pre-commit-hook-for-dns-zone-data/#comments</comments>
		<pubDate>Sun, 22 Jan 2012 03:46:31 +0000</pubDate>
		<dc:creator>Andy</dc:creator>
				<category><![CDATA[dns]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[hook]]></category>
		<category><![CDATA[named-checkzone]]></category>
		<category><![CDATA[pre-commit]]></category>

		<guid isPermaLink="false">http://andyleonard.com/?p=752</guid>
		<description><![CDATA[If you&#8217;re storing your DNS configuration in Git, a pre-commit hook to automatically run named-checkzone before zone file changes are committed may be useful to you. The pre-commit hook I use assumes that zone files (and only zone files) are in the format db.&#60;zonename&#62; (e.g. &#8220;db.andyleonard.com&#8221;), and only tests zone files (e.g. named-checkconf is not [...]]]></description>
				<content:encoded><![CDATA[<p>If you&#8217;re storing your <a href="/2011/12/28/git-driven-bind-plus-fabric/">DNS configuration in Git</a>, a pre-commit hook to automatically run named-checkzone before zone file changes are committed may be useful to you.  The pre-commit hook I use assumes that zone files (and <em>only</em> zone files) are in the format db.&lt;zonename&gt; (e.g. &#8220;db.andyleonard.com&#8221;), and only tests zone files (e.g. named-checkconf is not run against configuration files).</p>
<p>This pre-commit hook&#8217;s structure is based heavily on a <a href="http://blog.snijders-it.nl/2011/12/example-puppet-27-git-pre-commit-script.html">Puppet 2.7 pre-commit published elsewhere</a>.  <span id="more-752"></span>Without further ado:</p>
<pre class="brush: plain; title: ; notranslate">
#!/bin/bash

# Adapted from a puppet pre-commit hook at:
# http://blog.snijders-it.nl/2011/12/example-puppet-27-git-pre-commit-script.html
#
# install this as .git/hooks/pre-commit to check DNS zone files
# for errors before committing changes.

rc=0

[ &quot;$SKIP_PRECOMMIT_HOOK&quot; = 1 ] &amp;&amp; exit 0

# Make sure we're at top level of repository.
cd $(git rev-parse --show-toplevel)

trap 'rm -rf $tmpdir $tmpfile1' EXIT INT HUP
tmpdir=$(mktemp -d precommitXXXXXX)
tmpfile1=$(mktemp errXXXXXX)

echo &quot;$(basename $0): Validating changes.&quot;

# Here we copy files out of the index into a temporary directory. This
# protects us from a the situation in which we have staged an invalid
# zone file using ``git add`` but corrected the changes in the
# working directory. If we checked the files &quot;in place&quot;, we would
# fail to detect the errors.

git diff-index --cached --name-only HEAD |
grep '^db\.' |
git checkout-index --stdin --prefix=$tmpdir/

find $tmpdir -type f -name 'db.*' |
while read zonefile; do
    zone=`echo $zonefile | sed -e &quot;s/^${tmpdir}\/db\.\(.*\)$/\1/&quot;`
    named-checkzone -q $zone $zonefile
    # If named-checkzone reports an error, get some output:
    if [ $? -ne 0 ]; then
	named-checkzone $zone $zonefile | sed &quot;s#$tmpdir/##&quot; &gt;&gt; $tmpfile1 2&gt;&amp;1
    fi
done

if [ -s &quot;$tmpfile1&quot; ]; then
echo
echo Error: Zone file problem:
echo ----------------------------
cat $tmpfile1
echo ----------------------------
echo

rc=1
fi

exit $rc
</pre>
<p><strong>Update:</strong> Now available as a <a href="https://gist.github.com/anl/5024043#file-pre-commit">Gist</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://andyleonard.com/2012/01/21/git-pre-commit-hook-for-dns-zone-data/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Git-driven BIND (plus Fabric)</title>
		<link>http://andyleonard.com/2011/12/28/git-driven-bind-plus-fabric/</link>
		<comments>http://andyleonard.com/2011/12/28/git-driven-bind-plus-fabric/#comments</comments>
		<pubDate>Thu, 29 Dec 2011 03:46:23 +0000</pubDate>
		<dc:creator>Andy</dc:creator>
				<category><![CDATA[dns]]></category>
		<category><![CDATA[bind]]></category>
		<category><![CDATA[fabric]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[hook]]></category>
		<category><![CDATA[post-receive]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://andyleonard.com/?p=718</guid>
		<description><![CDATA[Step 0. Store your DNS configuration in Git. If you aren&#8217;t using some sort of version control system for your zone files and other BIND configuration, you ought to be. May I recommend Git? Put your entire configuration directory in there, but do read the &#8220;Downsides&#8221; section below for some important security considerations. Step 1. [...]]]></description>
				<content:encoded><![CDATA[<p>Step 0. <strong>Store your DNS configuration in Git.</strong>  If you aren&#8217;t using some sort of version control system for your zone files and other BIND configuration, you ought to be.  May I recommend <a href="http://git-scm.com/">Git</a>?  Put your entire configuration directory in there, but do read the &#8220;Downsides&#8221; section below for some important security considerations.</p>
<p>Step 1. <strong>Create a bare Git repository on your DNS server.</strong>  Using <a href="http://fabfile.org/">Fabric</a>, you&#8217;d do it something like this:</p>
<pre class="brush: python; light: true; title: ; notranslate">
def config_git():

    # Create bare git repo for direct DNS data pushes:
    sudo('/bin/mkdir /srv/bind.git')
    sudo('/bin/chown ubuntu:ubuntu /srv/bind.git')
    with cd('/srv/bind.git'):
        run('/usr/bin/git init --bare .')
    git_post_receive()
</pre>
<p>(The above assumes an Ubuntu system, where the &#8220;ubuntu&#8221; user has sudo privileges, such as on EC2; adjust to your environment as needed.)<br />
<span id="more-718"></span><br />
Step 2. <strong>Add a post-receive hook.</strong>  Notice that &#8220;git_post_receive()&#8221; in the fabfile snippet above?  That function is nothing more than something like this:</p>
<pre class="brush: python; light: true; title: ; notranslate">
def git_post_receive():
    put('git/post-receive', '/srv/bind.git/hooks/post-receive', mode=0755)
</pre>
<p>&#8220;git/post-receive&#8221; &#8211; which is, not surprisingly, a <a href="http://progit.org/book/ch7-3.html">post-receive Git hook</a> &#8211; is in turn something like this:</p>
<pre class="brush: plain; light: true; title: ; notranslate">
#!/bin/sh
sudo GIT_WORK_TREE=/etc/bind /usr/bin/git checkout -f
sudo chmod 0440 /etc/bind/rndc.key
</pre>
<p>(Again, assuming an Ubuntu environment, where BIND lives in /etc/bind.)</p>
<p>Step 3. <strong>Add some orchestration.</strong>  You could tack on an automatic DNS reload to your post-receive hook, but you may prefer to separate control of BIND into distinct functions in your fabfile, perhaps with some tests run before applying changes:</p>
<pre class="brush: python; light: true; title: ; notranslate">
def test():
    test_zones()
    test_conf()

def test_conf():
    run('/usr/sbin/named-checkconf')

def test_zones():
    with cd('/etc/bind'):
        run('for db in db.*; do zone=`/bin/grep SOA $db | /usr/bin/awk \'{ print $1 }\'`; echo ${zone}: $db; /usr/sbin/named-checkzone $zone $db; done')

def reload_bind():
    sudo('/usr/sbin/service bind9 reload')

def restart_bind():
    sudo('/usr/sbin/service bind9 restart')
</pre>
<p><strong>Downsides.</strong>  Note that you&#8217;re putting your rndc.key file (used to secure rndc) into Git if you put <em>all</em> your config files into your repository.  In that case, you&#8217;ll obviously want to restrict access to the repository.</p>
<p>Of course, it isn&#8217;t hard to imagine an adaptation of this system where the rndc.key file is not stored in Git, but is perhaps put in place by the post-receive hook.  (Call this an &#8220;exercise for the reader.&#8221;)</p>
<p><strong>Alternatives.</strong> If you are using DNSSEC, a tool like <a href="http://www-uxsup.csx.cam.ac.uk/~fanf2/hermes/conf/bind/bin/nsdiff">nsdiff</a> might be a better fit to manage updates to your zones.</p>
]]></content:encoded>
			<wfw:commentRss>http://andyleonard.com/2011/12/28/git-driven-bind-plus-fabric/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>What&#8217;s Wrong With OpenDNS</title>
		<link>http://andyleonard.com/2011/12/20/whats-wrong-with-opendns/</link>
		<comments>http://andyleonard.com/2011/12/20/whats-wrong-with-opendns/#comments</comments>
		<pubDate>Tue, 20 Dec 2011 13:54:55 +0000</pubDate>
		<dc:creator>Andy</dc:creator>
				<category><![CDATA[dns]]></category>
		<category><![CDATA[content filtering]]></category>
		<category><![CDATA[opendns]]></category>

		<guid isPermaLink="false">http://andyleonard.com/?p=703</guid>
		<description><![CDATA[First off, before I get to anything that&#8217;s wrong, there&#8217;s a lot that&#8217;s right about OpenDNS: It&#8217;s a simple, effective and flexible tool for content filtering. As a company, they&#8217;re trying to improve the state of DNS for end users with tools like DNSCrypt. You can&#8217;t beat their entry-level price &#8211; free. Their anycast network [...]]]></description>
				<content:encoded><![CDATA[<p>First off, before I get to anything that&#8217;s wrong, there&#8217;s a lot that&#8217;s right about OpenDNS: It&#8217;s a simple, effective and flexible tool for content filtering.  As a company, they&#8217;re trying to improve the state of DNS for end users with tools like <a href="http://www.opendns.com/technology/dnscrypt/">DNSCrypt</a>.  You can&#8217;t beat their entry-level price &#8211; free.  Their <a href="http://www.opendns.com/technology/network-map/">anycast network</a> is good, especially if you&#8217;re on the west coast of the United States, like I am (in fact, it&#8217;s better for me than surely-much-larger Google&#8217;s 8.8.8.8 and 8.8.4.4).  Their dashboard is pretty neat, too.</p>
<p>Second, let&#8217;s get the most common complaint about OpenDNS &#8211; one that isn&#8217;t going to be discussed here any further &#8211; out of the way: Their practice of returning ads on blocked or non-existent sites in your browser, via a bogus A RR of 67.215.65.132 (if you don&#8217;t go with one of their paid options).  OpenDNS is upfront about doing this, so you can decide if the trade-off is worthwhile before you sign up &#8211; and you can quit using them any time you want.</p>
<p>Those two preliminaries covered, here&#8217;s a case study of what I think is a serious problem with OpenDNS, plus some thoughts on how they could fix it.<br />
<span id="more-703"></span><br />
Background: Recently, I helped my wife buy a domain for a personal blog; it turns out OpenDNS has tagged this domain as pornographic.  Currently, the domain hosts nothing beyond an empty WordPress blog.  </p>
<p>I can think of three possible scenarios that could be in effect here:</p>
<ol>
<li><strong>The site has been compromised.</strong>  This was my first thought, although I now consider it unlikely.  I&#8217;ve gone through the site and the logs pretty closely, and have been unable to find anything amiss.  I&#8217;m willing to leave the possibility open that I missed something, though.</li>
<li><strong>The domain used to be pornographic, but is no longer.</strong>  This also appears unlikely, at least based on archive.org and other sites; the domain name itself also doesn&#8217;t exactly suggest a porn site, either, unless there&#8217;s some obscure slang I&#8217;m unaware of.</li>
<li><strong>The OpenDNS classification is incorrect.</strong>  The domain was accidentally or intentionally mislabeled.  It appears most classification &#8211; especially for small sites like the one in question &#8211; is crowd-sourced, but only a few members of the &#8220;crowd&#8221; might tag a small site.</li>
</ol>
<p>It&#8217;s unclear which of the above went wrong, but there are several obvious ideas that OpenDNS could implement to address this class of problems:</p>
<ol>
<li><strong>More metadata, please.</strong>  For my &#8220;pornographic&#8221; domain, it would have been nice to know when it was tagged as such, what the DNS servers and relevant records were when it was tagged, what a screenshot looked like then (not now), and what the registrar data was.  If it was a site compromise, this data would make my finding it that much easier.  If the classification is stale, metadata would make this obvious as well.</li>
<li><strong>Automatically-triggered classification reviews.</strong>  If there are substantial changes to a domain&#8217;s registrar entry, that should trigger an automatic review of the site &#8211; especially so if the domain registration lapses, and another party purchases the domain after a period of time.  The volume of users whitelisting a site in the control panel should be another signal of misclassification, although this probably won&#8217;t help obscure sites.  This is an obvious, simple fix, and perhaps OpenDNS has something like this in place, but I don&#8217;t see any signs of it.</li>
<li><strong>A better user interface.</strong>  When I first visited their tagging interface, the thumbnail viewer was broken, and the &#8220;flag for review&#8221; link was missing.  Both later reappeared, apparently functional, although I have no idea if or when a review will actually take place.</li>
<li><strong>Better policing of community members.</strong>  Some of the OpenDNS Community&#8217;s most prolific members tag in excess of 1,000 domains on an average day.  Many of these tags appear to be of dubious quality, going far beyond anything that could be called a difference of opinion into the realm of flat-out wrong.  Poor quality &#8211; or malicious &#8211; tagging is damaging OpenDNS&#8217;s product; I&#8217;m surprised they don&#8217;t appear more strongly motivated to address this.</li>
</ol>
<p>For now, I&#8217;m going to continue to use OpenDNS&#8217;s services at home, although I&#8217;m on the lookout for a better product elsewhere.  But until I see some signs that they&#8217;re addressing the issue of incorrect tags, I can no longer recommend them for professional use, which is too bad; as I said in the first paragraph, there&#8217;s a lot to like about OpenDNS.</p>
<p><strong>Update, 1/2/2012:</strong> It&#8217;s been a couple weeks since I flagged the domain for OpenDNS&#8217;s review &#8211; and nothing has happened.  I think it&#8217;s fair to update my conclusion: OpenDNS has a garbage-in, garbage-out problem and does not appear to be invested in the quality of their product; look elsewhere for a content-filtering tool that goes beyond hobbyist quality.</p>
]]></content:encoded>
			<wfw:commentRss>http://andyleonard.com/2011/12/20/whats-wrong-with-opendns/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>What t1.micro CPU Bursting Looks Like</title>
		<link>http://andyleonard.com/2011/12/09/what-t1-micro-cpu-bursting-looks-like/</link>
		<comments>http://andyleonard.com/2011/12/09/what-t1-micro-cpu-bursting-looks-like/#comments</comments>
		<pubDate>Sat, 10 Dec 2011 06:26:13 +0000</pubDate>
		<dc:creator>Andy</dc:creator>
				<category><![CDATA[utility computing]]></category>
		<category><![CDATA[aws]]></category>
		<category><![CDATA[cpu]]></category>
		<category><![CDATA[ec2]]></category>
		<category><![CDATA[t1.micro]]></category>

		<guid isPermaLink="false">http://andyleonard.com/?p=698</guid>
		<description><![CDATA[Amazon&#8217;s smallest and least expensive instance type, the t1.micro &#8220;provide[s] a small amount of consistent CPU resources and allow[s] you to burst CPU capacity when additional cycles are available. [It is] well suited for lower throughput applications and web sites that consume significant compute cycles periodically.&#8221; (source) Running a cpu-bound workload (building Perl modules) on [...]]]></description>
				<content:encoded><![CDATA[<p>Amazon&#8217;s smallest and least expensive instance type, the t1.micro &#8220;provide[s] a small amount of consistent CPU resources and allow[s] you to burst CPU capacity when additional cycles are available.  [It is] well suited for lower throughput applications and web sites that consume significant compute cycles periodically.&#8221; (<a href="http://aws.amazon.com/ec2/#instance">source</a>)</p>
<p>Running a cpu-bound workload (building Perl modules) on an Ubuntu 11.10 t1.micro instance in us-west-2 tonight, I noticed the following curious CPU usage pattern of approximately 15 seconds on, 60 seconds off:</p>
<pre class="brush: plain; light: true; title: ; notranslate">
&gt; vmstat 5
procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa
 1  0      0  38528  29524 370540    0    0    86   423   84  216 12  5 35  4
 1  0      0   6800  30288 388856    0    0  5356    26  660 1433 27 27  6 40
 5  0      0  21752  27624 378088    0    0    30   211  150  159 40 22  0  8
 6  0      0  21256  27636 378104    0    0     0    27    9    7  1  1  0  0
 7  0      0  21256  27644 378108    0    0     0    10    9    9  1  1  0  0
 7  0      0  21256  27652 378112    0    0     0     8    9    9  2  1  0  0
 7  0      0  20256  27652 378228    0    0     0     0    8   13  1  1  0  0
 8  0      0  20016  27660 378072    0    0     0   218   15   29  0  2  0  3
 6  0      0  37884  27672 378048    0    0     0    14    9   11  3  1  0  0
 4  0      0  30808  27684 378048    0    0     0    11    9   10  1  1  0  0
 4  0      0  23740  27692 378056    0    0     0    10    8    8  2  1  0  0
 4  0      0  30676  27692 378104    0    0     0     0   10   10  1  1  0  0
 5  0      0  26220  27700 378064    0    0     0     9    7   14  6  2  0  1
 5  0      0  21012  27712 378120    0    0     0    10    9   10  1  0  0  0
 5  0      0  27336  27720 378064    0    0     0    21   13   10  1  1  0  0
 1  0      0  29444  27732 378064    0    0     0    14  149   97 39 19  0  0
 1  0      0  33420  27744 378084    0    0     6    12  250  166 67 30  0  0
 2  0      0  41108  27756 378100    0    0     0    37  207  148 60 29  0  0
 6  0      0  33668  27768 378068    0    0     0    14    8    9  1  1  0  0
 5  0      0  37008  27780 378068    0    0     0    10   10   15  4  1  0  0
 4  0      0  30808  27788 378072    0    0     0    18   11    9  2  0  0  0
 5  0      0  24360  27796 378092    0    0     0     9    8    7  2  0  0  0
 2  0      0  19896  27796 378140    0    0     0     0    8    9  1  1  0  0
 6  0      0  27584  27804 378152    0    0     0     7    8   12  1  1  0  0
 6  0      0  22864  27812 378148    0    0     0     9   10   12  2  1  0  0
 7  0      0  19136  27820 378152    0    0     0    10    8    9  1  1  0  0
 6  0      0  26096  27828 378148    0    0     0    12   10    7  2  1  0  0
 6  0      0  20640  27828 378156    0    0     0    19   13    8  2  1  0  0
 6  0      0  27956  27836 378156    0    0     0    11    9   12  1  1  0  0
 6  0      0  22864  27844 378156    0    0     0     6    9   12  2  1  0  0
 6  0      0  19020  27844 378156    0    0     0     1    9    9  1  1  0  0
 2  0      0  46896  21504 368588    0    0   518    18  261  291 47 29  1  7
 1  0      0  35372  21692 368788    0    0     0    43  253  174 65 32  0  0
 1  0      0  43060  21796 368600    0    0     0    62  149  112 66 32  0  1
 5  0      0  38100  21808 368600    0    0     0    46   11   10  1  1  0  0
 5  0      0  45788  21816 368592    0    0     0     7    8   12  2  1  0  0
 7  0      0  38464  21816 368600    0    0     0     0    7    8  2  1  0  0
 7  0      0  45912  21824 368596    0    0     0    11    9    9  2  1  0  0
 7  0      0  39216  21832 368600    0    0     0     7    9    8  1  0  0  0
 4  0      0  35496  21840 368596    0    0     0    19   11    9  4  1  0  0
 5  0      0  43060  21848 368600    0    0     0    29   10   10  2  1  0  0
 5  0      0  37480  21856 368592    0    0     0    11    9   10  1  1  0  0
 5  0      0  45044  21864 368596    0    0     0     7    9   10  1  1  0  0
 5  0      0  38340  21872 368600    0    0     0     8    8    8  2  1  0  0
 4  0      0  46284  21880 368596    0    0     0    10   10   11  1  1  0  0
 6  0      0  38836  21888 368592    0    0     0     8    8    8  2  1  0  0
 1  0      0  38340  21888 368544    0    0     0    15   53   41 12  7  0  0
 1  0      0  40828  21900 368568    0    0     2    46  255  218 66 33  0  0
 1  0      0  39960  21912 368608    0    0     0    26  237  153 63 28  0  0
 3  0      0  50632  21924 368540    0    0     0    16   58   44 32 15  0  0
 4  0      0  46284  21932 368540    0    0     0     7    8   11  1  1  0  0
 4  0      0  45400  21940 368540    0    0     0     6    9   10  1  1  0  0
 5  0      0  45292  21948 368552    0    0     0    11    8   14  0  1  0  0
 6  0      0  37720  21948 368584    0    0     0    17   12    6  2  1  0  0
</pre>
<p>Apparently, the &#8220;small amount of consistent CPU resources&#8221; is about 3% of the CPU.</p>
<p>Moral of the story for me?  Next time, pay the big bucks and launch an m1.small spot instance.</p>
]]></content:encoded>
			<wfw:commentRss>http://andyleonard.com/2011/12/09/what-t1-micro-cpu-bursting-looks-like/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Deploying Ubuntu on Rackspace using Fog and Cloud-Init</title>
		<link>http://andyleonard.com/2011/11/28/deploying-ubuntu-on-rackspace-using-fog-and-cloud-init/</link>
		<comments>http://andyleonard.com/2011/11/28/deploying-ubuntu-on-rackspace-using-fog-and-cloud-init/#comments</comments>
		<pubDate>Mon, 28 Nov 2011 21:53:39 +0000</pubDate>
		<dc:creator>Andy</dc:creator>
				<category><![CDATA[utility computing]]></category>
		<category><![CDATA[cloud-init]]></category>
		<category><![CDATA[fog]]></category>
		<category><![CDATA[rackspace]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[ubuntu]]></category>

		<guid isPermaLink="false">http://andyleonard.com/?p=679</guid>
		<description><![CDATA[This post is an amalgamation of Vladimir Vuksan&#8217;s Provision to cloud in 5 minutes using fog (EC2-specific) and Jeff Gran&#8217;s Bootstrapping an Ubuntu Server on Rackspace Using Cloud-Init and Fog &#8211; I contributed little more than (inexpertly) gluing them together. Assuming you already have the Fog gem installed: First, as a prerequisite and as Jeff [...]]]></description>
				<content:encoded><![CDATA[<p>This post is an amalgamation of Vladimir Vuksan&#8217;s <a href="http://blog.vuksan.com/2010/07/20/provision-to-cloud-in-5-minutes-using-fog/">Provision to cloud in 5 minutes using fog</a> (EC2-specific) and Jeff Gran&#8217;s <a href="http://jeffgran.com/276/blog/ubuntu-cloud-init-rackspace-fog-ruby">Bootstrapping an Ubuntu Server on Rackspace Using Cloud-Init and Fog</a> &#8211; I contributed little more than (inexpertly) gluing them together.</p>
<p>Assuming you already have the Fog gem installed:</p>
<p>First, as a prerequisite and as Jeff Gran notes, you&#8217;ll need to create a Rackspace image with the cloud-init package installed.  </p>
<p>Next, similar to what Vladimir Vuksan describes, create a config.rb file, and populate the following values as appropriate for your environment:</p>
<pre class="brush: ruby; title: ; notranslate">
#!/usr/bin/env ruby

@flavor_id = 3
@image_id = 1234567

@rackspace_username =  'example'
@rackspace_api_key = '1234....'

@private_key_path = './ssh/id_rsa'
@public_key_path = './ssh/id_rsa.pub'
</pre>
<p>The flavor_id values and image_id specify the instance size and the image you built with cloud-init installed (see the &#8220;fog&#8221; executable&#8217;s &#8220;Compute[:rackspace].flavors&#8221; and &#8220;Compute[:rackspace].images&#8221;, respectively); the Rackspace username and api_key can be retrieved from within the console under &#8220;Your Account: API Access.&#8221;  The SSH key pair will be what you use to access the new instance as root.<br />
<span id="more-679"></span><br />
Third, create a cloud-init user_data file ERB template; place it in ./cloud-init/user_data.erb; for example:</p>
<pre class="brush: plain; title: ; notranslate">
#cloud-config
apt_upgrade: true
hostname: &lt;%= hostname %&gt;

packages:
- emacs
- git
- puppet

#runcmd:

#ssh_keys:
#  rsa_private: |
#  rsa_public:
#  dsa_private: |
#  dsa_public:
</pre>
<p>Finally, the script you launch to deploy Rackspace images is as follows:</p>
<pre class="brush: ruby; title: ; notranslate">
#!/usr/bin/env ruby

# Based on:
# http://blog.vuksan.com/2010/07/20/provision-to-cloud-in-5-minutes-using-fog/
# as well as:
# http://jeffgran.com/276/blog/ubuntu-cloud-init-rackspace-fog-ruby

require 'erb'
require 'optparse'

require 'rubygems'

require 'fog'
require 'mime'

# Parse options:
options = {}

optparse = OptionParser.new do|opts|
  opts.banner = &quot;Usage new_instance.rb [options]&quot;

  options[:name] = 'rax.example.com'
  opts.on( '-n', '--name INSTANCE_NAME', 'Instance name (default: rax.example.com)' ) do|n|
    options[:name] = n || 'rax.example.com'
  end

  options[:user_data] = './cloud-init/user_data.erb'
  opts.on( '-u', '--userdata USER_DATA', 'Path to Cloud-Init user_data ERB template (default: ./cloud-init/user_data.erb)' ) do |u|
    options[:user_data] = u || './cloud-init/user_data.erb'
  end

  opts.on( '-h', '--help', 'Display this help message' ) do
    puts opts
    exit
  end
end

optparse.parse!

hostname = options[:name] # to facilitate erb cloud-init template

# Create cloud-init data:

f = File.new(options[:user_data])
e = ERB.new(f.read)
user_data = MIME::MultipartMedia::Mixed.new
user_data.add_entity(MIME::TextMedia.new(e.result, 'text/plain'))

# Import Rackspace credentials:
require './config.rb'

# Connect to Rackspace:
connection = Fog::Compute.new({ 
  :provider           =&gt; 'Rackspace',
  :rackspace_api_key  =&gt; @rackspace_api_key, 
  :rackspace_username =&gt; @rackspace_username
})

# Launch instance:
puts &quot;Launching instance '#{options[:name]}'...&quot;

server = connection.servers.bootstrap({
  :flavor_id        =&gt; @flavor_id,
  :image_id         =&gt; @image_id,
  :name             =&gt; hostname,
  :personality      =&gt; [ { 'path'     =&gt; '/var/lib/cloud/seed/nocloud-net/user-data',
                           'contents' =&gt; user_data.to_s }, 
                         { 'path'     =&gt; '/var/lib/cloud/seed/nocloud-net/meta-data',
                           'contents' =&gt; ' ' } ],
  :private_key_path =&gt; @private_key_path,
  :public_key_path  =&gt; @public_key_path
})

puts &quot;Instance launched at #{server.public_ip_address()}&quot;
</pre>
<p>Launch it with the &#8220;-h&#8221; flag to see usage; otherwise, launch it with no arguments to launch an instance with your default options.</p>
]]></content:encoded>
			<wfw:commentRss>http://andyleonard.com/2011/11/28/deploying-ubuntu-on-rackspace-using-fog-and-cloud-init/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Replacing a Failed NetApp Drive with an Un-zeroed Spare</title>
		<link>http://andyleonard.com/2011/05/28/replacing-a-failed-netapp-drive-with-an-un-zeroed-spare/</link>
		<comments>http://andyleonard.com/2011/05/28/replacing-a-failed-netapp-drive-with-an-un-zeroed-spare/#comments</comments>
		<pubDate>Sat, 28 May 2011 13:42:01 +0000</pubDate>
		<dc:creator>Andy</dc:creator>
				<category><![CDATA[storage]]></category>
		<category><![CDATA[aggr]]></category>
		<category><![CDATA[netapp]]></category>
		<category><![CDATA[spare]]></category>
		<category><![CDATA[zero]]></category>

		<guid isPermaLink="false">http://andyleonard.com/?p=664</guid>
		<description><![CDATA[Jason Boche has a post on the method he used to replace a failed drive on a filer with an un-zeroed spare (transferred from a lab machine); my procedure was a little different. In this example, I&#8217;ll be installing a replacement drive pulled from aggr0 on another filer. Note that this procedure is not relevant [...]]]></description>
				<content:encoded><![CDATA[<p>Jason Boche has a <a href="http://www.boche.net/blog/index.php/2010/05/13/netapp-disk-replacement-so-easy-a-caveman-and-his-tech-savvy-neighbor-can-do-it/">post</a> on the method he used to replace a failed drive on a filer with an un-zeroed spare (transferred from a lab machine); my procedure was a little different.</p>
<p>In this example, I&#8217;ll be installing a replacement drive pulled from aggr0 on another filer.  Note that this procedure is not relevant for drive failures covered by a support contract, where you will receive a zeroed replacement drive directly from NetApp.</p>
<ul>
<li>Physically remove failed drive and replace with working drive.  This will generate log messages similar to the following:
<pre class="brush: plain; title: ; notranslate">
May 27 11:02:36 filer01 [raid.disk.missing: info]: Disk 1b.51 Shelf 3 Bay 3 [NETAPP   X268_SGLXY750SSX AQNZ] S/N [5QD599LZ] is missing from the system
May 27 11:03:00 filer01 [monitor.globalStatus.ok: info]: The system's global status is normal. 
May 27 11:03:16 filer01 [scsi.cmd.notReadyCondition: notice]: Disk device 0a.51: Device returns not yet ready: CDB 0x12: Sense Data SCSI:not ready - Drive spinning up (0x2 - 0x4 0x1 0x0)(7715).
May 27 11:03:25 filer01 [sfu.firmwareUpToDate: info]: Firmware is up-to-date on all disk shelves.
May 27 11:03:27 filer01 [diskown.changingOwner: info]: changing ownership for disk 0a.51 (S/N P8G9SMDF) from unowned (ID -1) to filer01 (ID 135027165)
May 27 11:03:27 filer01 [raid.assim.rg.missingChild: error]: Aggregate foreign:aggr0, rgobj_verify: RAID object 0 has only 1 valid children, expected 14.
May 27 11:03:27 filer01 [raid.assim.plex.missingChild: error]: Aggregate foreign:aggr0, plexobj_verify: Plex 0 only has 0 working RAID groups (2 total) and is being taken offline
May 27 11:03:27 filer01 [raid.assim.mirror.noChild: ALERT]: Aggregate foreign:aggr0, mirrorobj_verify: No operable plexes found.
May 27 11:03:27 filer01 [raid.assim.tree.foreign: error]: raidtree_verify: Aggregate aggr0 is a foreign aggregate and is being taken offline. Use the 'aggr online' command to bring it online.
May 27 11:03:27 filer01 [raid.assim.tree.dupName: error]: Duplicate aggregate names found, an instance of foreign:aggr0 is being renamed to foreign:aggr0(1).
May 27 11:03:28 filer01 [sfu.firmwareUpToDate: info]: Firmware is up-to-date on all disk shelves.
May 27 11:04:40 filer01 [asup.smtp.sent: notice]: System Notification mail sent: System Notification from filer01 (RAID VOLUME FAILED) ERROR
May 27 11:04:42 filer01 [asup.post.sent: notice]: System Notification message posted to NetApp: System Notification from filer01 (RAID VOLUME FAILED) ERROR
</pre>
<p>Note line 6, where it identifies the newly-added disk as part of &#8220;foreign:aggr0&#8243; and missing the rest of its RAID group; &#8220;foreign:aggr0&#8243; is taken offline in line 9.  In line 10, &#8220;foreign:aggr0&#8243; is renamed to &#8220;foreign:aggr0(1)&#8221; because the filer already has an aggr0, as you might expect.  Be sure to note the new aggregate name, as you will need it for later steps.</li>
<li>Verify aggregate status and names:
<pre class="brush: plain; light: true; title: ; notranslate">
filer01&gt; aggr status
           Aggr State           Status            Options
          aggr0 online          raid_dp, aggr     root
          aggr1 online          raid_dp, aggr     
       aggr0(1) failed          raid_dp, aggr     diskroot, lost_write_protect=off,
                                foreign           
                                partial           
          aggr2 online          raid_dp, aggr     nosnap=on
</pre>
</li>
<li>Double-check the name of the foreign, offline aggregate that was brought in with the replacement drive, and destroy it:
<pre class="brush: plain; light: true; title: ; notranslate">
filer01&gt; aggr destroy aggr0(1)
Are you sure you want to destroy this aggregate? yes
Aggregate 'aggr0(1)' destroyed.
</pre>
</li>
<li>Verify that the aggregate has been removed:
<pre class="brush: plain; light: true; title: ; notranslate">
netapp03&gt; aggr status          
           Aggr State           Status            Options
          aggr0 online          raid_dp, aggr     root
          aggr1 online          raid_dp, aggr     
          aggr2 online          raid_dp, aggr     nosnap=on
</pre>
</li>
<li>Zero the new spare.  First, confirm it is un-zeroed:
<pre class="brush: plain; light: true; title: ; notranslate">
filer01&gt; vol status -s

Spare disks

RAID Disk	Device	HA  SHELF BAY CHAN Pool Type  RPM  Used (MB/blks)    Phys (MB/blks)
---------	------	------------- ---- ---- ---- ----- --------------    --------------
Spare disks for block or zoned checksum traditional volumes or aggregates
spare   	0a.53	0a    3   5   FC:B   -  ATA   7200 635555/1301618176 635858/1302238304 (not zeroed)
spare   	0a.69	0a    4   5   FC:B   -  ATA   7200 635555/1301618176 635858/1302238304 
spare   	1b.51	1b    3   3   FC:A   -  ATA   7200 635555/1301618176 635858/1302238304 (not zeroed)
spare   	1b.61	1b    3   13  FC:A   -  ATA   7200 635555/1301618176 635858/1302238304 
spare   	1b.87	1b    5   7   FC:A   -  ATA   7200 847555/1735794176 847827/1736350304 
spare   	1b.89	1b    5   9   FC:A   -  ATA   7200 847555/1735794176 847827/1736350304 
</pre>
<p>In this example, we actually have two un-zeroed spares &#8211; the newly replaced drive (1b.51) and another drive (0a.53).  Zero them both:</p>
<pre class="brush: plain; light: true; title: ; notranslate">
filer01&gt; disk zero spares
</pre>
<p>And verify that they have been zeroed:</p>
<pre class="brush: plain; light: true; title: ; notranslate">
filer01&gt; vol status -s

Spare disks

RAID Disk	Device	HA  SHELF BAY CHAN Pool Type  RPM  Used (MB/blks)    Phys (MB/blks)
---------	------	------------- ---- ---- ---- ----- --------------    --------------
Spare disks for block or zoned checksum traditional volumes or aggregates
spare   	0a.53	0a    3   5   FC:B   -  ATA   7200 635555/1301618176 635858/1302238304 
spare   	0a.69	0a    4   5   FC:B   -  ATA   7200 635555/1301618176 635858/1302238304 
spare   	1b.51	1b    3   3   FC:A   -  ATA   7200 635555/1301618176 635858/1302238304 
spare   	1b.61	1b    3   13  FC:A   -  ATA   7200 635555/1301618176 635858/1302238304 
spare   	1b.87	1b    5   7   FC:A   -  ATA   7200 847555/1735794176 847827/1736350304 
spare   	1b.89	1b    5   9   FC:A   -  ATA   7200 847555/1735794176 847827/1736350304 
</pre>
</li>
<li>Done.  You have replaced a failed drive with a zeroed spare.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://andyleonard.com/2011/05/28/replacing-a-failed-netapp-drive-with-an-un-zeroed-spare/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>HAProxy and Keepalived: Example Configuration</title>
		<link>http://andyleonard.com/2011/02/01/haproxy-and-keepalived-example-configuration/</link>
		<comments>http://andyleonard.com/2011/02/01/haproxy-and-keepalived-example-configuration/#comments</comments>
		<pubDate>Wed, 02 Feb 2011 05:17:14 +0000</pubDate>
		<dc:creator>Andy</dc:creator>
				<category><![CDATA[Applications]]></category>
		<category><![CDATA[haproxy]]></category>
		<category><![CDATA[http]]></category>
		<category><![CDATA[keepalived]]></category>
		<category><![CDATA[proxy]]></category>
		<category><![CDATA[vrrp]]></category>

		<guid isPermaLink="false">http://andyleonard.com/?p=628</guid>
		<description><![CDATA[HAProxy is load balancer software that allows you to proxy HTTP and TCP connections to a pool of back-end servers; Keepalived &#8211; among other uses &#8211; allows you to create a redundant pair of HAProxy servers by moving an IP address between HAProxy hosts in an active-passive configuration. Example Network For this example, the following [...]]]></description>
				<content:encoded><![CDATA[<p><a href="http://haproxy.1wt.eu/">HAProxy</a> is load balancer software that allows you to proxy HTTP and TCP connections to a pool of back-end servers; <a href="http://www.keepalived.org/">Keepalived</a> &#8211; among other uses &#8211; allows you to create a redundant pair of HAProxy servers by moving an IP address between HAProxy hosts in an active-passive configuration.<br />
<span id="more-628"></span><br />
<strong>Example Network</strong></p>
<p>For this example, the following services are present, or will be configured here:</p>
<ul>
<li>192.168.1.34: Syslog server</li>
<li>192.168.1.68: Web backend #1</li>
<li>192.168.1.69: Web backend #2</li>
<li>192.168.1.80: HAProxy host #1 management address</li>
<li>192.168.1.81: HAProxy host #2 management address</li>
<li>192.168.1.84: Load balancer &#8220;front end&#8221; &#8211; the IP address users will ultimately connect to, managed by Keepalived</li>
<li>192.168.1.119: SMTP server</li>
</ul>
<p>Note that all interfaces are on the same subnet (192.168.1.0/24); the load balancer communicates with the backends on the same interface it uses for client connections.</p>
<p><strong>HAProxy</strong></p>
<p>To install HAProxy on Ubuntu, simply install the &#8220;haproxy&#8221; package; as described above, this example uses two hosts with IP addresses of 192.168.1.80 and 192.168.1.81.  Set &#8220;ENABLED=1&#8243; in /etc/default/haproxy to have the init script that comes with the package start HAProxy.  HAProxy&#8217;s configuration lives at /etc/haproxy/haproxy.cfg:</p>
<pre class="brush: plain; title: ; notranslate">
# this config needs haproxy-1.1.28 or haproxy-1.2.1

global
        # log 127.0.0.1 local0
        # log 127.0.0.1 local1 notice
        log 192.168.1.34 local0
        user haproxy
        group haproxy
        daemon
        maxconn 20000
 
defaults
        log global
        option dontlognull
        balance leastconn
        clitimeout 60000
        srvtimeout 60000
        contimeout 5000
        retries 3
        option redispatch

listen stats 192.168.1.80:80 # or 192.168.1.81:80 for second HAProxy host
        mode http
        stats enable
        stats uri /stats
        stats realm HAProxy\ Statistics
        stats auth admin:supersecret
 
listen http 192.168.1.84:80
        mode tcp
        option tcplog
        balance source
        maxconn 10000
        server web01 192.168.1.68:80 maxconn 5000
        server web02 192.168.1.69:80 maxconn 5000
 
 listen https 192.168.1.84:443 
        mode tcp
        option tcplog
        balance roundrobin
        maxconn 10000
        server web01 192.168.1.68:443 maxconn 5000
        server web02 192.168.1.69:443 maxconn 5000
</pre>
<p><strong>Config Notes</strong><br />
06. Send log messages to the syslog server at 192.168.1.34 using the local0 facility<br />
22. Only stats are provided on this interface &#8211; eth0 on the host.<br />
27. Replace &#8220;supersecret&#8221; with your password for the statistics interface.<br />
29. HTTP listener &#8211; 192.168.1.84 is the IP address Keepalived will manage.<br />
30. We are passing HTTP through &#8211; not terminating it on &#8211; the HAProxy host.<br />
31. In case you&#8217;re wondering why we&#8217;re not using Keepalived alone, given that we&#8217;re not terminating HTTP at HAProxy: The log format options available with HAProxy.  (This also gives us the option of changing to HTTP termination in HAProxy later, without installing new software, of course.)<br />
32. Balance based on source address.<br />
34-35. Two HTTP backends.<br />
37. HTTPS listener, again on the Keepalived IP address.<br />
38. HTTPS is also passed through to the web back-ends.<br />
39. See 31 above.<br />
40. Use a round-robin balancing algorithm.<br />
42-43. Two HTTPS backends.</p>
<p>Start HAProxy as follows:</p>
<pre class="brush: plain; light: true; title: ; notranslate">
# service haproxy start
</pre>
<p><strong>Keepalived</strong></p>
<p>Keepalived is installed via the Ubuntu &#8220;keepalived&#8221; package, intuitively enough.  The following entry needs to be made in /etc/sysctl.conf:</p>
<pre class="brush: plain; light: true; title: ; notranslate">
net.ipv4.ip_nonlocal_bind=1
</pre>
<p>(net.ipv4.ip_forward does not need to be set to &#8220;1&#8243; for this type of configuration.)  Run &#8220;sysctl -p&#8221; or reboot to apply the sysctl setting.</p>
<p>The keepalived file &#8211; /etc/keepalived/keepalived.conf &#8211; contains:</p>
<pre class="brush: plain; title: ; notranslate">
global_defs {
    notification_email {
        sysadmin@example.com
    }
    notification_email_from keepalived@haproxy01.example.com
    smtp_server 192.168.1.119
    smtp_connect_timeout 30
}

vrrp_script chk_haproxy { # Requires keepalived-1.1.13
    script &quot;killall -0 haproxy&quot; # widely used idiom
    interval 2 # check every 2 seconds
    weight 2 # add 2 points of prio if OK
}

vrrp_instance VI_1 {
    interface eth0
    state MASTER # or &quot;BACKUP&quot; on backup
    priority 101 # 101 on master, 100 on backup
    virtual_router_id 51

    smtp_alert # Activate SMTP notifications

    authentication {
        auth_type AH
        auth_pass supersecret
    }

    virtual_ipaddress {
        192.168.1.84
    }

    track_script {
        chk_haproxy
    }
}
</pre>
<p><strong>Config Notes</strong><br />
02-07. Configuration for email notifications on master/backup state changes.<br />
10-14. Script to check if HAProxy is still alive. (&#8220;<a href="http://www.google.com/search?q=cheaper+than+pidof">killall -0</a>&#8221; is a common idiom seen in Keepalived configurations; I&#8217;m curious to know the original source.)<br />
18-19. Configure master (or backup).<br />
22. Send SMTP notifications on master/backup state change for this interface.<br />
26. Set authentication password to something better than &#8220;supersecret&#8221; here.<br />
30. IP address of the virtual interface &#8211; in this case, the IP address in front of the load balancer.<br />
34. Use the chk_haproxy script defined above to check whether to fail over this interface or not.</p>
<p>Start the keepalived daemon:</p>
<pre class="brush: plain; light: true; title: ; notranslate">
service keepalived start
</pre>
<p><strong>Acknowledgement:</strong> My co-worker Will Winslow helped with much of the research for this post.<br />
<strong>Reference:</strong> <a href="http://haproxy.1wt.eu/download/1.3/doc/architecture.txt">HAProxy Architecture Guide</a></p>
]]></content:encoded>
			<wfw:commentRss>http://andyleonard.com/2011/02/01/haproxy-and-keepalived-example-configuration/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>S3fs, or, 256TB of Storage on the Cheap</title>
		<link>http://andyleonard.com/2011/01/25/s3fs-or-256tb-of-storage-on-the-cheap/</link>
		<comments>http://andyleonard.com/2011/01/25/s3fs-or-256tb-of-storage-on-the-cheap/#comments</comments>
		<pubDate>Tue, 25 Jan 2011 14:59:14 +0000</pubDate>
		<dc:creator>Andy</dc:creator>
				<category><![CDATA[utility computing]]></category>
		<category><![CDATA[aws]]></category>
		<category><![CDATA[puppet]]></category>
		<category><![CDATA[s3]]></category>
		<category><![CDATA[s3fs]]></category>

		<guid isPermaLink="false">http://andyleonard.com/?p=624</guid>
		<description><![CDATA[There&#8217;s something pretty satisfying about seeing 256TB of storage available on a machine and knowing that you&#8217;re only paying pennies for what you&#8217;re using: In the words of its authors, &#8220;s3fs is a FUSE filesystem that allows you to mount an Amazon S3 bucket as a local filesystem. It stores files natively and transparently in [...]]]></description>
				<content:encoded><![CDATA[<p>There&#8217;s something pretty satisfying about seeing 256TB of storage available on a machine and knowing that you&#8217;re only paying <a href="http://aws.amazon.com/s3/#pricing">pennies</a> for what you&#8217;re using:</p>
<pre class="brush: plain; light: true; title: ; notranslate">
&gt; df -h /cloud/hrc/src/
Filesystem            Size  Used Avail Use% Mounted on
s3fs-1.35             256T     0  256T   0% /cloud/hrc/src
</pre>
<p><span id="more-624"></span><br />
In the words of its authors, &#8220;<a href="http://code.google.com/p/s3fs/">s3fs</a> is a FUSE filesystem that allows you to mount an Amazon S3 bucket as a local filesystem. It stores files natively and transparently in S3 (i.e., you can use other programs to access the same files).&#8221;</p>
<p>Now, make no mistake about it &#8211; since s3fs is backed by object storage in a remote data center, this is not for high- or even moderate-IOPS workloads.  Routine tasks like expanding tarballs containing many small files or compiling code on an s3fs file system can be painful.  But for &#8220;colder&#8221; storage applications &#8211; think online archives, or possibly some backup applications &#8211; it shines.</p>
<p>The <a href="http://code.google.com/p/s3fs/wiki/FuseOverAmazon">installation procedure</a> for s3fs is straightforward.  I&#8217;ve also put a Puppet module for installing s3fs and managing its mounts on <a href="https://github.com/anl/puppet-s3fs">GitHub</a>, although you may want to adapt it to distribute your own package of s3fs instead of building it locally on each machine.</p>
<p>S3fs is licensed under the GPL, as is my Puppet module.</p>
]]></content:encoded>
			<wfw:commentRss>http://andyleonard.com/2011/01/25/s3fs-or-256tb-of-storage-on-the-cheap/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
