Deploying Multiple Instances of an Application With Capistrano
was posted on 04 Jan 07 at 2:50pm. it has been filed under code, ruby/rails and tagged with .
so far it has been commented on 3 times, you can add your own if you like. you can also ping the trackback url if you want, i don't mind.
related posts
archives
categories
Like most people I use capistrano to deploy my rails applications, at work we host with the excellent railsmachine and they have really helped simplify the process of deploying to their servers using the railsmachine gem, which builds on top of the excellent functionality of capistrano.
The way these tools work by default they only allow you to deploy one instance of your application, which is fine, that’s all they’re are intended to do. But in the client facing world you’re probably going to want to have at least two versions of the same application at different stages of development running on the same server.
A live forward facing version (my-app.com) and a staging version (staging.my-app.com) for client approval / production testing (not code testing, that should stay on your development box) / progressive reviews etc.
So how do we achieve that? The idea is to determine what context you are deploying your application in, and use the fact the capistrano tasks can be chained together to set everything up ready for deploying a revision of your application to a targeted url, independent of other deployments.
Step 1
The first thing to do is to require the railsmachine recipes and setup some variables, as you will see later some of these variables are redundant, and the code has a fair bit of duplication, but this is a work in progress and I hope to clean it up when I have some more spare time.
set :application, "my-app"
set :deploy_to, "/var/www/apps/"
set :main_domain, "my-app.com"
set :user, "deploy"
set :repository, "svn+ssh://@my-svn-server.com/var/svn//trunk"
set :rails_env, "production"
role :scm, "my-svn-server.com"
You’ll notice that I’m keeping my my project on separate svn server, by default the railsmachine gem will create an svn repo for you as part of the project tree, this is really useful at first, but when you’re deploying multiple instances of the same application you’re going to want your svn repo independent.
Step 2
Now comes the clever bit, creating the tasks which will be called before our regular cap commands, like so $ cap -a staging deploy (which would deploy the current revision of your application to the staging server)
desc "Set staging instance variables"
task :staging do
set :application, "my-app-staging"
set :domain, "staging.my-app.com"
set :apache_proxy_port, 8010
set :database_yml, "database-staging.yml"
set :deploy_to, "/var/www/apps/"
role :web, domain
role :app, domain
role :db, domain, :primary => true
end
desc "Set production instance variables"
task :production do
set :application, "my-app-production"
set :domain, "my-app.com"
set :apache_proxy_port, 8000
set :database_yml, "database-production.yml"
set :deploy_to, "/var/www/apps/"
role :web, domain
role :app, domain
role :db, domain, :primary => true
end
What this does is set all of the variables which any command you’ll call after staging or production will need, it’s pretty ugly at the moment and I hope to clean it up and make it a lot smarter, but for the moment it works, and in it’s ugliness it does make it clear what it’s doing.
Step 3
Next we overwrite the railsmachine setup_scm task and one of the tasks it calls, we’re doing this because we are using a custom location for our repository and not the one which the railsmachine gem tries to create for us.
In my particular situation I have an external server used for svn, but the repository could just as easily be on the same server as your application just so long as the folder you create the repo in is in the deploy group. Also in my situation I am creating a new repo for each project I deploy, if you already have a repo that you deploy all projects to you could easily just remove the setup_svn_repo task call from the setup_scm task.
Enough talk, on with the code:
desc "Setup svn repository"
task :setup_svn_repo, :roles => :scm do
dir = "/var/svn/"
run "mkdir -p "
run "chmod 770 "
run "svnadmin create "
end
desc "Setup source control server."
task :setup_scm, :roles => :scm do
setup_svn_repo
import_svn
end
Step 4
Because different instances of our application are going to need different databases we have to overwrite the method which read’s in the database configuration when setting up the database for the first time. The way it works at the moment we also need to remove the database.yml file from svn control, and recreate it from the correct file after every code update.
task :clean_db_yml do
puts "removing database.yml from svn"
system "svn remove config/database.yml"
puts "ignoring database.yml"
system "svn propset svn:ignore 'database.yml' config/"
system "svn update config/"
puts "committing changes"
system "svn commit -m 'Removed and ignored database.yml file'"
end
db_config = YAML.load_file("config/")
set :db_user, db_config[rails_env]["username"]
set :db_password, db_config[rails_env]["password"]
set :db_name, db_config[rails_env]["database"]
end
task :after_update_code, :roles => :app do
correct_db_yml
end
task :correct_db_yml do
put(File.read("config/"), "/config/database.yml", :mode => 0444)
end
Thats all there is to it, I run the following commands to set up an instance of my app:
cap -a staging setup cap -a staging setup_scm cap -a staging setup_servers ../[new directory] cap -a staging clean_db_yml cap -a staging cold_deploy cap -a staging restart_web cap -a staging deploy cap -a staging deploy_with_migrations
The End
There you have it, it works, it’s not the most elegant solution and definitely something I want to work further on. The last section with database.yml stuff is by far the ugliest, but it is a work in progress, and I plan to make further amendments to it.
3 comments
add your own
I’ve done something similar. Jamis Buck pointed me to this method, and I’ve been using it ever since: http://groups.google.com/group/capistrano/msg/ce9b54912a705aa7
i think i’m going to investigate Jamis’s method. i think there may be a problem with my way because when i checked my svn server it would appear as though the application is being checked out to the /var/www/apps directory there also. it’s something i really need to get nailed.
Hello webmaster
I would like to share with you a link to your site
write me here preonrelt@mail.ru