A Quality Experience during deployments
As much as I think Continuous Deployment (to production) is a Bad Thing&trade, the big benefit I see from that idea is that it treats deployment as a first class citizen. Too often getting software into the hands / browsers of customers is not the nicest experience for them, filled with standard web server ‘something is a miss’ pages, really slow page loads while things get reloaded into memory, etc.. This is especially true in the Rails world where a common deployment methodology is to proxy requests using apache to a pack of mongrels (web servers). We’re starting to play in the ‘enterprise’ space so that wasn’t going to cut it.
Enter Capistrano and mod_rewrite to save(ish) the day.
The first thing you need to make this work is two cap tasks which will start and stop your maintenance windows.
namespace :maintenance do
desc "start maintenance"
task :start do
run "ln -s #{deploy_to}/shared/maintenance/maintenance.html /tmp/#{application}-maintenance.html"
end
desc "stop maintenance"
task :stop do
run "rm /tmp/#{application}-maintenance.html"
end
end
Fairly self explanatory, but for the cap impaired what it is doing is creating a symlink in /tmp to a file in the deployment directory.
Notice that this is not in ‘current’ or ‘releases’. We want this outside of the deployment so we can control its content very tightly and always know which file is being served. You will have to put this there yourself.
Of course, just creating a file is not enough. We need to tell apache to do something special if it is there. This mod_rewrite rule will do that. I put it at the beginning of all my rules.
RewriteCond %{REQUEST_URI} !\.(css)$
RewriteCond %{REQUEST_URI} !\.(png)$
RewriteCond %{REQUEST_URI} !\.(jpg)$
RewriteCond %{REQUEST_URI} !\.(gif)$
RewriteCond /tmp/monkey-maintenance.html -f
RewriteRule ^.*$ /tmp/monkey-maintenance.html [L]
What this does is return the file we created with cap above if it exists. Because it is the first rule, then users will always get that page. And because we are not applying it to css or images requests our static page can have them in it. Without them the page would just be all text.
Customers will be still annoyed if they are doing something while you kick into maintenance mode, but at least there will be some sort of explanation saying what is going on so hopefully you don’t have to do this too many times in one day. In order to try and stave off that you can test the app before opening it up again.
Except you can’t if you show a maintenance page for every request. But this is easy to work around with 2 lines of apache config and a dns entry.
The first change you need to make to apache is create a ‘secret’ alias for your webserver using the ServerAlias config directive.
ServerName monkey.goucher.ca
ServerAlias s3kr3t.monkey.goucher.ca
You will need to add that new host into your DNS records to point to the same server and the non-s3kr3t one.
Next we add an additional rule to the ones created around the maintenance page.
RewriteCond %{HTTP_HOST} !^s3kr3t.monkey.goucher.ca$
Now the rule will be not be applied when going to the s3kr3t page so you can test it before taking down the window. For the hyper paranoid you could also restrict it to only the office’s gateway IP as well.
Ideally, you would have cascading deployments which result in no downtime for any customers, but is non-trivial and can result in greater hardware requirements than you might have at your disposal. For small to medium applications, I think this would work just fine.