<?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>Luke&#039;s Programming Blog &#187; Capistrano</title>
	<atom:link href="http://blog.lukeludwig.com/index.php/tag/capistrano/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.lukeludwig.com</link>
	<description>A blog about programming, ruby, rails.</description>
	<lastBuildDate>Mon, 11 Apr 2011 19:29:16 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.6</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Capistrano Tip to Avoid Disk Intensive Removal of Files</title>
		<link>http://blog.lukeludwig.com/index.php/2009/05/14/capistrano-tip-to-avoid-disk-intensive-removal-of-files/</link>
		<comments>http://blog.lukeludwig.com/index.php/2009/05/14/capistrano-tip-to-avoid-disk-intensive-removal-of-files/#comments</comments>
		<pubDate>Thu, 14 May 2009 23:33:09 +0000</pubDate>
		<dc:creator>Luke</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Capistrano]]></category>
		<category><![CDATA[Rails]]></category>

		<guid isPermaLink="false">http://blog.lukeludwig.com/?p=39</guid>
		<description><![CDATA[At TST Media we host our Rails app at Engine Yard on four slices which all utilize the same shared disk via gfs. Anytime there is any hard core disk activity our sites slow down to a crawl. This makes removing files a bit tricky, such as when we want to empty our cache files [...]]]></description>
			<content:encoded><![CDATA[<p>At <a href="http://www.tstmedia.com">TST Media</a> we host our Rails app at <a href="http://www.engineyard.com">Engine Yard</a> on four slices which all utilize the same shared disk via gfs. Anytime there is any hard core disk activity our sites slow down to a crawl. This makes removing files a bit tricky, such as when we want to empty our cache files or when <a href="http://www.capify.org/">Capistrano</a> removes a release at the end of a deploy. The strategy we have come up with to handle this is to move the files to a specific directory we call the &quot;caches_to_remove&quot; directory, and use a cron task to empty this directory at night when most of our users are asleep. Moving files is extremely fast and not disk intensive, as long as the source and destination is on the same disk of course. </p>
<p>The shell script that the cron runs nightly is simple:</p>
<p># remove_cache_dirs.sh<br />
rm -rf /data/tst/caches_to_remove<br />
mkdir /data/tst/caches_to_remove</p>
<p>We have a capistrano task to empty the cache files, which simply moves the cache directory into the caches_to_remove directory.</p>
<p>set :remove_files_dir, '/data/tst/caches_to_remove/'<br />
namespace :cache do<br />
&nbsp; desc &quot;Delete all cache files on disk.&quot;<br />
&nbsp; task :empty, :roles =&gt; :web do<br />
&nbsp;&nbsp;&nbsp; sudo &quot;mv /data/tst/cache #{remove_files_dir} &amp;&amp; mkdir /data/tst/cache&quot;<br />
&nbsp; end&nbsp; <br />
end</p>
<p>We also want a deploy to do a mv instead of a rm -rf on the release to be &quot;removed&quot;. Our application has quite a few files and some 33,000 lines of code not counting all the plugins, gems and Rails itself which are vendored, so doing a rm -rf at the end of deploy considerably slows our app down for several minutes. So we overrode the built-in capistrano deploy:cleanup task to accomplish this.</p>
<p>namespace(:deploy) do<br />
&nbsp; # overriding cleanup task, changing it from a rm -rf to a mv<br />
&nbsp; desc &lt;&lt;-DESC<br />
&nbsp;&nbsp;&nbsp; Clean up old releases. By default, the last 5 releases are kept on each \<br />
&nbsp;&nbsp;&nbsp; server (though you can change this with the keep_releases variable). All \<br />
&nbsp;&nbsp;&nbsp; other deployed revisions are removed from the servers. By default, this \<br />
&nbsp;&nbsp;&nbsp; will use sudo to clean up the old releases, but if sudo is not available \<br />
&nbsp;&nbsp;&nbsp; for your environment, set the :use_sudo variable to false instead.<br />
&nbsp; DESC<br />
&nbsp; task :cleanup, :except =&gt; { :no_release =&gt; true } do<br />
&nbsp;&nbsp;&nbsp; count = fetch(:keep_releases, 5).to_i<br />
&nbsp;&nbsp;&nbsp; if count &gt;= releases.length<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; logger.important &quot;no old releases to clean up&quot;<br />
&nbsp;&nbsp;&nbsp; else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; logger.info &quot;keeping #{count} of #{releases.length} deployed releases&quot;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # COMMENT OUT THIS CODE<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # directories = (releases - releases.last(count)).map { |release|<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #&nbsp;&nbsp; File.join(releases_path, release) }.join(&quot; &quot;)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # invoke_command &quot;rm -rf #{directories}&quot;, :via =&gt; run_method <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # ADD THIS CODE<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; directories = (releases - releases.last(count)).each do |release|<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; directory = File.join(releases_path, release)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; invoke_command &quot;mv #{directory} #{remove_files_dir}&quot;, :via =&gt; run_method<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end<br />
&nbsp;&nbsp;&nbsp; end<br />
&nbsp; end<br />
end</p>
<p>And the world is a happier place!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.lukeludwig.com/index.php/2009/05/14/capistrano-tip-to-avoid-disk-intensive-removal-of-files/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>A Capistrano task for a rolling Mongrel restart and deploy</title>
		<link>http://blog.lukeludwig.com/index.php/2009/05/13/a-capistrano-task-for-a-rolling-mongrel-restart-and-deploy/</link>
		<comments>http://blog.lukeludwig.com/index.php/2009/05/13/a-capistrano-task-for-a-rolling-mongrel-restart-and-deploy/#comments</comments>
		<pubDate>Wed, 13 May 2009 23:31:03 +0000</pubDate>
		<dc:creator>Luke</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Capistrano]]></category>
		<category><![CDATA[Mongrel]]></category>
		<category><![CDATA[Rails]]></category>

		<guid isPermaLink="false">http://blog.lukeludwig.com/?p=37</guid>
		<description><![CDATA[At TST Media we have our rails app hosted at Engine Yard. Currently we use Nginx, haproxy, and Mongrel and have 4 slices each with 4 mongrels. When an HTTP request first comes in to our system it hits the load balancer which chooses a slice to send it to. The nginx on the given [...]]]></description>
			<content:encoded><![CDATA[<p>At <a href="http://www.tstmedia.com">TST Media</a> we have our rails app hosted at <a href="http://engineyard.com">Engine Yard</a>. Currently we use <a href="http://wiki.nginx.org/Main">Nginx</a>, <a href="http://haproxy.1wt.eu/">haproxy</a>, and <a href="http://mongrel.rubyforge.org/">Mongrel</a> and have 4 slices each with 4 mongrels. When an HTTP request first comes in to our system it hits the load balancer which chooses a slice to send it to. The nginx on the given slice picks the request up and sends it onto haproxy. Haproxy chooses a mongrel to send the request to based on availability. When we roll out bug fixes, which we do once every other day or so, the Mongrels all restart at once and all the users browsing our sites experience 20-30 seconds of... basically downtime. The browser spins and waits until the mongrels are ready to go. If requests come in at a certain time the users may see a 502 Bad Gateway response or a 503 Service Unavailable response, both of which started showing up once we started using haproxy. Clearly this is unacceptable. Soon we hope to switch to Nginx with <a href="http://www.modrails.com/">Phusion Passenger</a> which may not have this problem. Until then we have started doing rolling restarts, where one slice is down at a time which allows us to do small deploys without impact to our users.</p>
<p>
To accomplish this rolling restart with our setup we have to stop nginx on the slice that is down. This prevents the load balancer from sending requests to the slice that is down. If we leave nginx up and only stop the mongrels then requests will still be routed to this slice and will hang in a similar manner as if we had restarted all the mongrels at once. We put together this capistrano task:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
namespace :mongrel do<br />
&nbsp; desc &lt;&lt;-DESC<br />
&nbsp; Rolling restart, 1 server at a time.<br />
&nbsp; DESC<br />
&nbsp; task :rolling_restart do<br />
&nbsp;&nbsp;&nbsp; find_servers(:roles =&gt; :app).each do |server|<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ENV['HOSTS'] = &quot;#{server.host}:#{server.port}&quot;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nginx.stop<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; puts &quot;Sleeping 10 seconds to wait for mongrels to finish.&quot;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep 10<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mongrel.restart<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; puts &quot;Sleeping 30 seconds to wait for mongrels to start up.&quot;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep 30<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nginx.start<br />
&nbsp;&nbsp;&nbsp; end<br />
&nbsp; end<br />
end</p>
<p>This task iterates over each server/slice and stops nginx, waits for 10 seconds to let the mongrels finish what they are doing, restarts the mongrels, waits 30 seconds for the mongrels to boot up, and then starts nginx up again. This capistrano task assumes the existence of nginx.stop, nginx.start, and mongrel.restart tasks.</p>
<p>With this mongrel:rolling_restart task in place, we then defined a deploy:rolling task like this:</p>
<p>desc &lt;&lt;-DESC<br />
&nbsp; A deploy without migrations where the mongrels restarted in a rolling manner.<br />
DESC<br />
task :rolling do<br />
&nbsp; update<br />
&nbsp; mongrel.rolling_restart<br />
end</p>
<p>When using this deploy:rolling task our site remains up and responsive during the entire deploy. This approach is useful for small bug fix roll outs where there are no migrations that need to be ran. There is a short window of time in which some of your servers will be out-of-date. For example you may see issues if your bug fix includes changes to a view file and a controller, and say a user hits a mongrel and is served the new view and then makes a post to an out-of-date mongrel with the new controller. However this is usually preferred to forcing all of your users to wait 30 seconds while all the mongrels restart. I would rather impact a very small percentage of our users than 100% of our users.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.lukeludwig.com/index.php/2009/05/13/a-capistrano-task-for-a-rolling-mongrel-restart-and-deploy/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

