Nick Sieger: RailsConf: Mike Clark -- Introduction to Capistranotag:blog.nicksieger.com,2005:TypoTypo2010-11-22T19:01:10+00:00Nick Siegerurn:uuid:fd4b197e-ec51-47aa-bc0c-7e90a4339f192006-06-23T16:45:00+00:002010-11-22T19:01:10+00:00RailsConf: Mike Clark -- Introduction to Capistrano<h2>Introduction to Capistrano</h2>
<p>Mike Clark has been Java-free for 15 months and 16 days. He’s here to
talk about deployment with Capistrano.</p>
<p>First off, props to Jamis! There are several hundred people in the
room and it’s a tribute to him.</p>
<p>Capistrano is about making it easy to deploy web applications. “Cap”,
the short name has quickly become jargon in the Rails community.
“Stop wasting time, just cap it!”</p>
<p>Capistrano is built to scale. From a single machine all the way up to
clusters, it is intended to make application deployment as simple as
the push of a button. That’s priceless for reducing friction as your
project nears its first production live date, as well as subsequent
maintenance releases.</p>
<h3>Configuring Capistrano - create a recipe</h3>
<h4>Set the application name</h4>
<div class="typocode"><pre><code class="typocode_ruby "><span class="ident">set</span> <span class="symbol">:application</span><span class="punct">,</span> <span class="punct">"</span><span class="string">depot</span><span class="punct">"</span></code></pre></div>
<h4>Set the repository</h4>
<div class="typocode"><pre><code class="typocode_ruby "><span class="ident">set</span> <span class="symbol">:repository</span><span class="punct">,</span> <span class="punct">"</span><span class="string">http://svn.yourhost.com/<span class="expr">#{application}</span>/trunk</span><span class="punct">"</span></code></pre></div>
<h4>Roles</h4>
<div class="typocode"><pre><code class="typocode_ruby "><span class="ident">role</span> <span class="symbol">:app</span><span class="punct">,</span> <span class="punct">"</span><span class="string">app01.example.com</span><span class="punct">",</span> <span class="punct">"</span><span class="string">app02.example.com</span><span class="punct">"</span>
<span class="ident">role</span> <span class="symbol">:web</span><span class="punct">,</span> <span class="punct">"</span><span class="string">web01.example.com</span><span class="punct">",</span> <span class="punct">"</span><span class="string">web02.example.com</span><span class="punct">"</span>
<span class="ident">role</span> <span class="symbol">:db</span><span class="punct">,</span> <span class="punct">"</span><span class="string">db.example.com</span><span class="punct">",</span> <span class="symbol">:primary</span> <span class="punct">=></span> <span class="constant">true</span></code></pre></div>
<h4>Deployment Root</h4>
<div class="typocode"><pre><code class="typocode_ruby "><span class="ident">set</span> <span class="symbol">:deploy_to</span><span class="punct">,</span> <span class="punct">"</span><span class="string">/Library/Rails/<span class="expr">#{application}</span></span><span class="punct">"</span></code></pre></div>
<h3>Setup</h3>
<p>Run <code>cap setup</code> once to setup the deployment directory structure on
all the roles you’ve configured:</p>
<pre><code>depot
`- releases
`- shared
`- log
`- system
</code></pre>
<h3>First time deploy</h3>
<p>Run <code>cap update_code symlink</code>. This checks out the code, adds a
“current” symlink to the code on the remote machine that points to the
timestamped releases directory where the code was checked out. You
can then manually start your web server(s) if you wish.</p>
<h3>New Release</h3>
<p>Run <code>cap deploy</code>. The newly committed code to your repository gets
pulled, a new release created, and it also restarts fcgi processes.</p>
<h3>Rollback release</h3>
<p>Run <code>cap rollback</code>. The “current” symlink gets updated to the previous release,
and the fcgi processes get restarted.</p>
<h3>Scheduled downtime</h3>
<p>Run <code>cap disable_web</code>. A maintenance screen is put up during the
maintenance period. This is great when you’re in firefighting mode
and you don’t want to think, just get the page up there. To enable
again, use <code>cap enable_web</code>.</p>
<h3>Customization</h3>
<p>Here are examples of several useful, real-world tasks. The fourth
task shows aggregation of tasks together.</p>
<div class="typocode"><pre><code class="typocode_ruby "><span class="ident">desc</span> <span class="punct">"</span><span class="string">locate ruby</span><span class="punct">"</span>
<span class="ident">task</span> <span class="symbol">:which_ruby</span><span class="punct">,</span> <span class="symbol">:roles</span> <span class="punct">=></span> <span class="punct">[</span><span class="symbol">:app</span><span class="punct">]</span> <span class="keyword">do</span>
<span class="ident">puts</span> <span class="punct">"</span><span class="string">You're running:</span><span class="punct">"</span>
<span class="ident">run</span> <span class="punct">"</span><span class="string">which ruby</span><span class="punct">"</span>
<span class="keyword">end</span>
<span class="ident">task</span> <span class="symbol">:current_revision</span> <span class="keyword">do</span>
<span class="ident">run</span> <span class="punct">"</span><span class="string">echo Current rev is <span class="expr">#{revision}</span></span><span class="punct">"</span>
<span class="ident">run</span> <span class="punct">"</span><span class="string">echo Current rev is at <span class="expr">#{releast_path}</span></span><span class="punct">"</span>
<span class="keyword">end</span>
<span class="ident">task</span> <span class="symbol">:uptime</span><span class="punct">,</span> <span class="symbol">:roles</span> <span class="punct">=></span> <span class="punct">[</span><span class="symbol">:app</span><span class="punct">,</span> <span class="symbol">:web</span><span class="punct">,</span> <span class="symbol">:db</span><span class="punct">]</span> <span class="keyword">do</span>
<span class="ident">run</span> <span class="punct">"</span><span class="string">uptime</span><span class="punct">"</span>
<span class="keyword">end</span>
<span class="ident">task</span> <span class="symbol">:status</span> <span class="keyword">do</span>
<span class="ident">which_ruby</span>
<span class="ident">current_revision</span>
<span class="ident">uptime</span>
<span class="keyword">end</span></code></pre></div>
<h3>Channels and streams</h3>
<p>Mike showed a log file tailing task here. I failed to capture the full task code, so you’re on your own here.</p>
<h3>Hooks</h3>
<div class="typocode"><pre><code class="typocode_ruby "><span class="ident">task</span> <span class="symbol">:before_deploy</span><span class="punct">,</span> <span class="symbol">:roles</span> <span class="punct">=></span> <span class="punct">[</span><span class="symbol">:app</span><span class="punct">]</span> <span class="keyword">do</span>
<span class="ident">cleanup</span>
<span class="keyword">end</span>
<span class="ident">task</span> <span class="symbol">:after_update_code</span><span class="punct">,</span> <span class="symbol">:roles</span> <span class="punct">=></span> <span class="punct">[</span><span class="symbol">:app</span><span class="punct">]</span> <span class="keyword">do</span>
<span class="ident">production_config</span> <span class="punct">=</span> <span class="punct">"</span><span class="string">path/to/database.yml</span><span class="punct">"</span>
<span class="ident">run</span> <span class="punct">"</span><span class="string">cp <span class="expr">#{production_config}</span> <span class="expr">#{current_path}</span>/config/database.yml</span><span class="punct">"</span>
<span class="keyword">end</span></code></pre></div>
<h3>Multiple configs</h3>
<div class="typocode"><pre><code class="typocode_ruby "><span class="keyword">if</span> <span class="ident">where</span> <span class="punct">==</span> <span class="punct">"</span><span class="string">production</span><span class="punct">"</span>
<span class="ident">set</span> <span class="symbol">:user</span><span class="punct">,</span> <span class="punct">"</span><span class="string">ops_gal</span><span class="punct">"</span>
<span class="keyword">else</span>
<span class="ident">set</span> <span class="symbol">:user</span><span class="punct">,</span> <span class="punct">"</span><span class="string">dev_guy</span><span class="punct">"</span>
<span class="keyword">end</span></code></pre></div>
<h3>Task libraries</h3>
<p>You can creat a file full of extra, reusable tasks and simply
<code>require</code> them in. The files can be published and installed as gems
or placed anywhere on your load path.</p>
<h3>Capistrano isn’t just for Rails</h3>
<p>It’s great at shuttling files around and executing remote commands on
the server. Mike describes some automation James Duncan Davidson did
to replicate new servers into a cluster with the push of a button.</p>
<h3>Assumptions</h3>
<p>Capistrano, out of the box comes with some assumptions:</p>
<ul>
<li>Remote servers talk POSIX</li>
<li>Same deploy structure and password on each machine</li>
<li>Web application uses FastCGI with Apache or Lighty</li>
</ul>
<p>It looks like you can bend some of these assumptions if you are
willing to write your own custom deployment tasks.</p>
<h3>Take Home</h3>
<p>Capistrano provides easy, consistent, worry-free deployment of new
releases. The friction to deploying your app just got simpler, so you
can go enjoy your life!</p>
<h3>Notes and Questions</h3>
<ul>
<li>Can you use SSH public keys to authenticate? <em>Yes, this is the most
typical scenario.</em></li>
<li>You can use <code>sudo</code> to execute remote commands -- this is built-in.</li>
<li>Can you/would you want to check out subsets of files on each server,
rather than pulling all the code everywhere? <em>So far most of our
stuff has been on the same server, so this hasn’t been an issue. It
seems like you would need to override deployment tasks for some
roles.</em></li>
<li>Sometimes I want to deploy a lot, but what if the app is big, lots
of media files? It can take a longer time to pull all the
code. <em>One solution I’ve seen is to check out once and rsync
everywhere else. I haven’t had to do that myself.</em></li>
<li>Who can I talk to for promoting my own extensions into Capistrano.
<em>You probably don’t. Instead, publish your extensions, blog
them, and see if it takes.</em></li>
<li><code>deploy_with_migrations</code> -- use with caution as it’s not as easy to
rollback, and not all migrations can be rolled back cleanly.</li>
</ul>