Using Capistrano to Overcome Deployment Hurdles on Dreamhost
While I have been happy with Dreamhost's services, let's face it, deploying a Rails app to a shared Dreamhost server can be a rather daunting task. Even after I succeeded for the first time, I could not duplicate that success on a dependable basis. Then I found Capistrano, many thanks to Jamis Buck."Capistrano is a utility that can execute commands in parallel on multiple servers." It's original purpose was deploying web apps, but, it can do much more. However, within the scope of this article, we will explore its uses for deploying web apps to Dreamhost. Many of the techniques used in the article are just my preference for using Capistrano and can be set up differently for your own use.
For this example we will assume an application name of 'blog' and a domain name of 'example.com'. We will set up the app as a sub-domain of example.com.
The first order of business is to set up Capistrano on your machine. Look here for instructions to do that.
Next we must 'Capistranize' your project on your development (local) machine. This step will add a deploy.rb (the deployment recipe) file to your app's config/ directory. It also adds capistrano.rake to your lib/tasks/ directory. Simply navigate to your project's root at a command line a type the following command. Don't overlook the period in the command, it is referring to the path to your app.
cap --apply-to . blog |
Now we will set up the new domain on Dreamhost. Go to the manage domains section of the Dreamhost panel and make your settings match this. The key thing that we have done here that we would not do for a non-Capistrano deployment is point the web app to the blog.example.com/current/public/ directory. This current/ directory is actually a symlink that points to a real directory whose name is derived from the time the app was deployed. This real directory will be under the release/ directory, which is at the same level as the current symlink in the directory structure. This will allow us to keep several deployments in the directory and point to the current deployment using the current symlink.
Once you hae saved the new domain at the Dreamhost control panel, you will need to ssh into your Dreamhost account and delete the real current directory that was created for you by the panel. If you do not do this, Capistrano will not be able to create a symlink due to a naming conflict. To delete the real directory, navigate to your apps root directory and issue a recursive remove command, as there is also a public directory underneath the current directory.
rm -r current |
Now we are going to set up our deployment recipe. Open the deploy.rb file. You will notice that the syntax is just pure Ruby syntax. The first set of variables we will configure is required for every deployment recipe.
1 2 3 4 5 |
set :user, "bloguser" set :application, "blog.example.com" set :repository, "http://svn.example.org/mephisto/trunk" set :svn_username, "bloguser" set(:svn_password) |
We will set the user to 'bloguser' because we specified this user when we set up the apps domain in the Dreamhost panel. You can use whatever user you want to as long as it has access to read, write and delete in and below the blog apps root directory. We also configured the name of the app, the subversion repository URL and user name, and told Capistrano to prompt for the subversion password. It is important to note that Capistrano is going to deploy from the repository, not our development machine, so you must have all code you want deployed committed prior to actually trying to deploy.
The next section of variables we will configure is the roles. You can set up any number of roles that your configuration will require. According to the Capistrano manual, "You can define whatever roles you want, but the default Capistrano tasks look for those three: :app, :web, and :db. The :app role describes which servers are acting as the application servers (the servers running the FastCGI instances). The :web role describes the servers running Apache, and the :db role describes the servers running your database(s)."
1 2 3 |
role :web, "#{application}" role :app, "#{application}" role :db, "#{application}", :primary => true |
On Dremhost shared hosting, all of our roles are on the same machine and that machine has the same name as the app, so I reuse the application variable.
The next section of variables are optional, as each of them has a default value if not specified here.
1 2 3 4 5 |
set :user_root, "/home/#{user}" set :deploy_to, "#{user_root}/#{application}" set :use_sudo, false set :checkout, "export" set :restart_via, :run |
The user root variable is the path to where the workspace for your applications user is. For us, it will be 'home/bloguser/'. So we reuse the user variable to keep our configuration DRY. The deploy to variable is the directory where we want to deploy our application. due to our choices in creating the domain at Dreamhost, the folder name the app will live in is that same as the application variable, so we will reuse it. The user sudo variable let's Capistano know if it should use the sudo utility to perform admin functions, which we cannot do at Dreamhost. the Checkout variable lets Capistrano know whether to check out or export from the repository. Of course, an export will save disk space and bandwidth.
Next comes the user variables section where you can define variable specific for tasks that you add to the deployment.
1 2 3 4 5 6 |
set :db_adapter, "mysql" set :db_host, "mysql.#{application}" set :db_name, "blog" set :db_dev_postfix, "" set :db_test_postfix, "_test" set :db_prod_postfix, "" |
We are going to set up variables that will aid us in dynamically creating a database.yml file at deployment time, an idea I got from Jeremy Voorhis. Using a standard naming convention for your web apps and resources at Dreamhost will help make your life easier. I always name the MySQL host mysql.[application name]. This is reflected in the db host variable. The db name variable is the root name of your database, while the postfix variables are the postfix for each environment's database. Because my production and development database are always on separate machines, I just name them without a post fix. Only my test database has a postfix.
Next we define custom operations using the after_update_code task that Capistrano calls by default.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
desc "Tasks to execute after code update" task :after_update_code, :roles => [:app, :db, :web] do # update the... run "cp #{user_root}/_resources/.htaccess #{release_path}/public/.htaccess" run "cp #{user_root}/_resources/dispatch.fcgi #{release_path}/public/dispatch.fcgi" #put the... run "cp -r #{user_root}/_resources/tmp #{release_path}/tmp" # delete the.. #run "rm #{release_path}/.project" # fix permissions... run "chmod +x #{release_path}/script/process/reaper" run "chmod +x #{release_path}/script/process/spawner" run "chmod 755 #{release_path}/public/dispatch.*" database_yml_change end |
Here, I am copying the .htaccess and dispatch.fcgi files that I have stored in the _resources/ directory that is located in my user root at Dreamhost. I have to do this because I develop on a windows machine. We also copy the tmp/ directory from this same location. I do not like to source control it so that I do not have to keep ignoring files, etc. I also delete the .project file that Eclipse IDE creates. I like to source control this so that I back up how I have my project set up. You can do this same thing with any IDE files you may have or just comment it out. Next we fix the permissions on several files. Last we call the database_yml_change task, which is a custom task we created and will review later in this article. Next we touch the dispatch.fcgi files after a deploy and after a rollback. This helps the app to restart on Dreamhost. I have sometimes run into trouble where it takes an app a little while to restart and this seems to help minimize this. I have yet to figure out how to get the reaper or spawner scripts to work correctly, so this is the next best thing.
1 2 3 4 5 6 7 8 9 |
desc "Restarting after deployment" task :after_deploy, :roles => [:app, :db, :web] do run "touch /home/#{user}/#{application}/current/public/dispatch.fcgi" end desc "Restarting after rollback" task :after_rollback, :roles => [:app, :db, :web] do run "touch /home/#{user}/#{application}/current/public/dispatch.fcgi" end |
In the last part of the deployment recipe we create the database.yml file dynamically from out settings and put it into the shared/config/ directory, as all versions of the app that we roll out can share this file. We then create a database.yml symlink in the current/config/ folder that points to the shared/config/database.yml file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
desc "Create database.yml in shared/config" task :database_yml_change do # remove the dev version of database.yml run "rm #{release_path}/config/database.yml" database_configuration = render :template => <<-EOF login: &login adapter: <%= db_adapter %> host: <%= db_host %> username: <%= user %> password: <%= password %> development: database: <%= "#{db_name}#{db_dev_postfix}" %> <<: *login test: database: <%= "#{db_name}#{db_test_postfix}" %> <<: *login production: database: <%= "#{db_name}#{db_prod_postfix}" %> <<: *login EOF run "mkdir -p #{deploy_to}/#{shared_dir}/config" put database_configuration, "#{deploy_to}/#{shared_dir}/config/database.yml" # link to the database.yml run "ln -nfs #{deploy_to}/#{shared_dir}/config/database.yml #{release_path}/config/database.yml" end |
The next thig we need do is set up the directory strucure on the Dreamhost side. Luckily, Capistrano provides a rake task to do this that will now work as we have configured everything it needs. Simply navigate to the root of your app on your development machine an enter this command.
1 2 |
rake remote:exec ACTION=setup #deprecated cap setup #this is the newer way |
This will create the releases/ and shared/ directories under the blog.example.com/ directory for our example.
The last thing you must do to start your first deployment is type this command on your development machine (don't forget to commit everything you expect to deploy).
cap deploy |
If everything was set up correctly, your app will be deployed to Dreamhost and you should be able to browse to it. The app may delay due to starting up. In addition, you have created a repeatable process that is dependable to roll out all of your changes and even roll back if you create an issue with a deployment.
Here is the full deploy.rb file from this article.
May 24th, 2007 at 01:05 PM Great post. I just had to struggle with Capistrano yesterday for the first time. Learning curve on it a bit harsh, but once you get it going, "cap deploy" is outstanding.
June 13th, 2007 at 10:11 AM Great writeup! Thanks for this. Note, though, that using rake to access Capistrano tasks is deprecated, and is not recommended. Rather than 'rake deploy', just use 'cap deploy'. And "cap setup" is much nicer than "rake remote:exec ACTION=setup". :)
July 11th, 2007 at 02:01 AM Hello Very interesting information! Thanks! G'night
July 13th, 2007 at 06:13 AM Hi Your site is very cognitive. I think you will have good future.:) http://wager.retproject.info/ http://21-020.retproject.info/ http://casino-006.retproject.info/ http://poker-006.retproject.info/ http://sports-book.retproject.info/ G'night
July 13th, 2007 at 09:39 AM Hi Looks good! Very useful, good stuff. Good resources here. Thanks much! G'night
July 21st, 2007 at 08:33 AM I've found that "script/process/reaper --dispatcher=dispatch.fcgi" (code from memory) gets the reaper going on dreamhost for me.