Deploying to multiple server environments with cap
-Thursday, April 30, 2009 By: Jon Kinney
Most anytime I develop a web application I need to deploy to multiple server environments. For me this used to mean maintaining two separate deploy.rb scripts, and I would rename one while deploying to staging, and then rename the other when I needed to deploy to production. After about three deploys I said, the hell with this! And I figured out how to allow the specification of your deployment at the command line during the cap deploy task.
Now I know what you're thinking, why not create your own cap task that is called cap deploy_production and cap deploy_staging (or whatever), but I didn't want to muck around with that. Here is how I get Capistrano to prompt ME!
1 default_run_options[:pty] = true
2 ssh_options[:forward_agent] = true
3 # also had to set up id_ras keys for the deploy user on the production box (to itself). And setup the tunnel definition as well in ~/.subversion/config
4 # Local => rs_ssh = /opt/local/bin/ssh -p 2384 -l jkinney
5 # Production => rs_ssh = /usr/bin/ssh -p 2384 -l jkinney
6
7 set :gems_for_project, %w(highline,will_paginate,etc...) # list of gems to be installed
8
9 # Make terminal prompt us for the location we want to deploy to
10 set :deploy_location, Proc.new { deploy_location = Capistrano::CLI.ui.ask "Enter deploy location (stage/prod)" }
11
12 if "#{deploy_location}" == "prod"
13 set :domain, "209.41.75.42"
14 else
15 # I have ssh setup on a non-standard port for my staging box, since it's exposed to the world. Our production box required VPN or local netowrk access.
16 ssh_options[:port] = 2384
17 set :domain, "YOUR STAGING IP HERE"
18 # note that your staging could be on the same server, just setup your application names differently then so you aren't overwriting things.
19 end
20
21 # prompts for a release tag, if that is your sort of thing...
22 set :release_tag, Proc.new { release_tag = Capistrano::CLI.ui.ask "Enter a release tag to deploy (type trunk or leave blank and hit enter to deploy from trunk)" }
23
24 role :app, domain
25 role :web, domain
26 role :db, domain, :primary => true
27
28 set :application, "jonkinneydotcom"
29
30 set :user, "deploy"
31 set :password, "secret"
32 set :deploy_to, "/var/www/apps/#{application}"
33
34 set :rails_env, "production"
35
36 # I thought this was automatic, but I seem to need to require it to cleanup my releases
37 set :keep_releases, 4
38 after "deploy", "deploy:cleanup"
39
40 # this is using subversion, I am transitioning to GIT and will post updates when I have a modified deploy.rb
41 set :repo_location, "/var/svn/your_subversion_repo"
42 set :repository, "svn+ssh://#{domain}#{repo_location}/#{application}/trunk"
43
44 namespace :deploy do
45 desc "restart passenger"
46 task :restart, :roles => :app, :except => {:no_release => true} do
47 run "touch #{current_path}/tmp/restart.txt"
48 end
49
50 [:start, :stop].each do |t|
51 desc "#{t} task is a no-op with passenger"
52 task t, :roles => :app do; end
53 end
54
55 task :after_symlink do
56 run "chmod -R a+rw #{release_path}/public"
57
58 #rcov messes with deployed apps...remove it in production
59 run "rm -rf #{release_path}/vendor/plugins/rails_rcov"
60
61 # OPTIONAL: symlink files from the FTP site's home directory to the rails_root
62 # keep them protected and use send_file to present them to the logged in user
63 # (this is more secure than hiding them in a public directory with directory listing off)
64 # run "rm -rf #{release_path}/admin_files"
65 # run "ln -s #{deploy_to}/#{shared_dir}/admin_files #{release_path}"
66
67 #symlink the files from outside the deploy path so we can keep all the uploaded images!
68 # This will allow images uploaded through an asset manager to be retained between deployments
69 run "rm -rf #{release_path}/public/assets"
70 run "ln -s #{deploy_to}/#{shared_dir}/assets #{release_path}/public/assets"
71
72 # setup database for production environment (database.yml should be ignored in svn)
73 db_params = {
74 "adapter"=>"mysql",
75 "database"=>"yourapp_production",
76 "username"=>"root",
77 "password"=>"secret",
78 "host"=>"localhost",
79 }
80
81 # OPTIONAL: Deploy to production with a sqlite3 database...because we don't need anything elaborage.
82 # And that way we can reset the db every hour or whatever with a cron job
83 # db_params = {
84 # "adapter"=>"sqlite3",
85 # "database"=>"db/production.sqlite3.db",
86 # "timeout"=>"5000"
87 # }
88
89 db_params.each do |param, default_val|
90 set "db_#{param}".to_sym,
91 #if you want to be prompted uncomment the line below this and comment out the one directly below that
92 # lambda { Capistrano::CLI.ui.ask "Enter database #{param}" do |q| q.default=default_val end}
93 param = default_val
94 end
95
96 # builds the database.yml
97 database_configuration = "production:\n"
98 db_params.each do |param, default_val|
99 val=self.send("db_#{param}")
100 database_configuration<<" #{param}: #{val}\n"
101 end
102
103 run "mkdir -p #{deploy_to}/#{shared_dir}/config"
104
105 put database_configuration, "#{deploy_to}/#{shared_dir}/config/database.yml"
106
107 #symlink the database.yml
108 run "ln -s #{deploy_to}/#{shared_dir}/config/database.yml #{deploy_to}/current/config"
109
110 #symlink the production database
111 run "ln -s #{deploy_to}/#{shared_dir}/config/production.sqlite3.db #{deploy_to}/current/db"
112 end
113 end
114
115 # This is what asks you if you're sure you want to deploy to production?!?!?
116 before "deploy:update_code", "user_confirmation_for_production_deployment"
117 task :user_confirmation_for_production_deployment, roles => :app do
118 if "#{deploy_location}" == 'prod'
119 message = "You are deploying to PRODUCTION. continue(y/n):"
120 answer = Capistrano::CLI.ui.ask(message)
121 abort "deployment to production was stopped" unless answer == 'y'
122 end
123 end
124
125
126
127 ######################Custom cap tasks that I find useful
128 desc "Configure VHost"
129 task :config_vhost do
130 vhost_config =<<-EOF
131 <VirtualHost *:80>
132 ServerName jonkinney.com
133 ServerAlias www.jonkinney.com
134 DocumentRoot #{deploy_to}/current/public
135 </VirtualHost>
136 EOF
137 put vhost_config, "#{deploy_to}/#{shared_dir}/config/vhost_config"
138 sudo "mv #{deploy_to}/#{shared_dir}/config/vhost_config /etc/apache2/sites-available/#{application}"
139 sudo "a2ensite #{application}"
140 sudo "/etc/init.d/apache2 reload"
141 end
142
143 desc "create assets directory"
144 task :create_assets_directory do
145 sudo "mkdir -p #{deploy_to}/#{shared_dir}/assets"
146 sudo "chmod -R 777 #{deploy_to}/#{shared_dir}/assets"
147 sudo "chown -R deploy:www-data #{deploy_to}/#{shared_dir}/assets"
148 end
149
150 desc "make current development database the production database"
151 task :upload_dev_db_to_prod do
152 put(File.read("db/development.sqlite3.db"), "#{deploy_to}/#{shared_dir}/config/production.sqlite3.db")
153 end
154
155 desc "run remote command"
156 task :show, :roles => :app do
157 run <<-COMMAND
158 /var/www/apps/#{application}/current/script/runner -e production 'require "pp"; pp #{command}'
159 COMMAND
160 end
161
162 desc "run remote rake db:migrate RAILS_ENV=production"
163 task :remote_db_migrate do
164 run("cd #{deploy_to}/current; /usr/bin/rake db:migrate RAILS_ENV=production")
165 end
Hopefully this has been helpful. I know Capistrano can be somewhat of a black box for people, so if you have issues with my deploy.rb or need any help feel free to contact me or post in the comments!
News & Events
Tech Review: Web Design For Developers - June 2009
A friend of mine in the web development community is releasing a book called "Web Design for Developers: A Programmer's Guide to Design Tools and Techniques". I was asked to do a tech review of the book and will be sharing my thoughts as well as some cool info presented in the book in a short series of upcoming blog posts. If you want to grab a beta copy of the book head over to my favorite publisher The Pragmatic Programmers.
First Class Audio Production - March 1st 2009
My most recent studio project was mixing and producing the latest a cappella album to come out of Eau Claire, WI. Until Proven Guilty is the Innocent Men's 4th studio album and marks a huge leap forward in recording and production quality for the group. Check back for demos of the mastered songs very soon! http://theinnocentmen.com.
Rate This Post: 4.0 (average)


