Nick Sieger: RailsConf: Mike Clark -- Introduction to Capistrano tag:blog.nicksieger.com,2005:Typo Typo 2010-11-22T19:01:10+00:00 Nick Sieger urn:uuid:fd4b197e-ec51-47aa-bc0c-7e90a4339f19 2006-06-23T16:45:00+00:00 2010-11-22T19:01:10+00:00 RailsConf: Mike Clark -- Introduction to Capistrano <h2>Introduction to Capistrano</h2> <p>Mike Clark has been Java&#45;free for 15 months and 16 days&#46; He&#8217;s here to talk about deployment with Capistrano&#46;</p> <p>First off, props to Jamis! There are several hundred people in the room and it&#8217;s a tribute to him&#46;</p> <p>Capistrano is about making it easy to deploy web applications&#46; &#8220;Cap&#8221;, the short name has quickly become jargon in the Rails community&#46; &#8220;Stop wasting time, just cap it!&#8221;</p> <p>Capistrano is built to scale&#46; 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&#46; That&#8217;s priceless for reducing friction as your project nears its first production live date, as well as subsequent maintenance releases&#46;</p> <h3>Configuring Capistrano &#45; 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">&quot;</span><span class="string">depot</span><span class="punct">&quot;</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">&quot;</span><span class="string">http://svn.yourhost.com/<span class="expr">#{application}</span>/trunk</span><span class="punct">&quot;</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">&quot;</span><span class="string">app01.example.com</span><span class="punct">&quot;,</span> <span class="punct">&quot;</span><span class="string">app02.example.com</span><span class="punct">&quot;</span> <span class="ident">role</span> <span class="symbol">:web</span><span class="punct">,</span> <span class="punct">&quot;</span><span class="string">web01.example.com</span><span class="punct">&quot;,</span> <span class="punct">&quot;</span><span class="string">web02.example.com</span><span class="punct">&quot;</span> <span class="ident">role</span> <span class="symbol">:db</span><span class="punct">,</span> <span class="punct">&quot;</span><span class="string">db.example.com</span><span class="punct">&quot;,</span> <span class="symbol">:primary</span> <span class="punct">=&gt;</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">&quot;</span><span class="string">/Library/Rails/<span class="expr">#{application}</span></span><span class="punct">&quot;</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&#8217;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>&#46; This checks out the code, adds a &#8220;current&#8221; symlink to the code on the remote machine that points to the timestamped releases directory where the code was checked out&#46; You can then manually start your web server(s) if you wish&#46;</p> <h3>New Release</h3> <p>Run <code>cap deploy</code>&#46; The newly committed code to your repository gets pulled, a new release created, and it also restarts fcgi processes&#46;</p> <h3>Rollback release</h3> <p>Run <code>cap rollback</code>&#46; The &#8220;current&#8221; symlink gets updated to the previous release, and the fcgi processes get restarted&#46;</p> <h3>Scheduled downtime</h3> <p>Run <code>cap disable_web</code>&#46; A maintenance screen is put up during the maintenance period&#46; This is great when you&#8217;re in firefighting mode and you don&#8217;t want to think, just get the page up there&#46; To enable again, use <code>cap enable_web</code>&#46;</p> <h3>Customization</h3> <p>Here are examples of several useful, real&#45;world tasks&#46; The fourth task shows aggregation of tasks together&#46;</p> <div class="typocode"><pre><code class="typocode_ruby "><span class="ident">desc</span> <span class="punct">&quot;</span><span class="string">locate ruby</span><span class="punct">&quot;</span> <span class="ident">task</span> <span class="symbol">:which_ruby</span><span class="punct">,</span> <span class="symbol">:roles</span> <span class="punct">=&gt;</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">&quot;</span><span class="string">You're running:</span><span class="punct">&quot;</span> <span class="ident">run</span> <span class="punct">&quot;</span><span class="string">which ruby</span><span class="punct">&quot;</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">&quot;</span><span class="string">echo Current rev is <span class="expr">#{revision}</span></span><span class="punct">&quot;</span> <span class="ident">run</span> <span class="punct">&quot;</span><span class="string">echo Current rev is at <span class="expr">#{releast_path}</span></span><span class="punct">&quot;</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">=&gt;</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">&quot;</span><span class="string">uptime</span><span class="punct">&quot;</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&#46; I failed to capture the full task code, so you&#8217;re on your own here&#46;</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">=&gt;</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">=&gt;</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">&quot;</span><span class="string">path/to/database.yml</span><span class="punct">&quot;</span> <span class="ident">run</span> <span class="punct">&quot;</span><span class="string">cp <span class="expr">#{production_config}</span> <span class="expr">#{current_path}</span>/config/database.yml</span><span class="punct">&quot;</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">&quot;</span><span class="string">production</span><span class="punct">&quot;</span> <span class="ident">set</span> <span class="symbol">:user</span><span class="punct">,</span> <span class="punct">&quot;</span><span class="string">ops_gal</span><span class="punct">&quot;</span> <span class="keyword">else</span> <span class="ident">set</span> <span class="symbol">:user</span><span class="punct">,</span> <span class="punct">&quot;</span><span class="string">dev_guy</span><span class="punct">&quot;</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&#46; The files can be published and installed as gems or placed anywhere on your load path&#46;</p> <h3>Capistrano isn&#8217;t just for Rails</h3> <p>It&#8217;s great at shuttling files around and executing remote commands on the server&#46; Mike describes some automation James Duncan Davidson did to replicate new servers into a cluster with the push of a button&#46;</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&#46;</p> <h3>Take Home</h3> <p>Capistrano provides easy, consistent, worry&#45;free deployment of new releases&#46; 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&#46;</em></li> <li>You can use <code>sudo</code> to execute remote commands &#45;&#45; this is built&#45;in&#46;</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&#8217;t been an issue&#46; It seems like you would need to override deployment tasks for some roles&#46;</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&#46; <em>One solution I&#8217;ve seen is to check out once and rsync everywhere else&#46; I haven&#8217;t had to do that myself&#46;</em></li> <li>Who can I talk to for promoting my own extensions into Capistrano&#46; <em>You probably don&#8217;t&#46; Instead, publish your extensions, blog them, and see if it takes&#46;</em></li> <li><code>deploy_with_migrations</code> &#45;&#45; use with caution as it&#8217;s not as easy to rollback, and not all migrations can be rolled back cleanly&#46;</li> </ul>